use strict;
use warnings;

package Code::Statistics::Collector;
$Code::Statistics::Collector::VERSION = '1.190680';
# ABSTRACT: collects statistics and dumps them to json

use 5.006_003;

use Moose;
use MooseX::HasDefaults::RO;
use Code::Statistics::MooseTypes;
use MooseX::SlurpyConstructor 1.1;
use Code::Statistics::Metric;
use Code::Statistics::Target;

use File::Find::Rule::Perl;
use Code::Statistics::File;
use JSON 'to_json';
use File::Slurp 'write_file';
use Term::ProgressBar::Simple;
use File::Find::Rule;

has no_dump => ( isa => 'Bool' );

has dirs => (
    isa    => 'CS::InputList',
    coerce => 1,
    default => sub { ['.'] },
);

has files => (
    isa     => 'ArrayRef',
    lazy    => 1,
    default => sub {
        return $_[0]->_prepare_files;
    },
);

has targets => (
    isa     => 'CS::InputList',
    coerce  => 1,
    default => sub { $_[0]->_get_all_submodules_for('Target') },
);

has metrics => (
    isa     => 'CS::InputList',
    coerce  => 1,
    default => sub { $_[0]->_get_all_submodules_for('Metric') },
);

has progress_bar => (
    isa     => 'Term::ProgressBar::Simple',
    lazy    => 1,
    default => sub {
        my $params = { name => 'Files', ETA => 'linear', max_update_rate => '0.1' };
        $params->{count} = @{ $_[0]->files };
        return Term::ProgressBar::Simple->new( $params );
    },
);

has command_args => (
    is      => 'ro',
    slurpy  => 1,
    default => sub { {} },
);


sub collect {
    my ( $self ) = @_;

    $_->analyze for @{ $self->files };

    my $json = $self->_measurements_as_json;
    $self->_dump_file_measurements( $json );

    return $json;
}

sub _find_files {
    my ( $self ) = @_;
    my @files = (
        File::Find::Rule::Perl->perl_file->in( @{ $self->dirs } ),
        File::Find::Rule->file->name( '*.cgi' )->in( @{ $self->dirs } ),
    );
    @files = sort { lc $a cmp lc $b } @files;
    return @files;
}

sub _prepare_files {
    my ( $self ) = @_;
    my @files = $self->_find_files;
    @files = map $self->_prepare_file( $_ ), @files;
    return \@files;
}

sub _prepare_file {
    my ( $self, $path ) = @_;

    my %params = (
        path      => $path,
        original_path => $path,
        targets   => $self->targets,
        metrics   => $self->metrics,
        progress => sub { $self->progress_bar->increment },
    );

    return Code::Statistics::File->new( %params, %{$self->command_args} );
}

sub _dump_file_measurements {
    my ( $self, $text ) = @_;
    return if $self->no_dump;

    write_file( 'codestat.out', $text );

    return $self;
}

sub _measurements_as_json {
    my ( $self ) = @_;

    my @files = map $self->_strip_file( $_ ), @{ $self->files };
    my @ignored_files = $self->_find_ignored_files( @{ $self->files } );

    my $measurements = {
        files => \@files,
        targets => $self->targets,
        metrics => $self->metrics,
        ignored_files => \@ignored_files
    };

    my $json = to_json( $measurements, { pretty => 1, canonical => 1 } );

    return $json;
}

sub _find_ignored_files {
    my ( $self, @files ) = @_;

    my %present_files = map { $_->{original_path} => 1 } @files;

    my @all_files = File::Find::Rule->file->in( @{ $self->dirs } );
    @all_files = grep { !$present_files{$_} } @all_files;
    my $useless_stuff = qr{
        (^|/)
            (
                [.]git   |   [.]svn   |   cover_db   |   [.]build   |   nytprof   |
                blib
            )
        /
    }x;
    @all_files = grep { $_ !~ $useless_stuff } @all_files; # filter out files we most certainly do not care about

    return @all_files;
}


sub _strip_file {
    my ( $self, $file ) = @_;
    my %stripped_file = map { $_ => $file->{$_} } qw( path measurements );
    return \%stripped_file;
}

sub _get_all_submodules_for {
    my ( $self, $type ) = @_;
    my $class = "Code::Statistics::$type";
    my @list = sort $class->all;

    $_ =~ s/$class\::// for @list;

    my $all = join ';', @list;

    return $all;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Code::Statistics::Collector - collects statistics and dumps them to json

=head1 VERSION

version 1.190680

=head2 collect
    Locates files to collect statistics on, collects them and dumps them to
    JSON.

=head2 _strip_file
    Cuts down a file hash to have only the keys we actually want to dump to the
    json file.

=head1 AUTHOR

Christian Walde <mithaldu@yahoo.de>

=head1 COPYRIGHT AND LICENSE


Christian Walde has dedicated the work to the Commons by waiving all of his
or her rights to the work worldwide under copyright law and all related or
neighboring legal rights he or she had in the work, to the extent allowable by
law.

Works under CC0 do not require attribution. When citing the work, you should
not imply endorsement by the author.

=cut
