#!/usr/bin/perl

=head1 DESCRIPTION

=head1 EXAMPLES

Переделывает структуру каталога пакета, чтобы он собирался debosh-ем

! перед началом работы очищает рабочую копию от изменений svn-reset'ом
! всегда внимательно проверять различия полученных пакетов

prepare-for-debosh --dir ./direct-utils/package-name


можно сделать изменения локально (например, поправить недостаточно хорошо сгенерированный meta.yaml) и проверить разницу снова с помощью параметра check
prepare-for-debosh --dir ./direct-utils/package-name --check

=cut

use strict;
use warnings;

use utf8;
use open ':std' => ':utf8';

use File::Temp qw/tempdir/;
use Getopt::Long;
use YAML;
use Data::Dumper;

my @dpkg_actions = qw/preinst postinst prerm postrm/;

my $readme_text = "пакет собирается утилитой debosh (пакет: yandex-du-debosh, исходники здесь: svn+ssh://svn.yandex.ru/direct-utils/debosh)";

run() unless caller();

sub run
{
    my $opt = parse_options();

    my $dir = $opt->{dir};
    
    if ($opt->{check}) {
        my $tempdir = tempdir("prepare_for_debosh_XXXXXXXX", CLEANUP => 1, DIR => "/tmp");

        $tempdir .= "/tmp_name";
        mkdir $tempdir;

        `svn info $dir` =~ m!^URL: (.+)$!im;
        my $svn_url = $1;

        `svn co $svn_url $tempdir`;
        
        build_package($tempdir, $opt->{make});
        my $package = get_packagename("$tempdir/debian");
        compare_debs("$tempdir/..", $dir, $package);
        return; 
    }
   
    build_package($dir, $opt->{make});

    clear_dir($dir);

    my $package = get_packagename("$dir/debian");

    my $control_file = read_file("$dir/debian/$package/DEBIAN/control");

    write_file("$dir/meta.yaml", generate_meta($control_file));

    my $changes_file = read_file("$dir/debian/changelog");
    write_file("$dir/changes", generate_changes($changes_file));

    write_readme($dir);

    get_actions("$dir/debian/$package/DEBIAN", "$dir/actions");

    get_sources("$dir/debian/$package", $dir);

    post_actions("$dir/debian", $package);
}


sub write_readme
{
    my $dir = shift;

    write_file("$dir/README", $readme_text);
}


sub post_actions
{
    my ($dir, $package) = @_;

    `rm -r $dir/../usr/share/doc`;
    `rm -r $dir/../usr/share` unless list_dir("$dir/../usr/share");

    compare_debs("$dir/../..", "$dir/..", $package);

    my $deb_files = join " ", map {"$dir/../../$_"} grep {-f "$dir/../../$_" && $_ =~ m!^$package!i} list_dir("$dir/../../");
    `rm -r $deb_files`;

    `svn rm --force $dir`;
    `svn st $dir/.. | grep '^!' | awk '{print \$2}' | xargs -r svn rm --force`;
    `svn st $dir/.. | grep '^?' | awk '{print \$2}' | xargs -r svn add --force`;
}


sub compare_debs
{
    my ($dir_old, $dir_new, $package) = @_;
    
    `cd $dir_new && debosh --dirty >> /dev/null 2>&1`;
    my $output = `compare_debs.pl $dir_old $dir_new/debian -p $package`;

    $output =~ m!^tmp dir: (.+)$!gm;
    print "\n################ compare_debs.pl $dir_old $dir_new/debian -p $package\n\n$output";

    $output = `tree $1/{1,2}`;
    print "\n!!! Проверьте, что пакеты достаточно совпадают !!!\n\n$output\n";

}


sub clear_dir
{
    my $dir = shift;

    my $to_rm = join " ", map {"$dir/$_"} grep { $_ ne "debian" } list_dir($dir);

    #`svn rm --force $to_rm`;
    `rm -r $to_rm`;
}

