package Data::AMF::Formatter::AMF0;
use Any::Moose;

require bytes;
use Scalar::Util qw/looks_like_number blessed/;
use Data::AMF::IO;

has 'io' => (
    is      => 'rw',
    isa     => 'Data::AMF::IO',
    lazy    => 1,
    default => sub {
        Data::AMF::IO->new( data => q[] );
    },
);

sub format {
    my ($self, @obj) = @_;
    $self = $self->new unless blessed $self;

    for my $obj (@obj) {
        if (my $pkg = blessed $obj) {
            $self->format_typed_object($obj);
        }
        elsif (my $ref = ref($obj)) {
            if ($ref eq 'ARRAY') {
                $self->format_strict_array($obj);
            }
            elsif ($ref eq 'HASH') {
                $self->format_object($obj);
            }
            else {
                Carp::confess qq[cannot format "$ref" object];
            }
        }
        else {
            if (looks_like_number($obj) && $obj !~ /^0\d/) {
                $self->format_number($obj);
            }
            elsif (defined($obj)) {
                $self->format_string($obj);
            }
            else {
                $self->format_null($obj);
            }
        }
    }

    $self->io->data;
}

sub format_number {
    my ($self, $obj) = @_;
    $self->io->write_u8(0x00);
    $self->io->write_double($obj);
}

sub format_string {
    my ($self, $obj) = @_;
    $self->io->write_u8(0x02);
    $self->io->write_utf8($obj);
}

sub format_strict_array {
    my ($self, $obj) = @_;
    my @array = @{ $obj };

    $self->io->write_u8(0x0a);

    $self->io->write_u32( scalar @array );
    for my $v (@array) {
        $self->format($v);
    }
}

sub format_object {
    my ($self, $obj) = @_;

    $self->io->write_u8(0x03);

    for my $key (keys %$obj) {
        my $len = bytes::length($key);
        $self->io->write_u16($len);
        $self->io->write($key);
        $self->format($obj->{$key});
    }

    $self->io->write_u16(0x00);
    $self->io->write_u8(0x09);      # object-end marker
}

sub format_null {
    my ($self, $obj) = @_;

    $self->io->write_u8(0x05);  # null marker
}

sub format_typed_object {
    my ($self, $obj) = @_;

    $self->io->write_u8(0x10);

    my $class = blessed $obj;
    $self->io->write_utf8($class);

    $self->format_object($obj);
}

no Any::Moose;

__PACKAGE__->meta->make_immutable;

__END__

=head1 NAME

Data::AMF::Formatter::AMF0 - AMF0 serializer

=head1 SYNOPSIS

    my $amf0_data = Data::AMF::Formatter::AMF0->format($obj);

=head1 METHODS

=head2 format

=head2 format_number

=head2 format_string

=head2 format_strict_array

=head2 format_object

=head2 format_null

=head2 format_typed_object

=head1 AUTHOR

Daisuke Murase <typester@cpan.org>

=head1 COPYRIGHT

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
LICENSE file included with this module.

=cut
