package Dist::Maker::Scatter;
use Mouse;
use MouseX::StrictConstructor;

use Dist::Maker::Name;
use Dist::Maker::Util qw(mkpath);

use Text::Xslate   ();
use File::Spec     ();

extends 'Dist::Maker::Base';

has dist => (
    is       => 'ro',
    isa      => 'Dist::Maker::Name',
    coerce   => 1,
    required => 1,
);

has template => (
    is       => 'ro',
    isa      => 'Dist::Maker::Name',
    coerce   => 1,
    default  => sub {
        my($self) = @_;
        return $self->config->data->{template}{default};
    },
);

has cache_dir => (
    is       => 'ro',
    isa      => 'Maybe[Str]',
    default  => sub {
        my($self) = @_;
        return $self->config->path('cache');
    },
    required => 0,
);

has content_map => (
    is         => 'ro',
    isa        => 'HashRef[Str]',
    init_arg   => undef,
    lazy_build => 1,
);

has _vpath => (
    is       => 'ro',
    isa      => 'HashRef[Dist::Maker::Template]',
    init_arg => undef,
    default  => sub { {} },
);

has _xslate => (
    is         => 'ro',
    isa        => 'Object',
    init_arg   => undef,
    lazy_build => 1,
    handles    => {
        _render       => 'render',
        render_string => 'render_string',
    },
);

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

    return Text::Xslate->new(
        type      => 'text',
        suffix    => '',
        path      => [ $self->_vpath ],
        module    => $self->config_data->{template}{module},
        cache_dir => $self->cache_dir,
    );
}


sub _load_dist_template {
    my($self, $name) = @_;

    (my $module_name = $name) =~ s/-/::/g;
    my $template_class = $self->load_class("Template::$module_name",);

    my $vpath = $self->_vpath;

    my $mtime = 0; # returns max mtime
    foreach my $c($template_class->meta->linearized_isa) {
        next unless $c->does('Dist::Maker::Template');

        my $t       = $c->new();
        my $moniker = $t->moniker;

        $self->info("loading $c as $moniker.\n");

        $vpath->{$moniker} = $t->distribution;

        my $mt = $t->mtime;
        if($mt > $mtime) {
            $mtime = $mt;
        }
    }
    return $mtime;
}

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

    my $name = $self->template->name;

    $self->info("build distribution with $name\n");

    my $mtime = $self->_load_dist_template($name);
    local $^T = $mtime; # XXX: hack to fool Xslate

    my $config = $self->config;
    my $vars = $config->merge_data(
        $config->data,
        { dist => $self->dist, template => { name => $self->template->module } },
    );
    my $packed = $self->_render($name, $vars);
    return $self->_parse_packed($packed);
}

sub _parse_packed { # parse Data::Section::Simple like packed structure
    my($self, $packed) = @_;

    my @pairs = split /^ \@\@ [ \t]* ([^\n]+) [ \t]* $/xms, $packed;
    shift @pairs;

    my %map;
    while(my($name, $content) = splice @pairs, 0, 2) {
        $content =~ s/\A \n+   //xms;   # remove starting newlines
        $content =~ s/   \n+ \z/\n/xms; # compress ending newlines
        $map{$name} = $content if length($content); # skip empty files
    }
    return \%map;
}

#sub raw_content_map {
#    my($self) = @_;
#    my $name = $self->template->name;
#    $self->_load_dist_template($name);
#    return $self->_parse_packed( $self->_vpath->{$name} );
#}

sub scatter {
    my($self, $to, $map) = @_;
    $map //= $self->content_map();

    $self->info("scatter to $to/\n");
    foreach my $file(sort keys %{$map}) {
        my $fullpath      = File::Spec->catfile($to, $file);
        my($volume, $dir) = File::Spec->splitpath($fullpath);
        $dir              = File::Spec->catpath($volume, $dir, '');
        unless(-e $dir) {
            $self->mkpath($dir);
        }

        $self->info("$file\n");
        open my $out, '>:utf8 :raw', $fullpath ## no critic
            or die("Cannot open($fullpath) for writing: $!");
        print $out $map->{$file};
        close $out or die("Cannot close($fullpath) in writing: $!");
    }
    $self->info("scattered successfully.\n");
    return;
}


no Mouse;
__PACKAGE__->meta->make_immutable();
__END__

=head1 NAME

Dist::Maker::Scatter - Builds and scatters distributions

=cut
