The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;
use utf8;

package Amon2::Setup::Flavor;
use Text::Xslate;
use File::Spec;
use File::Basename;
use File::Path ();
use Amon2;
use Plack::Util ();
use Carp ();
use Amon2::Trigger;

my $xslate = Text::Xslate->new(
    syntax => 'TTerse',
    type   => 'text',
    tag_start => '<%',
    tag_end   => '%>',
    'module'   => [ 'Text::Xslate::Bridge::Star' ],
);

sub infof {
    my $caller = do {
        my $x;
        for (1..10) {
            $x = caller($_);
            last if $x ne __PACKAGE__;
        }
        $x;
    };
    $caller =~ s/^Amon2::Setup:://;
    print "[$caller] ";
    @_==1 ? print(@_) : printf(@_);
    print "\n";
}

sub new {
    my $class = shift;
    my %args = @_ ==1 ? %{$_[0]} : @_;

    $args{amon2_version} = $Amon2::VERSION;

    for (qw/module/) {
        die "Missing mandatory parameter $_" unless exists $args{$_};
    }
    $args{module} =~ s!-!::!g;

    # $module = "Foo::Bar"
    # $dist   = "Foo-Bar"
    # $path   = "Foo/Bar"
    my @pkg  = split /::/, $args{module};
    $args{dist} = join "-", @pkg;
    $args{path} = join "/", @pkg;
    bless { %args }, $class;
}

sub run { die "This is abstract base method" }

sub mkpath {
    my ($self, $path) = @_;
    Carp::croak("path should not be ref") if ref $path;
    infof("mkpath: $path");
    File::Path::mkpath($path);
}

sub render_string {
    my $self = shift;
    my $template = shift;
    my %args = @_==1 ? %{$_[0]} : @_;
    return $xslate->render_string($template, {%$self, %args});
}

sub write_file {
    my ($self, $filename, $template) = (shift, shift, shift);
    Carp::croak("filename should not be reference") if ref $filename;

    $filename =~ s/<<([^>]+)>>/$self->{lc($1)} or die "$1 is not defined. But you want to use $1 in filename."/ge;

    my $content = $self->render_string($template, @_);
    $self->write_file_raw($filename, $content);
}

sub write_file_raw {
    my ($self, $filename, $content) = @_;
    Carp::croak("filename should not be reference") if ref $filename;

    infof("writing $filename");

    my $dirname = dirname($filename);
    File::Path::mkpath($dirname) if $dirname;

    open my $ofh, '>:encoding(utf-8)', $filename or die "Cannot open file: $filename: $!";
    print {$ofh} $content;
    close $ofh;
}

sub load_asset {
    my ($self, $asset) = @_;
    infof("Loading asset: $asset");
    my $klass = Plack::Util::load_class($asset, 'Amon2::Setup::Asset');

    my $require_newline = $self->{tags} ? 1 : 0;
    $self->{tags} .= $klass->tags;
    $self->{tags} .= "\n" if $require_newline;

    # $klass->run($self);
}

sub write_asset {
    my ($self, $asset, $base) = @_;
    $asset || die "Missing asset name";
    $base ||= 'static/';

    my $klass = Plack::Util::load_class($asset, 'Amon2::Setup::Asset');
    my $files = $klass->files;
    while (my ($fname, $content) = each %$files) {
        $self->write_file_raw("$base/$fname", $content);
    }
}

1;
__END__

=head1 NAME

Amon2::Setup::Flavor - Abstract base class for flavors.

=head1 DESCRIPTION

This is an abstract base class for flavors. But you don't need to inherit this class. Amon2 uses duck typing. You should implement only C<< Class->run >> method.

In Amon2, flavor means setup script.

=head1 METHODS

This class provides some useful methods to write setup script.

=over 4

=item $flavor->init()

Hook point to initialize module directory.

=item $flavor->mkpath($dir)

same as C<< `mkdir -p $dir` >>.

=item $flavor->write_file($fnametmpl, $template)

C<< $fnametmpl >> will be replace with the parameters.

Generate file using L<Text::Xslate>.

For more details, read the source Luke! Or please write docs...

=item $flavor->write_file_raw($fname, $content)

Write C<< $content >> to the C<< $fname >>.

=back