=head1 NAME

App::Aphra - A simple static sitebuilder in Perl.

=head1 SYNOPSIS

    use App::Aphra;

    @ARGV = qw[build];

    my $app = App::Aphra->new;
    $app->run;

=head1 DESCRIPTION

For now, you probably want to look at the command-line program L<aphra>
which does all you want and is far better documented.

I'll improve this documentation in the future.

=cut

package App::Aphra;

use 5.014;

use Moose;
use Template;
use Template::Provider::Pandoc;
use FindBin '$Bin';
use File::Find;
use File::Basename;
use Getopt::Long;
use Carp;
use Clone 'clone';

use App::Aphra::File;

our $VERSION = '0.0.6';

has commands => (
  isa => 'HashRef',
  is => 'ro',
  default => sub { {
    build => \&build,
    serve => \&serve,
  } },
);

has config_defaults => (
  isa => 'HashRef',
  is  => 'ro',
  lazy_build => 1,
);

sub _build_config_defaults {
  return {
    source     => 'in',
    fragments  => 'fragments',
    layouts    => 'layouts',
    wrapper    => 'page',
    target     => 'docs',
    extensions => {
      tt => 'template',
      md => 'markdown',
    },
    output     => 'html',
  };
}

has config => (
  isa => 'HashRef',
  is  => 'ro',
  lazy_build => 1,
);

sub _build_config {
  my $self = shift;

  my %opts;
  GetOptions(\%opts,
             'source=s', 'fragments=s', 'layouts=s', 'wrapper=s',
             'target=s', 'extensions=s%', 'output=s',
             'version', 'help');

  for (qw[version help]) {
    $self->$_ and exit if $opts{$_};
  }

  my %defaults = %{ $self->config_defaults };

  my %config;
  for (keys %defaults) {
    $config{$_} = $opts{$_} // $defaults{$_};
  }
  return \%config;
}

has include_path => (
  isa => 'ArrayRef',
  is  => 'ro',
  lazy_build => 1,
);

sub _build_include_path {
  my $self = shift;

  my $include_path;
  foreach (qw[source fragments layouts]) {
    push @$include_path, $self->config->{$_}
      if exists $self->config->{$_};
  }

  return $include_path;
}

has template => (
  isa => 'Template',
  is  => 'ro',
  lazy_build => 1,
);

sub _build_template {
  my $self = shift;

  my $exts = clone $self->config->{extensions};
  delete $exts->{tt};

  return Template->new(
    LOAD_TEMPLATES => [
      Template::Provider::Pandoc->new(
        INCLUDE_PATH  => $self->include_path,
        EXTENSIONS    => $exts,
        OUTPUT_FORMAT => $self->config->{output},
      ),
    ],
    INCLUDE_PATH => $self->include_path,
    OUTPUT_PATH  => $self->config->{target},
    WRAPPER      => $self->config->{wrapper},
  );
}

sub run {
  my $self = shift;

  $self->config;

  @ARGV or die "Must give a command\n";

  my $cmd = shift @ARGV;

  if (my $method = $self->commands->{$cmd}) {
    $self->$method;
  } else {
    die "$cmd is not a valid command\n";
  }
}

sub build {
  my $self = shift;

  my $src = $self->config->{source};

  -e $src or die "Cannot find $src\n";
  -d $src or die "$src is not a directory\n";

  find({ wanted => $self->_make_do_this, no_chdir => 1 },
       $self->config->{source});
}

sub _make_do_this {
  my $self = shift;

  return sub {
    return unless -f;

    my $f = App::Aphra::File->new({
      app => $self, filename => $_,
    });

    $f->process;
  };
}

sub serve {
  my $self = shift;

  require App::HTTPThis;
  if ($@) {
    croak "App::HTTPThis must be installed for 'serve' command";
  }

  local @ARGV = $self->config->{target};
  App::HTTPThis->new->run;
}

sub version {
  my $me = basename $0;
  say "\n$me version: $VERSION\n";
}

sub help {
  my $self = shift;
  my $me = basename $0;
  $self->version;

  say <<ENDOFHELP;
$me is a simple static sitebuilder which uses the Template Toolkit to
process input templates and turn them into a web site.
ENDOFHELP
}

__PACKAGE__->meta->make_immutable;

1;

=head1 AUTHOR

Dave Cross <dave@perlhacks.com>

=head1 COPYRIGHT AND LICENCE

Copyright (c) 2017, Magnum Solutions Ltd. All Rights Reserved.

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

=cut