package PGP::Packet;

use strict;

use Carp;
use Digest::MD5;
use Digest::SHA1;

use PGP::Unpackable;
use PGP::Packet::Pubkey;
use PGP::Packet::Subkey;
use PGP::Packet::Userid;
use PGP::Packet::Signature;

BEGIN {
    no strict 'refs';

    foreach my $accessor (qw(ctag bodyref packetref)) {
	*{$accessor} = sub {
	    my ($this) = @_;
	    $this->{$accessor};
	};
    }
};

sub new {
    my ($classname, $ctag, $bodyref, $packetref) = @_;

    bless {
	"ctag" => $ctag,
	"bodyref" => $bodyref,
	"packetref" => $packetref,
    }, $classname;
}

sub next {
    my ($uring) = @_;

    my ($ctag, $bodyref);

    if ($uring->remaining == 0) {
	return(undef);
    }

    my $begin = $uring->offset();

    my $ptag = $uring->unpackC();

    if (($ptag & 0xc0) == 0x80) {
	# old (2.6.x) format

	$ctag = ($ptag & 0x3c) >> 2;
	my $ltype = ($ptag & 0x03);

	my $plen;

	if ($ltype == 0) {
	    $plen = $uring->unpackC();
	} elsif ($ltype == 1) {
	    $plen = $uring->unpack("n");
	} elsif ($ltype == 2) {
	    $plen = $uring->unpack("N");
	} elsif ($ltype == 3) {
	    $plen = $uring->remaining();
	}

	$bodyref = \$uring->unpacka($plen);
    } elsif (($ptag & 0xc0) == 0xc0) {
	# new (5.x) packet format

	$ctag = ($ptag & 0x3f);

	my $plen;

	while(1) {
	    my $lbyte = $uring->unpackC();

	    if ($lbyte < 192) {
		$plen = $lbyte;
		last;
	    } elsif ($lbyte < 224) {
		my $octet = $uring->unpackC();
		$plen = (($lbyte - 192) << 8) + $octet + 192;
		last;
	    } elsif ($lbyte == 255) {
		$plen = $uring->unpack("N");
		last;
	    } else {
		$plen = (1 << ($lbyte & 0x1f));
		$$bodyref .= $uring->unpacka($plen);
	    }
	}

	$$bodyref .= $uring->unpacka($plen);
    }

    my $end = $uring->offset();

    my $packetref = \$uring->substr($begin, $end-$begin);

    if ($ctag == 6) {
	return PGP::Packet::Pubkey->new($bodyref, $packetref);
    } elsif ($ctag == 14) {
	return PGP::Packet::Subkey->new($bodyref, $packetref);
    } elsif ($ctag == 13) {
	return PGP::Packet::Userid->new($bodyref, $packetref);
    } elsif ($ctag == 2) {
	return PGP::Packet::Signature->new($bodyref, $packetref);
    } else {
	return PGP::Packet->new($ctag, $bodyref, $packetref);
    }
}

1;
