package SWF::Parser;

use strict;
use Carp;
use SWF;
use vars qw(@ISA $VERSION);
@ISA = qw(SWF);
$VERSION = .1;

my $debug = 0;

sub new {

    #  Usage:  my $swf = SWF::Parser->new($infile);
    #          my $swf = SWF::Parser->new(\*FILEHANDLE);
    #          my $swf = SWF::Parser->new($swfstring);

    my $class = shift;
    $class = ref($class) || $class;

    my $input = shift || return;
    my $flag = shift || "";

    my $self = $class->SUPER::new();

    $debug = 1 if ($flag eq 'debug');

    print " SWF::Parser - Debug mode on.\n" if $debug;

    #  Initialize the SWF object content

    $self->{'content'} = SWF::Readable->new->init;

    my $content;
    if (ref $input eq "GLOB") {
	# passed a filehandle for input - read whole file in

	print " Reading from filehandle:\n\n" if $debug;

	local $/ = undef;
	$content = <$input>;
    }
    elsif (-f $input) {
	# we think they passed us a file - try to open and read it

	print " Reading from file \"$input\":\n\n" if $debug;

	local *INFILE;
	open (INFILE, $input) 
	    || die "SWF::Parser - can't open $input: $!";
	{
	    local $/ = undef;
	    $content = <INFILE>;
	}
    }
    else {
	# we're assuming the string passed is the contents of a SWF

	print " Reading from string:\n\n" if $debug;

	$content = $input;
    }

    $self->{'content'}->content($content);

    #  Initialize handler lookup

    $self->{'_handlers'} = {};

    for my $tag (SWF::validTags) {
	$self->{'_handlers'}->{$tag} = eval("\\\&p$tag");
	
    }

    return $self;

}

sub parse {

    #  Usage:  $swf->parse;
    #    This is the bit that takes the actual SWF and digests it into
    #    a nice, easy-to-use data structure.

    my $self = shift;
    
    #  go to the beginning of content and clear stuff out

    $self->{'content'}->seek;
    $self->_tagList([]);

    #  process the header

    &{$self->{'_handlers'}->{'Header'}}($self);

    #  start reading in the tags
    
    my $frameNum = 0;        #  current frame number
    my $movieP = 1;          #  true while processing movie

    while ($movieP) {
	
	my $frameP = 1;      #  true while we process the frame
	
	print "\n Reading frame $frameNum:\n" if $debug;
	
	while ($frameP) {
	    
	    #  read in the tag and get some useful info
	    
	    my $tag = SWF::Tag->new;
	    $tag->read($self->content);
	    $tag->frame($frameNum);
	    
	    my $tagnum = $tag->tagID;
	    my $taglen = $tag->length;
	    my $taglabel = SWF::tagName($tagnum);
	    
	    print "   $tagnum: \t$taglabel\n" if $debug;
	    print "     $taglen bytes\n" if ($debug && $taglen);
	    
	    #  process the tag - first, do mandatory stuff for control tags
	    
	    if ($tagnum == 1) {        # ShowFrame - go to next frame
		$frameP = 0;
	    }
	    elsif ($tagnum == 0) {     # End - last tag - end of movie
		$frameP = 0;
		$movieP = 0;
	    }
	    elsif ($tagnum == 43) {    # FrameLabel - add a label to frame
		my $label = $tag->{'SWF::Tag::content'}->readString;
		$tag->{'Name'} = $label;
		$self->addLabel($label, $frameNum);
	    }
	    elsif ($tagnum == 24) {    # Protect
		$self->protect(1);
	    }

	    #  now, process according to handlers

	    $tag = &{$self->{'_handlers'}->{$taglabel}}($tag);

	    #  after processing, add tag to {'_tagList'}

	    $self->appendTag($tag) || 
		die "Bad tag in frame $frameNum: $tagnum - $taglabel";
	    
	}

	$frameNum++;
    }

    print "\nDone parsing.\n\n" if $debug;

}

sub listTags {

    #  for debugging only.

    my $self = shift;

    my $taglist = $self->_tagList;
    for my $tag (@$taglist) {
	my $tagnum = $tag->tagID;
	my $taglen = $tag->length;
	my $taglabel = SWF::tagName($tagnum);
	my $tagframe = $tag->frame;

	print "Frame $tagframe:\t$taglabel - $taglen bytes\n";

    }
}

sub setHandlers {

    #  Usage:  $swf->setHandlers("tag1" => \&tag1handler,
    #                            "tag2" => \&tag2handler...)
    #
    #  This allows the user to set custom handlers that are executed
    #  during the parsing process instead of (*not* in addition to
    #  the default handlers.
    
    my ($self, @handler_pairs) = @_;
  
    die ("Uneven number of arguments to setHandlers method")
	if (int(@handler_pairs) & 1);
  
    my @ret;
    while (@handler_pairs) {
	my $type = shift @handler_pairs;
	my $handler = shift @handler_pairs;
	if (ref $handler ne "CODE") {
	    die("Handler for $type must be coderef!");
	}
	unless (defined SWF::tagNum($type)) {
	    my @types = SWF::validTags;
	    die("Unknown Parser handler type: $type\n Valid types: @types");
	}
	$self->{'_handlers'}->{$type} = $handler;
    }

}


#
#  Start defining tag handlers
#

#
#  So, I've decided I've been doing this all wrong - it is morally evil 
#  to access or set instance data by directly accessing the object hash.
#  However, each tag type needs different types of data, and there is no
#  way of knowing which types we'll need beforehand.  So, there are three
#  solutions to this: 1) add ALL data type getter/setters to the tag 
#  class, and only use the ones we need.  2) use a base 'tag' class, and
#  create a subclass for each type of tag with the proper data methods.
#  1) and 2) are, for various reasons, unacceptable to me.  So, when I
#  get some time, I'll do 3) add a method to the 'tag' class which allows
#  the user to dynamically create new methods.  something like:
#    $tag->addMethods('ShapeId', 'BoundRect', 'ColorTransform', ...)
#  I think I like that.  Yeah, I'll do it.  Later.
#

sub pHeader                 {

    my $self = shift;

    #  read and verify SWF signature
    
#use Data::Dumper;
#print "CONTENT:\n". Dumper $self->{'content'};

    my $signature = $self->{'content'}->readBytes(3) ||
	die "SWF::Parser ERROR  -signature too short";
    
    if ($signature ne "FWS" and $signature ne "CWS") {
	die "SWF::Parser ERROR - bad signature: $signature";
    }

    print " Signature: $signature\n" if $debug;

    #  read version

    my $version = $self->{'content'}->readBytes(1) 
	|| die "no version number";
    print "VERS: $version\n";
    $version = ord($version);

    $self->version($version);
    print " SWF Version $version\n" if $debug;

    #  read and verify file length

    my $fileLength = $self->{'content'}->readBytes(4) 
	|| die "file length too short";
    $fileLength = unpack("L",$fileLength);

    if ($fileLength != $self->content->rlength) {
	die "SWF::Parser ERROR - file length $fileLength not correct";
    }
    
    $self->fileLength($fileLength);
    print " FileLength: $fileLength bytes\n" if $debug;

    #  read rect for frame size

    my $frameSize = SWF::Rect->new;
    $frameSize->read($self->content)
	|| die "can't read rect for frame size";
    
    $self->frameSize($frameSize);

    print " FrameSize: " if $debug;
    $frameSize->debug if $debug;

    #  read numbers for frame rate

    my $FR_aft  = ord($self->{'content'}->readBytes(1));
    my $FR_fore = ord($self->{'content'}->readBytes(1));
    my $frameRate = "$FR_fore\.$FR_aft";

    $self->frameRate($frameRate);
    print " FrameRate: $frameRate FPS\n" if $debug;

    #  read number of frames
    
    my $frameCount = unpack("S",$self->{'content'}->readBytes(2));

    $self->frameCount($frameCount);
    print " FrameCount: $frameCount\n" if $debug;

}

sub pEnd                    {
    #  End tag - no action necessary
    my $tag = shift;
    return $tag;
}

sub pShowFrame              {
    #  Show Frame - no action necessary
    my $tag = shift;
    return $tag;
}

sub pDefineShape            {
    my $tag = shift;

    my $shapeID = unpack("S",$tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ShapeId'} = $shapeID;
    print "     ShapeID: $shapeID\n" if $debug;
    
    my $shapeBounds = SWF::Rect->new->read($tag->content);
    $tag->{'ShapeBounds'} = $shapeBounds;
    print "     ShapeBounds: " if $debug;
    $shapeBounds->debug if $debug;

    my $shape = SWF::ShapeWithStyle->new;
    $shape->ShapeType(1);
    $shape->read($tag->{'content'});
    $tag->{'Shapes'} = $shape;

    return $tag;
}

sub pFreeCharacter          {
    #  Free Character - I can't find ANY documentation on this tag.
    my $tag = shift;
    print "Free Character?  What is this?\n" if $debug;
    return $tag;
}

sub pPlaceObject            {
    my $tag = shift;

    my $charID = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'CharacterID'} = $charID;
    print "     CharID:  $charID\n" if $debug;

    my $depth  = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'Depth'} = $depth;
    print "     Depth:  $depth\n" if $debug;

    my $matrix = SWF::Matrix->new->read($tag->content);
    $tag->{'Matrix'} = $matrix;
    $matrix->debug if $debug;
	
    if (my $cxform = SWF::CXform->new->read($tag->content)) {
	$tag->{'ColorTransform'} = $cxform;
        $cxform->debug if $debug;
    }

    return $tag;
}

