#!/usr/bin/perl

use warnings;
use strict;
use Getopt::Long;
use Cwd;

use lib 'lib';
use BuildHelper;

BEGIN {
    for my $val (qw/PERL5LIB PERL_MB_OPT PERL_LOCAL_LIB_ROOT PERL_MM_OPT/) {
        undef $ENV{$val} if defined $ENV{$val};
    }
};

####################################
our($minimal_perl_version, @packages, $additional_requirements, $build_requirements, $skip_missing);
do("./build.config") or die($@);

####################################
my $verbose = 0;
my $list_mode;
GetOptions(
    'v|verbose' => \$verbose,
    'l|list'    => \$list_mode,
);

####################################
# get module dependencies
my($deps,$files) = BuildHelper::get_all_deps(1);
my $orphaned     = BuildHelper::get_orphaned($deps, $files, $verbose);

####################################
if($list_mode) {
    my $cwd = cwd();
    chdir("src") or die("cannot change to src dir");
    my @tarballs = glob("*.tgz *.tar.gz *.zip");
    chdir($cwd) or die("cannot change dir back");
    my $tarballs = {};
    my $dups = {};
    for my $t (@tarballs) { $tarballs->{$t} = $t; }
    for my $p (@packages) {
        next unless -e $p;
        printf("* %s\n", $p);
        my $meta     = BuildHelper::get_meta($p);
        my $rem_deps = BuildHelper::get_deps_from_meta($meta, "runtime");
        for my $dep (sort keys %{$rem_deps}) {
            _print_deps(2, $dep, $dups, $tarballs);
        }
    }
    for my $dep (sort keys %{$additional_requirements}) {
        _print_deps(2, $dep, $dups, $tarballs);
    }
    for my $dep (sort keys %{$build_requirements}) {
        _print_deps(2, $dep, $dups, $tarballs);
    }
    for my $t (sort keys %{$tarballs}) {
        print "WARNING: ",$t, " is not used anywhere and can be removed.\n";
    }
    exit;
}

####################################
# remove referenced modules
for my $p (@packages) {
    next unless -e $p;
    my $meta     = BuildHelper::get_meta($p);
    my $rem_deps = BuildHelper::get_deps_from_meta($meta, "runtime");
    for my $rem_dep (keys %{$rem_deps}) {
        my $rdep = BuildHelper::module_to_file($rem_dep, $files, $rem_deps->{$rem_dep});
        my $cv   = BuildHelper::is_core_module($rem_dep, $minimal_perl_version);
        if(!$rdep && !BuildHelper::version_compare($cv, $rem_deps->{$rem_dep}) && !defined $skip_missing->{$rem_dep}) {
            print "WARNING: ",$rem_dep, " does not resolve to a file\n";
        } else {
            delete $orphaned->{$rdep} if $rdep;
        }
    }
}

####################################
for my $f (keys %{$files}) {
    my($m,$v) = BuildHelper::file_to_module($files->{$f});
    next if defined $additional_requirements->{$m};
    next if defined $build_requirements->{$m};
    my $cv    = BuildHelper::is_core_module($m, $minimal_perl_version);
    if($cv and BuildHelper::version_compare($cv, $v)) {
        print "WARNING: ",$m, " is a core module (".$cv.") and ",$files->{$m}," should be removed\n";
    }
}

# check other modules
for my $m (keys %{$additional_requirements}) {
    my $mdep = BuildHelper::module_to_file($m, $files, $additional_requirements->{$m});
    if(!defined $mdep) {
        print "WARNING: ",$m, " does not resolve to a file\n";
    } else {
        delete $orphaned->{$mdep};
    }
}
for my $m (keys %{$build_requirements}) {
    my $mdep = BuildHelper::module_to_file($m, $files, $build_requirements->{$m});
    if(!defined $mdep) {
        print "WARNING: ",$m, " does not resolve to a file\n";
    } else {
        delete $orphaned->{$mdep};
    }
}

####################################
# print result
for my $file (keys %{$orphaned}) {
    my($m,$v) = BuildHelper::file_to_module($file);
    if(!defined $additional_requirements->{$m} && !defined $build_requirements->{$m}) {
        print $file, " is orphaned and could probably removed\n";
    }
}

####################################
sub _print_deps {
    my($lvl, $mod, $dups, $tarballs) = @_;

    my $file  = BuildHelper::module_to_file($mod, $files);
    delete $tarballs->{$file} if $file;
    my $cv    = BuildHelper::is_core_module($mod, $minimal_perl_version);
    if(BuildHelper::version_compare($cv, $deps->{$mod})) {
        if($lvl <= 2) {
            printf("%-12s %-30s %s\n", "*"x$lvl, $mod, "core module");
        }
        return;
    }
    if($dups->{$mod}) {
        printf("%-12s %-30s %s\n", "*"x$lvl, $mod, "duplicate");
        return;
    }
    $dups->{$mod} = 1;
    printf("%-12s %-30s %s\n", "*"x$lvl, $mod, $file//"");
    return unless $file;
    my $cwd = cwd();
    chdir("src") or die("cannot change to src dir");
    my $meta  = BuildHelper::get_meta($file);
    chdir($cwd) or die("cannot change dir back");
    my $deps  = BuildHelper::get_deps_from_meta($meta, "runtime");
    for my $dep (sort keys %{$deps}) {
        next if $mod eq $dep;
        _print_deps($lvl+1, $dep, $dups, $tarballs);
    }
}
