#!/usr/bin/env perl
use strict;
use warnings;
use Carp;
use Getopt::Long;
use YAML::Syck;
use List::MoreUtils qw/uniq firstidx/;

my %args;

confess "unknown option"
  unless GetOptions( \%args, 'update-order', 'keep-requires=s',
    'keep-recommends=s', 'keep-build-requires=s', 'for-dists=s', 'help', );

my $USAGE = <<'END'
run: ./bin/shipwright-utility --update-order

options: 

help: print this usage

update-order: regenerate install order.
    sub options:
        keep-requires: keep dists with requires dep type. default is true.
        keep-recommends: keep dists with recommends dep type. default is true.
        keep-build-requires: keep dists with build-requires dep type. default is true.
        for-dists: make order only for these dists, seperated by comma.
        default is for all the dists in the source.

    e.g. --update-order --keep-recommends 0 --for-dists Jifty-DBI,Jifty

END
  ;

if ( $args{'help'} ) {
    print $USAGE;
    exit 0;
}
if ( $args{'update-order'} ) {
    for ( 'keep-requires', 'keep-recommends', 'keep-build-requires' ) {
        $args{$_} = 1 unless defined $args{$_};
    }

    my @dists = split /,\s*/, $args{'for-dists'} || '';
    unless (@dists) {
        my $out = `ls scripts`;
        my $sep = $/;
        @dists = split /$sep/, $out;
        chomp @dists;
        s{/$}{} for @dists;
    }

    my $require = {};

    for (@dists) {

        # bloody hack, cpan-Module-Build have recommends that will
        # cause circular deps
        if ( $_ eq 'cpan-Module-Build' ) {
            $require->{'cpan-Module-Build'} = [];
        }
        else {
            fill_deps( %args, require => $require, name => $_ );
        }
    }

    require Algorithm::Dependency::Ordered;
    require Algorithm::Dependency::Source::HoA;

    my $source = Algorithm::Dependency::Source::HoA->new($require);
    $source->load();
    my $dep = Algorithm::Dependency::Ordered->new( source => $source, )
      or confess $@;
    my $order = $dep->schedule_all();

    # fiddle the order a bit
    # put cpan-ExtUtils-MakeMaker and cpan-Module-Build to the head of
    # cpan dists.
    # also put cpan-Module-Build's recommends right after it,
    # since we omitted them in the $require->{'cpan-Module-Build'}

    for my $maker ( 'cpan-Module-Build', 'cpan-ExtUtils-MakeMaker' ) {
        if ( grep { $_ eq $maker } @$order ) {
            @$order = grep { $_ ne $maker } @$order;
            my $first_cpan_index = firstidx { /^cpan-/ } @$order;
            splice @$order, $first_cpan_index, 0, $maker;

            if ( $maker eq 'cpan-Module-Build' ) {

                # cpan-Regexp-Common is the dep of cpan-Pod-Readme
                my @maker_recommends = (
                    'cpan-Regexp-Common', 'cpan-Pod-Readme',
                    'cpan-version',       'cpan-ExtUtils-CBuilder',
                    'cpan-Archive-Tar',   'cpan-ExtUtils-ParseXS'
                );
                my %maker_recommends = map { $_ => 1 } @maker_recommends;
                @$order = grep { $maker_recommends{$_} ? 0 : 1 } @$order;
                splice @$order, $first_cpan_index + 1, 0, @maker_recommends;
            }
        }
    }

    DumpFile( 'shipwright/order.yml', $order );
}

sub fill_deps {
    my %args    = @_;
    my $require = $args{require};
    my $name    = $args{name};

    my $string;
    my $req = LoadFile("scripts/$name/require.yml");

    if ( $req->{requires} ) {
        for (qw/requires recommends build_requires/) {
            my $arg = "keep-$_";
            $arg =~ s/_/-/g;
            push @{ $require->{$name} }, keys %{ $req->{$_} }
              if $args{$arg};
        }
    }
    else {

        #for back compatbility
        push @{ $require->{$name} }, keys %$req;
    }

    @{ $require->{$name} } = uniq @{ $require->{$name} };

    for my $dep ( @{ $require->{$name} } ) {
        next if $require->{$dep};
        fill_deps( %args, name => $dep );
    }
}