sub pRemoveObject           {
    my $tag = shift;

    my $charID = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'CharacterID'} = $charID;
    print "     CharID:  $charID\n" if $debug;

    my $depth  = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'Depth'} = $depth;
    print "     Depth:  $depth\n" if $debug;

    return $tag;
}

sub pDefineBits             {
    my $tag = shift;

    my $bitmapID = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'BitmapId'} = $bitmapID;

    my ($byte, $jpeg);
    while ($byte = unpack("C", $tag->{'content'}->readBytes(1))) {
	$jpeg .= $byte;
    }
    $tag->{'BitmapJPEGImage'} = $jpeg;
    
    return $tag;
}

sub pDefineButton           {
    my $tag = shift;

    #  this is a little wonky, because I have to check for end of
    #  buttonrecord and actionrecord bytes before I can read them.
    #  luckily, my readable seek and tell methods should do for this.

    my $buttonID = unpack("S", $tag->{'content'}->readBytes(2));
    $tag->{'ButtonId'} = $buttonID;

    my @buttons = ();
    my $readBR = 1;
    while ($readBR) {
	my $pos = $tag->{'content'}->tell;
	my $flag = ord($tag->{'content'}->readBytes(1));
	if ($flag == 0) {
	    $readBR = 0;
	}
	else {
	    $tag->{'content'}->seek($pos-1);
	    my $buttonrecord = SWF::ButtonRecord->new->read($tag->content);
	    push @buttons, $buttonrecord;
	}
    }    

    my @actions = ();
    my $readAR = 1;
    while ($readAR) {
	my $pos = $tag->{'content'}->tell;
	my $flag = ord($tag->{'content'}->readBytes(1));
	if ($flag == 0) {
	    $readAR = 0;
	}
	else {
	    $tag->{'content'}->seek($pos-1);
	    my $actionrecord = SWF::ActionRecord->new->read($tag->content);
	    push @actions, $actionrecord;
	}
    }    

    $tag->{'Buttons'} = \@buttons;
    $tag->{'Actions'} = \@actions;

    return $tag;
}

sub pJPEGTables             {
    my $tag = shift;
    return $tag;
}

sub pSetBackgroundColor     {

    #  Set Background Color - parse and display RGB

    my $tag = shift;
    
    my $RGB = SWF::RGB->new->read($tag->content);

    $tag->{'BackgroundColor'} = $RGB;
    $RGB->debug if $debug;

    return $tag;
}

sub pDefineFont             {
    my $tag = shift;

    my $fontid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'FontId'} = $fontid;

    my @offsets;
    my @shapes;

    $offsets[0] = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $numElem = $offsets[0] / 2;

    my $i = 1;
    while ($i < $numElem) {
	$offsets[$i] = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$i++;
    }

    $i = 0;
    while ($i < $numElem) {
	$shapes[$i] = SWF::Shape->new->read($tag->content);
	$i++;
    }

    $tag->{'OffsetTable'} = \@offsets;
    $tag->{'ShapeTable'} = \@shapes;

    return $tag;
}

sub pDefineText             {
    my $tag = shift;

    my $textid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $bounds = SWF::Rect->new->read($tag->content);
    my $matrix = SWF::Matrix->new->read($tag->content);
    my $glyphbits = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    my $advancebits = ord($tag->{'SWF::Tag::content'}->readBytes(1));

    $tag->{'TextId'} = $textid;
    $tag->{'TextBounds'} = $bounds;
    $tag->{'TextMatrix'} = $matrix;
    $tag->{'TextGlyphBits'} = $glyphbits;
    $tag->{'TextAdvanceBits'} = $advancebits;

    my @records = ();
    my $readrec = 1;
    while ($readrec) {
	my $pos = $tag->{'SWF::Tag::content'}->tell;
        my $flag = ord($tag->{'SWF::Tag::content'}->readBytes(1));
        if ($flag == 0) {
            $readrec = 0;
	}
	else {
	    $tag->{'SWF::Tag::content'}->seek($pos-1);
	    my $textrecord = SWF::TextRecord->new;
	    $textrecord->GlyphBits($glyphbits);
	    $textrecord->advancebits($advancebits);
	    $textrecord->read($tag->content);
	    push @records, $textrecord;
	}
    }

    $tag->{'TextRecords'} = \@records;

    return $tag;
}

