package JIB::Package;

use strict;
use warnings;
use base 'Object::Accessor';

use JIB::Constants;
use JIB::Config;

use JIB::Package::Source;
use JIB::Package::Binary;
use JIB::Package::Installed;
use JIB::Package::Installable;

use File::Spec;
use File::Basename          qw[basename];
use Params::Check           qw[check];
use Log::Message::Simple    qw[:STD];
use YAML                    qw[LoadFile];

use Data::Dumper;

$Data::Dumper::Indent = 1;

my $Package_re = qr/^(\w+)     - # prefix
                    ([\w-]+?)  - # package name
                    ([\d.]+)   - # version
                    (\w+\+\S+) $ # authority
                /smx;

=head1 NAME

    JIB::Package

=head1 DESCRIPTION

Base class for:

    JIB::Package::Source
    JIB::Package::Binary
    JIB::Package::Installed

=head1 ACCESSORS 

=head2 package

Set the name of the full package package. For example:

    p5-foo-1-cpan+kane

=head1 METHODS

=head2 $pkg = JIB::Package->new( file => /path/to/jib | meta => META_OBJ );

Returns a JIB::Package::Source on a jib file, and a JIB::Package::Installed
on a META_OBJ.

XXX needs verification
XXX needs binary package recognition

=cut

{   my $acc = {
        file            => { allow => FILE_EXISTS },
        meta            => { allow => ISA_JIB_META },
        config          => { allow => ISA_JIB_CONFIG },
        package         => { allow => $Package_re, no_override => 1 },
    };

    ### acc is just for accessors, the others get added for our 
    ### param checking too
    my $tmpl = {
        %$acc,
        installation    => { allow => ISA_JIB_INSTALLATION },
        repository      => { allow => ISA_JIB_REPOSITORY },
    };

    sub new {
        my $class = shift;
        my %hash  = @_;
        
        ### might get params for underlying classes
        local $Params::Check::ALLOW_UNKNOWN = 1;
        my $args = check( $tmpl, \%hash ) 
                        or error( Params::Check->last_error ), return;
        
        ### XXX need better checks
        
        ### XXX create an object::accessor object, blessed in the right class
        my $obj = 
            $args->{repository} && $args->{file}
                ? JIB::Package::Installable->Object::Accessor::new      :
            $args->{file}           
                ? JIB::Package::Source->Object::Accessor::new           :
            $args->{installation}   
                ? JIB::Package::Installed->Object::Accessor::new        :
            JIB::Package::Binary->Object::Accessor::new;
    
        return unless $obj;

        ### create accessors
        my %acc = map { $_ => $acc->{$_}->{allow} } keys %$acc;
        $obj->mk_accessors( \%acc );
        
        ### set the config
        $obj->config( JIB::Config->new );
        
        ### call the objects new method and return it
        return $obj->new( %hash );
        
    }
}    

=head2 $meta = $pkg->extract_meta_object;

=cut

sub extract_meta_object {
    my $self = shift;
    my $conf = $self->config;
    my $meta = $self->meta;

    ### if we didn't get a meta object, we'll fetch it from the .jib
    unless( $meta ) {
        
        ### installed packages don't have a 'file' method, but they /should/
        ### have a meta object at all times
        unless( $self->can('file') ) {
            error("No file associated with this object -- " .
                  "can not extract meta object" );
            return;
        }
        
        ### extract to a temp dir
        my $my_tmp_dir = File::Spec->catdir( $conf->temp_dir . "$$" );
        system( qq[mkdir -p $my_tmp_dir] )                      and die $?;
        
        ### extract the archive to the temp dir
        system( qq[tar -f ] . $self->file . qq[ -C $my_tmp_dir -xz]) 
                                                                and die $?;

        ### extract the meta info
        my $control  = $conf->archive_control;
        system( qq[tar -f $my_tmp_dir/$control -C $my_tmp_dir -xz] )
                                                                and die $?;
              
        $meta = eval { LoadFile( File::Spec->catfile( 
                                    $my_tmp_dir,
                                    $conf->meta_file )
                ) };
        $@ and error( "Could not load meta file from archive: $@" ), return;
    
        $self->meta(JIB::Meta->new_from_struct(struct => $meta)) or return;
        system( "rm -rf $my_tmp_dir" )                          and die $?;
    }

    return $meta;
}

=head2 package_re

=head2 prefix

=head2 name

=head2 version

=head2 authority

=cut

### XXX could autogenerate
{   
    sub package_re { $Package_re };

    sub prefix {
        return $1 if shift->package() =~ $Package_re;
    }

    sub name {
        return $2 if shift->package() =~ $Package_re;
    }

    sub version {
        return $3 if shift->package() =~ $Package_re;
    }
    
    sub authority {
        return $4 if shift->package() =~ $Package_re;
    }
}    

1;

# Local variables:
# c-indentation-style: bsd
# c-basic-offset: 4
# indent-tabs-mode: nil
# End:
# vim: expandtab shiftwidth=4:
