package Crypt::Keyczar::Tool;

use strict;
use warnings;
use Crypt::Keyczar::Crypter;
use Crypt::Keyczar::FileReader;
use Crypt::Keyczar::KeyMetadata;
use Crypt::Keyczar::Manager;
use Crypt::Keyczar::Signer;
use Crypt::Keyczar::Util;
use Crypt::Keyczar::FileWriter;
use Carp;


sub new {
    my $class = shift;
    my $writer = shift;
    $writer ||= Crypt::Keyczar::FileWriter->new();
    return bless { writer => $writer }, $class;
}


sub writer { return $_[0]->{writer} }


sub create {
    my $self = shift;
    my ($location, $purpose, $opt) = @_;

    my $meta;
    if ($purpose eq 'sign') {
        if (!defined $opt->{asymmetric} && !defined $opt->{type}) {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'SIGN_AND_VERIFY', 'HMAC_SHA1');
        }
        elsif (!defined $opt->{asymmetric} && defined $opt->{type}) {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'SIGN_AND_VERIFY', $opt->{type});
        }
        elsif (uc $opt->{asymmetric} eq 'RSA') {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'SIGN_AND_VERIFY', 'RSA_PRIV');
        }
        else {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'SIGN_AND_VERIFY', 'DSA_PRIV');
        }
    }
    elsif ($purpose eq 'crypt') {
        if (defined $opt->{asymmetric}) {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'DECRYPT_AND_ENCRYPT', 'RSA_PRIV');
        }
        else {
            $meta = Crypt::Keyczar::KeyMetadata->new($opt->{name}, 'DECRYPT_AND_ENCRYPT', 'AES');
        }
    }
    else {
        croak 'unknonw purpose';
    }

    $self->writer->location($location);
    $self->writer->put_metadata($meta);
}


sub addkey {
    my $self = shift;
    my ($location, $status, $opt) = @_;

    my $kcz = Crypt::Keyczar::Manager->new($location);
    my $v = $kcz->add_version($status, $opt->{size});

    $self->writer->location($location);
    $self->writer->put_metadata($kcz->metadata);
    $self->writer->put_key($v->get_number, $kcz->get_key($v));
    return $v->get_number;
}


sub promote {
    my $self = shift;
    my ($location, $version) = @_;
    my $kcz = Crypt::Keyczar::Manager->new($location);
    $kcz->promote($version);
    $self->writer->location($location);
    $self->writer->put_metadata($kcz->metadata);
}


sub demote {
    my $self = shift;
    my ($location, $version) = @_;
    my $kcz = Crypt::Keyczar::Manager->new($location);
    $kcz->demote($version);
    $self->writer->location($location);
    $self->writer->put_metadata($kcz->metadata);
}


sub revoke {
    my $self = shift;
    my ($location, $version) = @_;
    my $kcz = Crypt::Keyczar::Manager->new($location);
    $kcz->revoke($version);
    $self->writer->location($location);
    $self->writer->put_metadata($kcz->metadata);
    if (!$self->writer->delete_key($version)) {
        croak "can't delete revoked key file: $location/$version: $!";
    }
}


sub pubkey {
    my $self = shift;
    my ($location, $destination) = @_;
    my $kcz = Crypt::Keyczar::Manager->new($location);
    my $private = $kcz->metadata;

    my $public;
    if ($private->get_type eq 'RSA_PRIV') {
        if ($private->get_purpose eq 'DECRYPT_AND_ENCRYPT') {
            $public = Crypt::Keyczar::KeyMetadata->new($private->get_name, 'ENCRYPT', 'RSA_PUB');
        }
        elsif ($private->get_purpose eq 'SIGN_AND_VERIFY') {
            $public = Crypt::Keyczar::KeyMetadata->new($private->get_name, 'VERIFY', 'RSA_PUB');
        }
        else {
            croak("unknown propose: ". $private->get_purpose);
        }
    }
    elsif ($private->get_type eq 'DSA_PRIV') {
        if ($private->get_purpose eq 'SIGN_AND_VERIFY') {
            $public = Crypt::Keyczar::KeyMetadata->new($private->get_name, 'VERIFY', 'DSA_PUB');
        }
        else {
            croak("unknown propose: ". $private->get_purpose);
        }
    }
    else {
        croak("not implement");
    }
    if (!$public) {
        croak("cannot export public key");
    }

    $self->writer->location($destination);
    for my $v ($private->get_versions) {
        if ($v) {
            my $p = $kcz->get_key($v)->get_public;
            $self->writer->put_key($v->get_number, $p);
        }
        $public->add_version($v);
    }
    $self->writer->put_metadata($public);
}


sub usekey {
    my $self = shift;
    my ($location, $message, $destination, $opt) = @_;

    my $reader = Crypt::Keyczar::FileReader->new($location);
    my $meta = Crypt::Keyczar::KeyMetadata->read($reader->get_metadata());
    my $result = '';
    if ($meta->get_purpose eq 'DECRYPT_AND_ENCRYPT') {
        my $crypter = Crypt::Keyczar::Crypter->new($reader);
        $result = $crypter->encrypt($message);
    }
    elsif ($meta->get_purpose eq 'SIGN_AND_VERIFY') {
        my $signer = Crypt::Keyczar::Signer->new($reader);
        $result = $signer->sign($message);
    }
    else {
        croak "unsupported purpose: ". $meta->get_purpose;
    }

    if ($destination) {
        open my $fh, '>', $destination
            or croak "cannot open destination file: $destination: $!";
        print $fh Crypt::Keyczar::Util::encode($result);
        close $fh;
    }
    else {
        print Crypt::Keyczar::Util::encode($result), "\n";
    }
}

1;
__END__