sub pDoAction               {
    my $tag = shift;

    my @actions = ();
    my $readAR = 1;
    while ($readAR) {
	my $pos = $tag->{'SWF::Tag::content'}->tell;
	my $flag = ord($tag->{'SWF::Tag::content'}->readBytes(1));
#	if ($flag == 0) {
	    $readAR = 0;
#	}
#	else {
#	    $tag->{'SWF::Tag::content'}->seek($pos-1);
#	    my $actionrecord = SWF::ActionRecord->new->read($tag->content);
#	    push @actions, $actionrecord;
#	}
    }    

    $tag->{'Actions'} = \@actions;

    return $tag;
}

sub pDefineFontInfo         {
    my $tag = shift;

    my $fontid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $namelen = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    my $name = $tag->{'SWF::Tag::content'}->readBytes($namelen);
    my $flagsReserved = $tag->{'SWF::Tag::content'}->readBits(2);
    my $flagsUnicode = $tag->{'SWF::Tag::content'}->readBits(1);
    my $flagsShiftJIS = $tag->{'SWF::Tag::content'}->readBits(1);
    my $flagsANSI = $tag->{'SWF::Tag::content'}->readBits(1);
    my $flagsItalic = $tag->{'SWF::Tag::content'}->readBits(1);
    my $flagsBold = $tag->{'SWF::Tag::content'}->readBits(1);
    my $flagsWideCodes = $tag->{'SWF::Tag::content'}->readBits(1);

    $tag->{'FontId'} = $fontid;
    $tag->{'FontNameLen'} = $namelen;
    $tag->{'FontName'} = $name;
    $tag->{'FontFlagsReserved'} = $flagsReserved;
    $tag->{'FontFlagsUnicode'} = $flagsUnicode;
    $tag->{'FontFlagsShiftJIS'} = $flagsShiftJIS;
    $tag->{'FontFlagsANSI'} = $flagsANSI;
    $tag->{'FontFlagsItalic'} = $flagsItalic;
    $tag->{'FontFlagsBold'} = $flagsBold;
    $tag->{'FontFlagsWideCodes'} = $flagsWideCodes;

    my $codetable;
    if ($flagsWideCodes) {
	$codetable = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    }
    else {
	$codetable = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    }
    $tag->CodeTable($codetable);

    return $tag;
}

sub pDefineSound            {
    my $tag = shift;

    my $soundID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'SoundId'} = $soundID;

    my $soundformat = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(4));
    $tag->{'SoundFormat'} = $soundformat;
    my $soundrate = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(2));
    $tag->{'SoundRate'} = $soundrate;
    my $soundsize = $tag->{'SWF::Tag::content'}->readBits(1);
    $tag->{'SoundSize'} = $soundsize;
    my $soundtype = $tag->{'SWF::Tag::content'}->readBits(1);
    $tag->{'SoundType'} = $soundtype;

    my $samplecount = unpack("L", $tag->{'SWF::Tag::content'}->readBytes(4));
    $tag->{'SoundSampleCount'} = $samplecount;

    my $sounddata = $tag->{'SWF::Tag::content'}->readBytes($tag->{'SWF::Tag::content'}->length);
    $tag->{'SoundData'} = $sounddata;

    return $tag;
}

sub pStartSound             {
    my $tag = shift;

    my $soundID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'SoundId'} = $soundID;
    
    my $soundInfo = SWF::SoundInfo->new->read($tag->{'SWF::Tag::content'});
    $tag->{'SOUNDINFO'} = $soundInfo;

    return $tag;
}

sub pDefineButtonSound      {
    my $tag = shift;

    my $buttonID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonId'} = $buttonID;

    my $buttonSC0 = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonSoundChar0'} = $buttonSC0;

    my $buttonSI0 = SWF::SoundInfo->new->read($tag->content);
    $tag->{'ButtonSoundInfo0'} = $buttonSI0;

    my $buttonSC1 = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonSoundChar1'} = $buttonSC1;

    my $buttonSI1 = SWF::SoundInfo->new->read($tag->content);
    $tag->{'ButtonSoundInfo1'} = $buttonSI1;

    my $buttonSC2 = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonSoundChar2'} = $buttonSC2;

    my $buttonSI2 = SWF::SoundInfo->new->read($tag->content);
    $tag->{'ButtonSoundInfo2'} = $buttonSI2;

    my $buttonSC3 = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonSoundChar3'} = $buttonSC3;

    my $buttonSI3 = SWF::SoundInfo->new->read($tag->content);
    $tag->{'ButtonSoundInfo3'} = $buttonSI3;

    return $tag;
}

sub pSoundStreamHead        {
    my $tag = shift;

    my $reserved = $tag->{'SWF::Tag::content'}->readBits(4);
    my $pbRate = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(2));
    my $pbSize = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $pbType = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sCompression = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(4));
    my $sRate = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(2));
    my $sSize = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sType = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sCount = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));

    $tag->{'PlaybackSoundRate'} = $pbRate; 
    $tag->{'PlaybackSoundSize'} = $pbSize; 
    $tag->{'PlaybackSoundType'} = $pbType; 
    $tag->{'StreamSoundCompression'} = $sCompression;
    $tag->{'StreamSoundRate'} = $sRate;  
    $tag->{'StreamSoundSize'} = $sSize;  
    $tag->{'StreamSoundType'} = $sType;  
    $tag->{'StreamSoundSampleCount'} = $sCount; 

    return $tag;
}

sub pSoundStreamBlock       {
    my $tag = shift;
    return $tag;
}

sub pDefineBitsLossless     {
    my $tag = shift;
    return $tag;
}

sub pDefineBitsJPEG2        {
    my $tag = shift;
    return $tag;
}

sub pDefineShape2           {
    my $tag = shift;

    my $shapeID = unpack("S",$tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ShapeId'} = $shapeID;
    print "     ShapeID: $shapeID\n" if $debug;
    
    my $shapeBounds = SWF::Rect->new->read($tag->content);
    $tag->{'ShapeBounds'} = $shapeBounds;
    print "     ShapeBounds: " if $debug;
    $shapeBounds->debug if $debug;

    my $shape = SWF::ShapeWithStyle->new;
    $shape->ShapeType(2);
    $shape->read($tag->{'SWF::Tag::content'});
    $tag->{'Shapes'} = $shape;

    return $tag;
}

sub pDefineButtonCxform     {
    my $tag = shift;

    my $buttonID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonId'} = $buttonID;

    my $cxform = SWF::CXform->new->read($tag->content);
    $tag->{'ButtonColorTransform'} = $cxform;

    return $tag;
}

sub pProtect                {
    #  Protect tag - no action necessary
    my $tag = shift;
    return $tag;
}

sub pPathsArePostScript     {
    my $tag = shift;
    return $tag;
}

sub pPlaceObject2           {
    my $tag = shift;

    my $pfReserved          = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasDefineClip     = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasName           = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasRatio          = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasColorTransform = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasMatrix         = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfHasCharacter      = $tag->{'SWF::Tag::content'}->readBits(1);
    my $pfMove              = $tag->{'SWF::Tag::content'}->readBits(1);
    
    my $depth  = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'Depth'} = $depth;
    print "     Depth:  $depth\n" if $debug;

    if ($pfHasCharacter) {
	my $charID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'CharacterID'} = $charID;
	print "     CharID:  $charID\n" if $debug;
    }

    if ($pfHasMatrix) {
	my $matrix = SWF::Matrix->new->read($tag->content);
	$tag->{'Matrix'} = $matrix;
	$matrix->debug if $debug;
    }
	
    if ($pfHasColorTransform) {
	my $cxform = SWF::CXform->new->read($tag->content);
	$tag->{'ColorTransform'} = $cxform;
	$cxform->debug if $debug;
    }

    if ($pfHasRatio) {
	my $ratio = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'Ratio'} = $ratio;
	print "     Ratio: $ratio\n" if $debug;
    }

    if ($pfHasDefineClip) {
	my $clipDepth = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'ClipDepth'} = $clipDepth;
	print "     ClipDepth: $clipDepth\n" if $debug;
    }

    if ($pfHasName) {
	my $name = $tag->{'SWF::Tag::content'}->readString;
	$tag->{'Name'} = $name;
	print "     Name: $name\n" if $debug;
    }

    return $tag;
}

sub pRemoveObject2          {
    my $tag = shift;

    my $depth  = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'Depth'} = $depth;
    print "     Depth:  $depth\n" if $debug;

    return $tag;
}

