#!/usr/bin/perl

use 5.006;
use strict;
use warnings;

use Cwd;
use File::Path;
use Config::General;
use Getopt::Long qw/:config bundling require_order pass_through/;

my $rcfile;
foreach ("$ENV{HOME}/.archdeb.conf", "/etc/archdeb.conf")
  {
    if (-f $_)
      {
        $rcfile = $_;
        last;
      }
  }

sub system_log
  {
    my @args = @_;
    if ((scalar @args) > 1)
      {
	print ((join ' ', @args) . "\n");
	return system @args;
      }
    else
      {
	my $cmd = $args[0];
	print "$cmd" . "\n";
	return system("$cmd");
      }
  }

my @rcdata = defined $rcfile ? (-ConfigFile => $rcfile) : (-String => '');

my $config = new Config::General (@rcdata,
                                  -AllowMultiOptions => "no",
                                  -LowerCaseNames => "yes",
                                  -UseApacheInclude => "yes",
                                  -IncludeRelative => "yes",
                                  -MergeDuplicateBlocks => "yes",
                                  -AutoTrue => "yes",
                                  -InterPolateVars => "yes",
                                 );

my %config = $config->getall();

my $tla = $config{commands}{tla} || "tla";
my $dpkg_buildpackage = $config{commands}{'dpkg-buildpackage'} || "dpkg-buildpackage -i'\\+\\+pristine-trees|\\+\\+saved.*|,,.*'";
my $skip_upstream_config;
my $skip_debian_config;
my $skip_tarball;
my $pre_build_hook;
my $source_tree;
GetOptions('arch=s' => \$tla,
           'dpkg-buildpackage=s' => \$dpkg_buildpackage,
           'skip-upstream-config!' => \$skip_upstream_config,
           'skip-debian-config!', => \$skip_debian_config,
           'skip-tarball!', => \$skip_tarball,
           'source-tree-name=s' => \$source_tree,
           'pre-build-hook=s', => \$pre_build_hook,
          );

my @dpkg_buildpackage_args;
my @other_args;
foreach my $arg (@ARGV)
  {
    if ($arg =~ /^-/)
      {
        push @dpkg_buildpackage_args, $arg;
      }
    else
      {
        push @other_args, $arg;
      }
  }

my $package = shift @other_args or die "No package given";
my $version = shift @other_args or die "No version given";

die "Unrecognised arguments: @other_args" if scalar @other_args;

sub get_version
  {
    my $config = shift;
    $config =~ m,^\Q$package-\E(.*)$, or die "BUG: Couldn't parse config name '$config'";
    return "$1";
  }

sub debian_version_cmp
  {
    my $a = shift;
    my $b = shift;
    my @a = ($a =~ /(\d+|[^\d]+)/g);
    my @b = ($b =~ /(\d+|[^\d]+)/g);
    return @a <=> @b if @a <=> @b;
    while (@a and @b)
      {
        $a = shift @a;
        $b = shift @b;
        next if $a eq $b;
        if ($a =~ /^\d+$/ and $b =~ /^\d+$/)
          {
            return $a <=> $b if $a <=> $b;
          }
        else
          {
            return $a cmp $b if $a cmp $b;
          }
      }
    # Equal
    return 0;
  }

sub config_version_cmp
  {
    my $a = get_version(shift);
    my $b = get_version(shift);
    return debian_version_cmp($a, $b);
  }

if (not $skip_debian_config)
  {
    if ($version !~ /-/ and not -e "configs/$package/debian/$package-$version")
      {
        # Looks like we're supposed to guess the Debian version
        opendir DIR, "configs/$package/debian/" or die "Failed to opendir configs/$package/debian/: $!";
        my @candidates = readdir DIR;
        closedir DIR;
        @candidates = grep {m,^$package-$version\E,} @candidates;
        die "Couldn't find any configs for $package-$version" unless scalar @candidates;
        # Sort by debian revision
        @candidates = sort {config_version_cmp($a, $b)} @candidates;
        my $target = $candidates[-1];
        $version = get_version($target);
      }
  }

