#!/usr/bin/perl
=head1 NAME

/usr/lib/obs/service/dzil_build - Generate a cpan dist with Zilla

=head1 SYNOPSIS

/usr/lib/obs/service/dzil_build [options]

Options:

   --source             Glob of source tarball, or directory, or obsinfo
   --count              How much sources i need to parse (default: 1)
   --outdir             Output directory
   --filename           Specify name of package (macros supported)
   --filename-regex     Specify regex over filename variable
   --version            Specify version of package (macros supported)
   --version-regex      Specify regex over version variable
   --help               Show this output
   --without-version    Specify use of version (default: disable)
   --generate           File types to generate, with ',' delimiter (available: info, cpio, tar)
   --noop               Do nothing
=cut
use Pod::Usage;
use Getopt::Long;
use Dist::Zilla::Chrome::Term;
use Dist::Zilla::Dist::Builder;
use Archive::Libarchive::Extract;
use File::Path qw(make_path);
use File::Spec;
use Archive::Libarchive ':const';
use Path::Tiny qw( path );
use File::Slurper qw(write_text);

no warnings "deprecated";

our $help;
our $code; if (!defined($code)) { $code = 0; };
our $count; if (!defined($count)) { $count = 1; };
our $noop;
our $outdir;
our $source;
our @sources;
our $current_file; if (!defined($current_file)) { $current_file = __FILE__; };
our $generate; if (!defined($generate)) { $generate = 'cpio,info'; };
our $name_ref; if (!defined($name_ref)) { $name_ref = '%{name}'; };
our $version_ref; if (!defined($version_ref)) { $version_ref = '%{version}'; };
our $version_regex;
our $name_regex;
our $version_place;
our @filelist;

GetOptions(
    'help'                 => \$help,
    'source=s'             => sub{
      push @sources, $_[1];
    },
    'count=s'              => \$count,
    'noop=s'               => \$noop,
    'outdir=s'             => \$outdir,
    'generate=s'           => \$generate,
    'filename=s'           => \$name_ref,
    'version=s'            => \$version_ref,
    'version-regex=s'      => \$version_regex,
    'filename-regex=s'     => \$name_regex,
    'without-version=s'    => \$version_place
);

$count = int($count);

if ($noop eq "enable"){
  exit(0);
}


for my $src (@sources){
  my @files = glob($src);
  if (scalar(@files) > 1 || -e $files[0]){
    push @filelist, @files;
  }
}

if ($count > 0 && scalar @filelist > $count){
  @filelist = @filelist[0..$count-1];
}

if (((! scalar(@filelist)) || (! defined($outdir))) && (! $help) ){
   $code = 1;
   goto jump_label;
}

if ( $help ){
   jump_label:
   pod2usage({-exitval => $code, -input => $current_file});
}

goto count_data_label;
count_data_return:

 if ( -f $source ) {
    my $filename = $source;
    my $archive = Archive::Libarchive::Extract->new(filename => $filename);
    my $number = 0;
    my $temp;
    do{
       $number++;
       $temp = sprintf("%08X%08X", rand(0xFFFFFFFF), $number);
       $source = "${filename}${temp}";
    } while ( -e $source);
    $archive->extract(to => $source);
 } else {
    make_path($source);
 }

my $zilla = Dist::Zilla::Dist::Builder->from_config({dist_root => $source, chrome=> Dist::Zilla::Chrome::Term->new()});

my $meta = $zilla->distmeta;

sub abr{
   my $zilla = shift;
   my $prefix = shift;
   my $element = shift;
   my $ret_meta = $meta;
   if (defined($prefix)){
      for my $str1 (split(/\./, $element)){
         my @str2_array = split(/\[|\]\[|\]/, $str1);
         my $str2 = shift @str2_array;
         if (defined($str2)){
            $ret_meta = $ret_meta->{$str2};
            for $str2 (@str2_array){
               $ret_meta = $ret_meta->[int($str2)];
            }
         }
      }
      return join('',$prefix,"$ret_meta");
   } else {
      return '%';
   }
}

sub replaceWithAbr{
   my $zilla = shift;
   my $str = shift;
   $str = " $str";
   $str=~s/([^%])%\{([a-zA-Z][[a-zA-Z0-9\.\[\]]*)\}|%%/abr($zilla,$1,$2)/ge;
   return substr($str, 1);
};

$version = replaceWithAbr($zilla, $version_ref);
$name = replaceWithAbr($zilla, $name_ref);

if (defined($version_regex)) { $version = eval "\$_=${version}; ${version_regex}; return \$_"; };
if (defined($name_regex)) { $name = eval "\$_=${name}; ${name_regex}; return \$_"; };

if ($version_place eq "enable"){
   $_ = "$name";
} else {
   $_ = "$name-$version";
}

my $obsbase = $_;
my $obspath = File::Spec->catdir($outdir, $_);
$zilla->build_in($obspath);

make_path($obspath);
path($source)->visit(sub  {
   my $path = shift;
   if($path->is_dir){
      path(File::Spec->catdir($obspath, $path->relative($source)))->mkpath();
   } else {
      $path->copy(path(File::Spec->catfile($obspath, $path->relative($source))));
   }
}, { recurse => 1 });

my $filename = File::Spec->catfile($outdir, $name);

for my $type (split(',', $generate)){
   if ($type eq "cpio"){
      add_files(ARCHIVE_FORMAT_CPIO_BIN_LE, "obscpio");
   } elsif ($type eq "tar"){
      add_files(ARCHIVE_FORMAT_TAR, "tar");
   } elsif ($type eq "gzip"){
      add_files(ARCHIVE_FORMAT_TAR_GNUTAR, "tar.gz");
   } elsif ($type eq "info"){
      my $mtime = "" .. time();
      my $commit = join('', map {sprintf("%04x", rand(0xFFFF)) }  1..10);
      write_text("${filename}.obsinfo",
      "name: ${filename}\nversion: ${version}\nmtime: ${mtime}\ncommit: ${commit}");
   }
}

sub add_files{

   my $w = Archive::Libarchive::ArchiveWrite->new;
   my ($format, $type) = @_;
   $w->set_format($format);
   $w->open_filename("${obspath}.${type}");

   path($obspath)->visit(sub  {
      my $path = shift;
      return if $path->is_dir;

      my $e = Archive::Libarchive::Entry->new;
      my $name = $path->relative($obspath);
      $e->set_pathname("$obsbase/$name");
      $e->set_size(-s $path);
      $e->set_perm(oct('0644'));
      $e->set_filetype('reg');
      $w->write_header($e);
      $w->write_data(\$path->slurp_raw);
   }, { recurse => 1 });
   
   $w->close();
}


count_data_label:
$source = shift @sources;
if (defined ($source)){
   goto count_data_return;
}