sub pSyncFrame              {
    my $tag = shift;
    return $tag;
}

sub pFreeAll                {
    my $tag = shift;
    return $tag;
}

sub pDefineShape3           {
    my $tag = shift;

    my $shapeID = unpack("S",$tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ShapeId'} = $shapeID;
    print "     ShapeID: $shapeID\n" if $debug;
    
    my $shapeBounds = SWF::Rect->new->read($tag->content);
    $tag->{'ShapeBounds'} = $shapeBounds;
    print "     ShapeBounds: " if $debug;
    $shapeBounds->debug if $debug;

    my $shape = SWF::ShapeWithStyle->new;
    $shape->ShapeType(3);
    $shape->read($tag->{'SWF::Tag::content'});
    $tag->{'Shapes'} = $shape;

    return $tag;
}

sub pDefineText2            {
    my $tag = shift;

    my $textid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $bounds = SWF::Rect->new->read($tag->content);
    my $matrix = SWF::Matrix->new->read($tag->content);
    my $glyphbits = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    my $advancebits = ord($tag->{'SWF::Tag::content'}->readBytes(1));

    $tag->{'TextId'} = $textid;
    $tag->{'TextBounds'} = $bounds;
    $tag->{'TextMatrix'} = $matrix;
    $tag->{'TextGlyphBits'} = $glyphbits;
    $tag->{'TextAdvanceBits'} = $advancebits;

    my @records = ();
    my $readrec = 1;
    while ($readrec) {
	my $pos = $tag->{'SWF::Tag::content'}->tell;
        my $flag = ord($tag->{'SWF::Tag::content'}->readBytes(1));
        if ($flag == 0) {
            $readrec = 0;
	}
	else {
	    $tag->{'SWF::Tag::content'}->seek($pos-1);
	    my $textrecord = SWF::TextRecord->new;
	    $textrecord->GlyphBits($glyphbits);
	    $textrecord->AdvanceBits($advancebits);
	    $textrecord->TextType(2);
	    $textrecord->read($tag->content);
	    push @records, $textrecord;
	}
    }

    $tag->{'TextRecords'} = \@records;

    return $tag;
}

sub pDefineButton2          {

    #  I'm not sure I understand this whole Button2ActionCondition offset
    #  thing - I'll have to investigate some more before this tag is complete.

    my $tag = shift;

    my $buttonID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'ButtonId'} = $buttonID;

    my $flags = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    $tag->{'Flags'} = $flags;

    my $offset = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'Offset'} = $offset;

    return $tag;
}

sub pDefineBitsJPEG3        {
    my $tag = shift;
    return $tag;
}

sub pDefineBitsLossless2    {
    my $tag = shift;
    return $tag;
}

sub pDefineEditText         {
    my $tag = shift;

    my $textid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $bounds = SWF::Rect->new->read($tag->content);
    my $hastext = $tag->{'SWF::Tag::content'}->readBits(1);
    my $wordwrap = $tag->{'SWF::Tag::content'}->readBits(1);
    my $multiline = $tag->{'SWF::Tag::content'}->readBits(1);
    my $password = $tag->{'SWF::Tag::content'}->readBits(1);
    my $readonly = $tag->{'SWF::Tag::content'}->readBits(1);
    my $hastextcolor = $tag->{'SWF::Tag::content'}->readBits(1);
    my $hasmaxlength = $tag->{'SWF::Tag::content'}->readBits(1);
    my $hasfont = $tag->{'SWF::Tag::content'}->readBits(1);
    my $reserved = $tag->{'SWF::Tag::content'}->readBits(2);
    my $haslayout = $tag->{'SWF::Tag::content'}->readBits(1);
    my $noselect = $tag->{'SWF::Tag::content'}->readBits(1);
    my $border = $tag->{'SWF::Tag::content'}->readBits(1);
    my $reserved2 = $tag->{'SWF::Tag::content'}->readBits(2);
    my $useoutlines = $tag->{'SWF::Tag::content'}->readBits(1);

    $tag->{'TextId'} = $textid;
    $tag->{'Bounds'} = $bounds;
    $tag->{'HasText'} = $hastext;
    $tag->{'WordWrap'} = $wordwrap;
    $tag->{'Multiline'} = $multiline;
    $tag->{'Password'} = $password;
    $tag->{'ReadOnly'} = $readonly;
    $tag->{'HasTextColor'} = $hastextcolor;
    $tag->{'HasMaxLength'} = $hasmaxlength;
    $tag->{'HasFont'} = $hasfont;
    $tag->{'HasLayout'} = $haslayout;
    $tag->{'NoSelect'} = $noselect;
    $tag->{'Border'} = $border;
    $tag->{'UseOutlines'} = $useoutlines;

    if ($hasfont) {
	my $fontid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $fontheight = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'FontID'} = $fontid;
	$tag->{'FontHeight'} = $fontheight;
    }
    if ($hastextcolor) {
	my $textcolor = SWF::RGBA->new->read($tag->content);
	$tag->{'TextColor'} = $textcolor;
    }
    if ($hasmaxlength) {
	my $maxlength = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'MaxLength'} = $maxlength;
    }
    if ($haslayout) {
	my $align = ord($tag->{'SWF::Tag::content'}->readBytes(1));
	my $leftmargin = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $rightmargin = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $indent = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $leading = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'Align'} = $align;
	$tag->{'LeftMargin'} = $leftmargin;
	$tag->{'RightMargin'} = $rightmargin;
	$tag->{'Indent'} = $indent;
	$tag->{'Leading'} = $leading;
    }
    my $variablename = SWF::String->new->read($tag->content);
    $tag->{'VariableName'} = $variablename;
    if ($hastext) {
	my $initialtext = SWF::String->new->read($tag->content);
	$tag->{'InitialText'} = $initialtext;
    }

    return $tag;
}

