package FLV::Body;

use warnings;
use strict;
use Carp;
use English qw(-no_match_vars);

use base 'FLV::Base';

use FLV::Tag;
use FLV::VideoTag;
use FLV::AudioTag;
use FLV::MetaTag;

our $VERSION = '0.03';

=head1 NAME

FLV::Body - Flash video file data structure

=head1 LICENSE

Copyright 2006 Clotho Advanced Media, Inc., <cpan@clotho.com>

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

=head1 METHODS

This is a subclass of FLV::Base.

=over

=item $self->parse($fileinst)

Takes a FLV::File instance and extracts the FLV body from the file
stream.  This method throws exceptions if the stream is not a valid
FLV v1.0 or v1.1 file.

There is no return value.

=cut

sub parse
{
   my $self = shift;
   my $file = shift;

   my @tags;

 TAGS:
   while (1)
   {
      my $lastsize = $file->get_bytes(4);

      if ($file->at_end())
      {
         last TAGS;
      }

      my $tag = FLV::Tag->new();
      $tag->parse($file); # might throw exception
      push @tags, $tag->get_payload();
   }

   $self->{tags} = [sort {$a->{start} <=> $b->{start}} @tags];
   return;
}

=item $self->serialize($filehandle)

Serializes the in-memory FLV body.  If that representation is not
complete, this throws an exception via croak().  Returns a boolean
indicating whether writing to the file handle was successful.

=cut

sub serialize
{
   my $self = shift;
   my $filehandle = shift || croak 'Please specify a filehandle';

   return if (! print {$filehandle} pack 'V', 0);

   for my $tag (@{$self->{tags}})
   {
      my $size = FLV::Tag->serialize($tag, $filehandle);
      if (!$size)
      {
         return;
      }
      print {$filehandle} pack 'V', $size;
   }
   return 1;
}

=item $self->get_info()

Returns a hash of FLV metadata.  See File::Info for more details.

=cut

sub get_info
{
   my $self = shift;

   my %info = (
      duration    => $self->last_start_time(),
      FLV::VideoTag->get_info(grep {$_->isa('FLV::VideoTag')} @{$self->{tags}}),
      FLV::AudioTag->get_info(grep {$_->isa('FLV::AudioTag')} @{$self->{tags}}),
      FLV::MetaTag->get_info(grep {$_->isa('FLV::MetaTag')} @{$self->{tags}}),
   );


   return %info;
}

=item $self->get_tags()

Returns an array of tag instances.

=cut

sub get_tags
{
   my $self = shift;

   return @{$self->{tags}};
}

=item $self->count_video_frames()

Returns the number of video tags in the FLV stream.

=cut

sub count_video_frames
{
   my $self = shift;

   return scalar grep {$_->isa('FLV::VideoTag')} @{$self->{tags}};
}

=item $self->count_audio_packets()

Returns the number of audio tags in the FLV stream.

=cut

sub count_audio_packets
{
   my $self = shift;

   return scalar grep {$_->isa('FLV::AudioTag')} @{$self->{tags}};
}

=item $self->count_meta_tags()

Returns the number of meta tags in the FLV stream.

=cut

sub count_meta_tags
{
   my $self = shift;

   return scalar grep {$_->isa('FLV::MetaTag')} @{$self->{tags}};
}

=item $self->last_start_time()

Returns the start timestamp of the last tag, in milliseconds.

=cut

sub last_start_time
{
   my $self = shift;

   my $tag = $self->{tags}->[-1]
       or die 'No tags found';
   return $tag->{start};
}

# =item $self->end_time()
#
# Doesn't work yet.
#
# This is a prototypic implementation that tries to extrapolate the end time of the FLV from metadata about the last tags.
#
# =cut
#
# sub end_time
# {
#    my $self = shift;
#
#    my $tag = $self->{tags}->[-1]
#        or die 'No tags found';
#
#    my $duration;
#    if ($tag->isa('FLV::VideoTag'))
#    {
#       my $f = $self->video_frames();
#       $duration = $tag->{start}/($f-1);
#    }
#    else
#    {
#       # Doesn't work yet
#       $duration = $tag->get_duration();
#    }
#    return $tag->{start} + $duration;
# }

=item $self->get_meta($key);

=item $self->set_meta($key, $value);

These are convenience functions for interacting with an C<onMetadata>
tag at time 0, which is a common convention in FLV files.  If the 0th
tag is not an L<FLV::MetaTag> instance, one is created and prepended
to the tag list.

See also C<get_value> and C<set_value> in L<FLV::MetaTag>.

=cut

sub get_meta
{
   my $self = shift;
   my $key = shift;

   return if (!$self->{tags});
   my $meta = $self->{tags}->[0];
   return if (!eval {$meta->isa('FLV::MetaTag')});
   return $meta->get_value($key);
}

sub set_meta
{
   my $self = shift;
   my $key = shift;
   my $value = shift;

   my $meta;
   $self->{tags} ||= [];
   $meta = $self->{tags}->[0];
   if (!eval {$meta->isa('FLV::MetaTag')})
   {
      $meta = FLV::MetaTag->new();
      $meta->{start} = 0;
      unshift @{$self->{tags}}, $meta;
   }
   $meta->set_value($key => $value);
   return;
}

1;

__END__

=back

=head1 AUTHOR

Clotho Advanced Media Inc., I<cpan@clotho.com>

Primary developer: Chris Dolan

=cut