sub get_sources
{
    my ($dir_from, $dir_to) = @_;
    
    my @dirs = list_dir($dir_from);
    @dirs = map {"$dir_from/$_"} grep {-d "$dir_from/$_" && $_ ne "DEBIAN"} @dirs;

    my $dirs_str = join " ", @dirs;

    `cp -r $dirs_str $dir_to`;
}


sub list_dir
{
    my $dir = shift;

    opendir(my $dh, $dir);
    my @files = grep {$_ =~ m!^[^\.]!} readdir($dh);
    closedir($dh);

    return @files;
}


sub get_actions
{
    my ($dir_from, $dir_to) = @_;

    for my $action (@dpkg_actions) {
        if (-f "$dir_from/$action") {
            `install -d $dir_to` unless -d $dir_to;
            `cp $dir_from/$action $dir_to`;
        }
    }
}


sub read_file
{
    my $filename = shift;
    open(my $fh, '<', $filename) or die "Unable to open file $filename, $!";
    my @lines = <$fh>;
    close($fh);
    return join("", @lines);
}


sub write_file
{
    my ($filename, $content) = @_;
    open(my $fh, '>', $filename) or die "Unable to open file $filename, $!";
    print $fh $content;
    close($fh);
    return;
}


sub get_packagename
{
    my $dir = shift;
    my $pre_control_file = read_file("$dir/control");

    die "can't get package name" unless $pre_control_file =~ m!^Package: (.+?)\s*$!im;
    return $1;
}


sub build_package
{
    my $dir = shift;
    my $make = shift;

    `svn-reset $dir && svn-reset $dir && svn up $dir`;

    eval {
        if ($make) {
            system("{ cd $make; make svn_checkout; make; } > /dev/null 2>&1");
        } else {
            system("cd $dir && dpkg-buildpackage -rfakeroot >> /dev/null 2>&1");
        }
    };
    
    return;
}


sub generate_meta
{
    my $control_file = shift;

    my $result = "---\n";

    if ($control_file =~ m!^Package: (.+)$!im) {
        $result .= "package: $1\n";
    }

    if ($control_file =~ m!^Description: (.+)$!im) {
        my $description = $1;
        $result .= "description: $description\n";
    }

    if ($control_file =~ m!^Depends: (.+?)(?=\Z|^\w)!ims) {
        my $requires = join "\n", map {my $tmp = $_; $tmp =~ s/^\s+//; $tmp =~ s/\s+$//; "   $tmp:" } split ",", $1;
        $result .= "requires:\n$requires\n";
    }

    if ($control_file =~ m!^Conflicts: (.+?)(?=\Z|^\w)!ims) {
        my $requires = join "\n", map {my $tmp = $_; $tmp =~ s/^\s+//; $tmp =~ s/\s+$//; "   $tmp:" } split ",", $1;
        $result .= "conflicts:\n$requires\n";
    }

    return $result;
}

sub generate_changes
{
    my $changes_file = shift;

    my $result = "";

    my @changes = $changes_file =~ m!^\w[^ ]+ \((.+?)\).+?(?=\*)(.+?)(?=^$)!ismg;

    for (my $i = 1; $i <= $#changes; $i += 2) {
        $changes[$i] = join("\n", map {my $tmp = $_; $tmp =~ s/^\s+//; "  $tmp"} split("\n", $changes[$i]));

        $result .= "$changes[$i-1]\n$changes[$i]\n\n";
    }

    return $result;
}


sub parse_options
{
    my %O;
    GetOptions(
        "help" => sub {
            system("podselect -section NAME -section DESCRIPTION -section OPTIONS -section EXAMPLES $0 | pod2text-utf8");
            exit 0;
        },
        "dir=s" => \$O{dir},
        "check" => \$O{check},
        "make=s" => \$O{make},
    ) || die "can't parse options, stop";

    die "укажите путь до debian каталога" unless $O{dir};

    return \%O;
}