sub pDefineMovie            {
    my $tag = shift;

    my $movieID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'MovieID'} = $movieID;

    my $movieName = $tag->{'SWF::Tag::content'}->readString;
    $tag->{'MovieName'} = $movieName;

    return $tag;
}

sub pDefineSprite           {
    my $tag = shift;
    return $tag;
}

sub pNameCharacter          {
    my $tag = shift;
    return $tag;
}

sub pSerialNumber           {
    my $tag = shift;
    return $tag;
}

sub pDefineTextFormat       {
    my $tag = shift;
    return $tag;
}

sub pFrameLabel             {

    #  Add Frame Label - taken care of in control tag stuff up there

    my $tag = shift;

    print "     Label: \"".$tag->{'Name'}."\" Frame: ".$tag->frame."\n" 
	if $debug;

    return $tag;
}

sub pSoundStreamHead2       {
    my $tag = shift;

    my $reserved = $tag->{'SWF::Tag::content'}->readBits(4);
    my $pbRate = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(2));
    my $pbSize = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $pbType = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sCompression = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(4));
    my $sRate = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(2));
    my $sSize = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sType = SWF::_bits2int($tag->{'SWF::Tag::content'}->readBits(1));
    my $sCount = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));

    $tag->{'PlaybackSoundRate'} = $pbRate; 
    $tag->{'PlaybackSoundSize'} = $pbSize; 
    $tag->{'PlaybackSoundType'} = $pbType; 
    $tag->{'StreamSoundCompression'} = $sCompression;
    $tag->{'StreamSoundRate'} = $sRate;  
    $tag->{'StreamSoundSize'} = $sSize;  
    $tag->{'StreamSoundType'} = $sType;  
    $tag->{'StreamSoundSampleCount'} = $sCount; 

    return $tag;
}

sub pDefineMorphShape       {
    my $tag = shift;

    my $charID = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    my $startbounds = SWF::Rect->new->read($tag->content);
    my $endbounds = SWF::Rect->new->read($tag->content);
    my $offset = unpack("L", $tag->{'SWF::Tag::content'}->readBytes(4));
    my $mFillStyles = SWF::MorphFillStyles->new->read($tag->content);
    my $mLineStyles = SWF::MorphLineStyles->new->read($tag->content);
    my $startEdges = SWF::Shape->new->read($tag->content);
    my $endEdges = SWF::Shape->new->read($tag->content);

    $tag->{'CharacterID'} = $charID;
    $tag->{'StartBounds'} = $startbounds;
    $tag->{'EndBounds'} = $endbounds;
    $tag->{'Offset'} = $offset;
    $tag->{'MorphFillStyles'} = $mFillStyles;
    $tag->{'MorphLineStyles'} = $mLineStyles;
    $tag->{'StartEdges'} = $startEdges;
    $tag->{'EndEdges'} = $endEdges;

    return $tag;
}

sub pFrameTag               {
    my $tag = shift;
    return $tag;
}

