#!perl
use strict;
use warnings;
use utf8;
use 5.008001;

use Getopt::Long ();
use File::Basename ();
use Module::CPANfile ();

use App::scan_prereqs_cpanfile qw(
    debugf find_perl_files scan_inner_packages scan scan_test_requires load_diff_src
);

my $version;
my $diff;
my $include_empty;
my $scan_test_requires;
my $dir = ".";
my $ignorefile;
my $ignore_regexp;
my @ignore = qw(eg examples share fatlib _build .git blib local .build);
my $add_ignore;
my $p = Getopt::Long::Parser->new(
    config => [qw(posix_default no_ignore_case auto_help)]
);
$p->getoptions(
    'version!'       => \$version,
    'diff=s'         => \$diff,
    'ignore=s'       => \$add_ignore,
    'ignore-file=s'    => \$ignorefile,
    'include-empty!' => \$include_empty,
    'scan-test-requires' => \$scan_test_requires,
    'dir=s'          => \$dir,
);
push @ignore, split /,/,$add_ignore if $add_ignore;

if ($ignorefile) {
    ! -r $ignorefile && die "Unable to read ignore-file '$ignorefile' !" ;
    open my $fh, '<', $ignorefile;
    chomp(my @lines = <$fh>);
    $ignore_regexp = join '|', @lines;
    close $fh;
}

if ($version) {
    printf "%s %s\n",
        File::Basename::basename($0),
        $App::scan_prereqs_cpanfile::VERSION;
    exit 0;
}


{
    my ($runtime_files, $test_files, $configure_files, $develop_files) = find_perl_files($dir, ignore => \@ignore, ignore_regexp => $ignore_regexp);
    debugf($develop_files);

    my @inner_packages = scan_inner_packages(@$test_files, @$runtime_files, @$configure_files, @$develop_files);
    my $meta_prereqs = $diff ? load_diff_src($diff) : +{};

    # runtime
    my $runtime_prereqs = scan($runtime_files, \@inner_packages, $meta_prereqs, [qw(runtime)], 'runtime', +{});

    # test
    my $test_prereqs = scan($test_files, \@inner_packages, $meta_prereqs, [qw(test runtime)], 'test', $runtime_prereqs);

    # configure
    my $configure_prereqs = scan($configure_files, \@inner_packages, $meta_prereqs, [qw(configure runtime)], 'configure', $runtime_prereqs);

    # develop
    my $develop_prereqs = scan($develop_files, \@inner_packages, $meta_prereqs, [qw(develop test runtime)], 'develop', +{ %{$runtime_prereqs||{}}, %{$test_prereqs||{}}});

    if ($scan_test_requires) {
        $develop_prereqs = scan_test_requires($dir, $develop_prereqs);
    }

    print Module::CPANfile->from_prereqs(
        {
            runtime => {
                requires => $runtime_prereqs,
            },
            configure => {
                requires => $configure_prereqs,
            },
            test => {
                requires => $test_prereqs,
            },
            develop => {
                requires => $develop_prereqs,
            },
        }
    )->to_string($include_empty);
}

__END__

=head1 NAME

scan-prereqs-cpanfile - Scan prerequisite modules and generate CPANfile

=head1 SYNOPSIS

    % scan-prereqs-cpanfile

        --diff=META.json      # Generate diff from META.json
        --diff=cpanfile       # Generate diff from cpanfile
        --ignore=extlib
        --dir=/foo/bar
        --scan-test-requires

=head1 DESCRIPTION

This script scans prerequisite modules from your code, and generate CPANfile.
You can also list missing prerequisite modules.

=head1 SCANNING RULES

=over 4

=item Used modules in `Build.PL` or `Makefile.PL` as 'test' requires

=item Used modules in `t/` as 'test' requires

=item Used modules in `xt/`, `benchmark/` and `author/` as 'develop' requires

=item Used modules in other directories as 'runtime' requires

=back

=head1 OPTIONS

=over 4

=item --diff

        --diff=META.json      # Generate diff from META.json
        --diff=cpanfile       # Generate diff from cpanfile

Compare the scanning result with META.json, META.yml or cpanfile.
With this option, scan-prereqs-cpanfile displays missing prerequisite modules only.

=item --ignore

    --ignore=tools,extlib

Ignore some directories.

=item --ignore-file

    --ignore-file=ignored.regex

Ignore all files and directories matching a regex pattern listed in this file.

=item --include-empty

By default, phases without any prereqs are not dumped, By giving this option, cpanfile will have an empty block such as:

    on test => sub {

    };

Defaults to false.

=item --scan-test-requires

Scan test files and include the modules specified by L<Test::Requires> as 'develop' requires.

=item --dir

    --dir=DIRECTORY

Scan for modules in DIRECTORY (instead of '.') 

=back

=head1 AUTHOR

Tokuhiro Matsuno

=head1 SEE ALSO

L<Module::CPANfile>, L<Perl::PrereqScanner::Lite>