use strict;
use warnings;

package App::Embra;
$App::Embra::VERSION = '0.001'; # TRIAL
# ABSTRACT: build a site from parts

use Method::Signatures;
use Moo;


with 'App::Embra::Role::Logging';

around '_build_log_prefix' => func( $orig, $self ) {
    return "[Embra] ";
};

around '_build_logger' => func( $orig, $self ) {
    return Log::Any->get_logger;
};


has 'plugins' => (
    is => 'ro',
    default => sub{[]},
);


has 'files' => (
    is => 'ro',
    default => sub{[]},
);


method from_config_mvp_sequence( $class:, Config::MVP::Sequence :$sequence ) {
    my $payload = {};
    if ( my $root_section = $sequence->section_named( '_' ) ) {
        $payload = $root_section->payload;
    }
    my $creatura = $class->new( $payload );
    for my $plugin_section ( $sequence->sections ) {
        next if $plugin_section->name eq '_';
        $plugin_section->package->register_plugin(
            name => $plugin_section->name,
            args => $plugin_section->payload,
            embra => $creatura,
        );
    }
    $creatura;
}


method add_plugin( $plugin where { $_->DOES( "App::Embra::Role::Plugin" ) } ) {
    push @{ $self->plugins}, $plugin;
}


method find_plugin( $class ) {
    ( grep { ref $_ eq $class } @{ $self->plugins } )[0];
}


method collate {
    $self->debug( 'collating' );
    $_->gather_files    for $self->plugins_with( -FileGatherer );
    $_->prune_files     for $self->plugins_with( -FilePruner );
    $_->transform_files for $self->plugins_with( -FileTransformer );
    $_->assemble_files  for $self->plugins_with( -FileAssembler );
    $_->publish_site    for $self->plugins_with( -SitePublisher );
}


method plugins_with( $rolename ) {
  $rolename =~ s/^-/App::Embra::Role::/xms;
  return grep { $_->does( $rolename ) } @{ $self->plugins };
}


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::Embra - build a site from parts

=head1 VERSION

version 0.001

=head1 DESCRIPTION

App::Embra collates your content into a static website.

=head1 ATTRIBUTES

=head2 plugins

The objects which will help you build your site. An array reference of objects which implement L<App::Embra::Role::Plugin>.

=head2 files

Your site content. An array reference of L<App::Embra::File> instances. Plugins add, remove, read, and alter files via this attribute.

=head1 METHODS

=head2 from_config_mvp_sequence

    my $embra = App::Embra->from_config_mvp_sequence( $sequence );

Returns a new C<App::Embra> with its attributes & plugins taken from a L<Config::MVP::Sequence>. Called by the L<command-line base class|App::Embra::App::Command> whenever L<embra> is run.

=head2 add_plugin

    $embra->add_plugin( $plugin );

Adds a plugin to L<C</plugins>>. C<$plugin> must implement L<App::Embra::Role::Plugin>.

=head2 find_plugin

    my $plugin = $embra->find_plugin( $class );

Returns the first plugin in L<C</plugins>> with class C<$class>. Returns an emtpy list if no plugin with class C<$class> is present

=head2 collate

    $embra->collate;

Assembles your site. Plugins are called in this order:

=over 4

=item *

gather

=item *

prune

=item *

transform

=item *

assemble

=item *

publish

=back

For each of the types, all plugins which implement C<< App::Embra::Role::File<Type> >> have their C<< <type>_files> >> method called, in ths same order as they appear in L<C</plugins>>.

=head2 plugins_with

    say for $embra->plugins_with( $rolename );

Returns all elements of L<C</plugins>> which implement C<$rolename>. Role names should be fully specified; as a shorthand, you can pass C<<-<relative_role_name> >> and it will be treated as if you had specified C<< App::Embra::Role::<relative_role_name> >>.

=head1 SEE ALSO

=over 4

=item *

L<Blio>

=item *

L<jekyll|http://jekyllrb.com/>

=item *

L<Octopress|https://github.com/imathis/octopress#readme>

=back

=head1 AUTHOR

Daniel Holz <dgholz@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Daniel Holz.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