sub pDefineFont2            {
    my $tag = shift;

    my $fontid = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    $tag->{'FontId'} = $fontid;

    my $haslayout = $tag->{'SWF::Tag::content'}->readBits(1);
    my $shiftJIS = $tag->{'SWF::Tag::content'}->readBits(1);
    my $unicode = $tag->{'SWF::Tag::content'}->readBits(1);
    my $ANSI = $tag->{'SWF::Tag::content'}->readBits(1);
    my $wideoffsets = $tag->{'SWF::Tag::content'}->readBits(1);
    my $widecodes = $tag->{'SWF::Tag::content'}->readBits(1);
    my $italic = $tag->{'SWF::Tag::content'}->readBits(1);
    my $bold = $tag->{'SWF::Tag::content'}->readBits(1);
    my $reserved = $tag->{'SWF::Tag::content'}->readBits(8);
    my $namelen = ord($tag->{'SWF::Tag::content'}->readBytes(1));
    my $name = $tag->{'SWF::Tag::content'}->readBytes($namelen);
    my $glyphcount = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));

    $tag->{'FontFlagsHasLayout'} = $haslayout;
    $tag->{'FontFlagsShiftJIS'} = $shiftJIS;
    $tag->{'FontFlagsUnicode'} = $unicode;
    $tag->{'FontFlagsAnsi'} = $ANSI;
    $tag->{'FontFlagsWideOffsets'} = $wideoffsets;
    $tag->{'FontFlagsWideCodes'} = $widecodes;
    $tag->{'FontFlagsItalic'} = $italic;
    $tag->{'FontFlagsBold'} = $bold;
    $tag->{'FontFlagsReserved'} = $reserved;
    $tag->{'FontNameLen'} = $namelen;
    $tag->{'FontName'} = $name;

    no warnings;
    my @offsets;
    my $i = 0;
    while ($i < $glyphcount) {
	my $offset;
	if ($wideoffsets) {
	    $offset = unpack("L", $tag->{'SWF::Tag::content'}->readBytes(4));
	}
	else {
	    $offset = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	}
	push @offsets, $offset;
	$i++;
    }
    $tag->{'FontOffsetTable'} = \@offsets;

    my $codeoffset;
    if ($wideoffsets) {
	$codeoffset = unpack("L", $tag->{'SWF::Tag::content'}->readBytes(4));
    }
    else {
	$codeoffset = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
    }
    $tag->{'FontCodeOffset'} = $codeoffset;

    my @shapes;
    $i = 0;
    while ($i < $glyphcount) {
	my $shape = SWF::Shape->new->read($tag->content);
	push @shapes, $shape;
	$i++;
    }
    $tag->{'FontShapeTable'} = \@shapes;

    my @codes;
    $i = 0;
    while ($i < $glyphcount) {
        my $code;
        if ($widecodes) {
            $code = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
        }
        else {
            $code = unpack("C", $tag->{'SWF::Tag::content'}->readBytes(1));
        }
        push @codes, $code;
        $i++;
    }
    $tag->{'FontCodeTable'} = \@codes;

    if ($haslayout) {
	my $ascent = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $descent = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	my $leading = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'FontAscent'} = $ascent;
	$tag->{'FontDescent'} = $descent;
	$tag->{'FontLeading'} = $leading;

	my @advances;
	$i = 0;
	while ($i < $glyphcount) {
	    my $advance = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));	
	    push @advances, $advance;
	    $i++;
	}
	$tag->{'FontAdvanceTable'} = \@advances;

	my @bounds;
	$i = 0;
	while ($i < $glyphcount) {	
	    my $bound = SWF::Rect->new->read($tag->content);
	    push @bounds, $bound;
	    $i++;
	}
	$tag->{'FontBoundsTable'} = \@bounds;

	my $kerningcount = unpack("S", $tag->{'SWF::Tag::content'}->readBytes(2));
	$tag->{'FontKerningCount'} = $kerningcount;

	my @kernings;
	$i = 0;
	while ($i < $kerningcount) {
	    my $kerning = SWF::KerningRecord->new->read($tag->content);
	    push @kernings, $kerning;
	    $i++;
	}
	$tag->{'FontKerningTable'} = \@kernings;

    }

    return $tag;
}

sub pGenCommand             {
    my $tag = shift;
    return $tag;
}

sub pDefineCommandObj       {
    my $tag = shift;
    return $tag;
}

sub pCharacterSet           {
    my $tag = shift;
    return $tag;
}

sub pFontRef                {
    my $tag = shift;
    return $tag;
}

sub pDefineBitsPtr          {
    my $tag = shift;
    return $tag;
}


1;