my $upstreamversion = $version;
$upstreamversion =~ s/-[^-]*$// or undef $upstreamversion;
$upstreamversion =~ s/^[^:]+:// if defined $upstreamversion;

die "configs/$package/debian/$package-$version not found"
  unless $skip_debian_config or -e "configs/$package/debian/$package-$version";

$skip_upstream_config = $skip_tarball = 1 unless defined $upstreamversion;

# If both were defined, don't bother checking and warning
unless (defined $skip_upstream_config and defined $skip_tarball)
  {
    if (-e "$package/${package}_$upstreamversion.orig.tar.gz")
      {
        print "Original upstream tarball already exists, skipping generation\n";
        # Otherwise skip the stuff we don't need
        $skip_upstream_config = 1 unless defined $skip_upstream_config;
        $skip_tarball = 1 unless defined $skip_tarball;
      }
  }

die "configs/$package/upstream/$package-$upstreamversion not found"
  if not $skip_upstream_config and defined $upstreamversion and not -e "configs/$package/upstream/$package-$upstreamversion";

mkpath("$package/upstream");

unless ($skip_upstream_config)
  {
    my $tree = "$package/upstream/$package-$upstreamversion";

    # Prep upstream version
    if (-d $tree)
      {
        print "Updating $tree to version $upstreamversion\n";
        system_log("$tla cfgcat configs/$package/upstream/$package-$upstreamversion | xargs -r -n2 tla update -d ")
          == 0 or die "update failed";
      }
    else
      {
        print "Building $tree from version $upstreamversion\n";
        system_log($tla, "buildcfg", "--no-pristines", "configs/$package/upstream/$package-$upstreamversion")
          == 0 or die "buildcfg failed";
      }
  }

# Generate original tarball

unless ($skip_tarball)
  {
    my $olddir = cwd;
    chdir "$package/upstream" or die "Failed to chdir to $package/upstream";
    print "cd $package/upstream\n";
    system_log("tar",
	       "--gzip", "--create",
	       "--exclude", "++pristine-trees",
	       "--exclude", "++saved-*",
               "--exclude", ",,*",
	       "--file", "../${package}_$upstreamversion.orig.tar.gz",
	       $source_tree || "$package-$upstreamversion") == 0
		 or die "tar failed: $!";
    chdir $olddir or die "Failed to chdir to $olddir";
    print "cd $olddir\n";
  }

my $tree = $upstreamversion ? "$package/$package-$upstreamversion"
                            : "$package/$package";

unless ($skip_debian_config)
  {
    # Prep upstream version
    if (-d $tree)
      {
        print "Updating $tree to version $version\n";
        system_log("$tla cfgcat configs/$package/debian/$package-$version | xargs -r -n2 tla update -d ")
          == 0 or die "update failed";
      }
    else
      {
        print "Building $tree from version $version\n";
        system_log($tla, "buildcfg", "--no-pristines", "configs/$package/debian/$package-$version")
          == 0 or die "buildcfg failed";
      }
  }

$tree = $source_tree ? "$package/$source_tree" : $tree;

# Fiddle for package-framework
if (-f "$tree/=RELEASE-ID")
  {
    open RELID, ">", "$tree/=RELEASE-ID" or die "Couldn't open $tree/=RELEASE-ID for writing\n";
    print RELID "# automatically generated release id (by arch-buildpackage)\n";
    print RELID "\n";
    print RELID "${package}-${version}\n";
    close RELID;
  }

if (defined $pre_build_hook)
  {
    system_log("cd $tree && $pre_build_hook");
  }

# Run pre-build target
# Error return is ignored (should we just ignore missing target
# instead? doogie says it can't be done)
system_log("cd $tree && debian/rules prebuild");

# Build package
exit system_log(join(' ', "cd $tree && $dpkg_buildpackage", @dpkg_buildpackage_args));
