#!/usr/bin/perl

use strict;
use warnings;
use Dancer::Template::Simple;
use File::Basename 'basename', 'dirname';
use File::Spec::Functions;
use Getopt::Long;
use Dancer::Renderer;
use constant FILE => 1;

# options
my $help = 0;
my $name = undef;
my $path = '.';

sub templates($);
sub app_tree($);
sub create_node($;$);

GetOptions(
    "h|help"          => \$help,
    "a|application=s" => \$name,
    "p|path=s"        => \$path,
);

# main 
usage() and exit(0) if $help;
usage() if not defined $name;
usage() unless -d $path && -w $path;

my $DO_OVERWRITE_ALL = 0;
my $DANCER_APP_DIR  = catfile($path, $name);

safe_mkdir($DANCER_APP_DIR);
create_node( app_tree($name), $DANCER_APP_DIR );

# subs

sub usage {
    print <<'ENDUSAGE';
Dancer Helper - bootstrap a Dancer application

Usage:
    dancer [options] -a <appname>
    
    options are following :
    -h, --help            : display this help message
    -a, --application     : name of the application to create
    -p, --path            : existing path where to create the application tree
                            (current directory if not specified, must be writable)
                            
ENDUSAGE
    exit 0;
}

sub create_node($;$) {
    my ($node, $root) = @_;
    $root ||= '.';
    
    while ( my ($path, $content) = each %$node ) {
        $path = catfile($root, $path);

        if (ref($content) eq 'HASH') {
            safe_mkdir($path); 
            create_node($content, $path);
        } elsif (ref($content) eq 'CODE') {
            # The content is a coderef, which, given the path to the file it
            # should create, will do the appropriate thing:
            $content->($path);
       } else {
            my $file = basename($path);
            my $dir  = dirname($path);
            my $ex = ($file =~ s/^\+//); # look for '+' flag (executable)
            my $template = templates($name)->{$file};

            $path = catfile($dir, $file); # rebuild the path without the '+' flag
            write_file($path, $template, {appdir => File::Spec->rel2abs($DANCER_APP_DIR)});
            chmod 0755, $path if $ex;
        }
    }
}

sub app_tree($)  {
    my ($appname) = @_;

    return {
        "app.psgi"      => FILE,
        "+$appname.pl"  => FILE,
        "$appname.pm"   => FILE,
        "config.yml"    => FILE,
        "environments"  => {
            "development.yml" => FILE,
            "production.yml"  => FILE,
        },
        "views" => {
            "layouts" => {
                "main.tt" => FILE,
            },
            "index.tt" => FILE,
        },
        "public" => {
            "404.html" => FILE,
            "500.html" => FILE,
            "css" => { 
                "style.css" => FILE,
                "error.css" => FILE,
            },
            "images" => { },
            "favicon.ico" => \&write_favicon,
        },
    };
}

sub safe_mkdir {
    my ($dir) = @_;
    if (not -d $dir) {
        print "+ $dir\n";
        mkdir $dir;
    }
    else {
        print "  $dir\n";
    }
}

sub write_file {
    my ($path, $template, $vars) = @_;
    die "no template found for $path" unless defined $template;

    # if file already exists, ask for confirmation
    if (-f $path && (not $DO_OVERWRITE_ALL)) {
        print "! $path exists, overwrite? [N/y/a]: ";
        my $res = <STDIN>; chomp($res);
        $DO_OVERWRITE_ALL = 1 if $res eq 'a';
        return 0 unless ($res eq 'y') or ($res eq 'a');
    }

    my $fh;
    my $content = process_template($template, $vars);
    print "+ $path\n";
    open $fh, '>', $path or die "unable to open file `$path' for writing: $!";
    print $fh $content;
    close $fh;
}

sub process_template {
    my ($template, $tokens) = @_;
    my $engine = Dancer::Template::Simple->new;
    $engine->{start_tag} = '[%';
    $engine->{stop_tag} = '%]';
    return $engine->render(\$template, $tokens);
}

# Given a path, get the favicon.ico file content from the __DATA__ section and
# write it to that file.
sub write_favicon {
    my $path = shift;
    open(my $fh, '>', $path)
      or warn "Failed to write favicon to $path - $!" and return;
    my $data = do { local $/; <DATA> };
    print {$fh} unpack 'u*', $data;
    close $fh;
}

sub templates($) {
    my $appname = shift;
    return {

'app.psgi'  => 

"# PSGI application bootstraper for Dancer
use lib '[%appdir%]';
use $appname;

use Dancer::Config 'setting';
setting apphandler  => 'PSGI';
Dancer::Config->load;

my \$handler = sub {
    my \$env = shift;
    my \$request = Dancer::Request->new(\$env);
    Dancer->dance(\$request);
};
",
'index.tt'  => '<h2>It Works!</h2>

<p>
I\'m in <code>[%appdir%]/views/index.tt</code>
</p>
',

'main.tt'   => 
    Dancer::Renderer->html_page($appname, '<% content %>'),

"$appname.pl" => 

"#!/usr/bin/perl
use Dancer;
use $appname;
dance;
",

"$appname.pm" => 

"package $appname;
use Dancer;

get '/' => sub {
    template 'index';
};

true;
",

'style.css' => 

'body {
    font-family: Lucida,sans-serif;   
    color: #eee;
    background-color: #1f1b1a;
}

#content {
    color: #000;
    background-color: #eee;
    padding: 1em;
    margin: 1em;
    padding-top: 0.5em;
}

a { 
    color: #a5ec02;
}

h1 { 
    color: #a5ec02;
}

#footer {
    border-top: 1px solid #aba29c;
    margin-top: 2em;
    padding-top: 1em;
    font-size: 10px;
    color: #ddd;
}

pre {
    font-family: \"lucida console\",\"monaco\",\"andale mono\",\"bitstream vera sans mono\",\"consolas\",monospace;
}

',

"error.css" => 

"body {
    font-family: Lucida,sans-serif;
}

h1 {
    color: #AA0000;
    border-bottom: 1px solid #444;
}

h2 { color: #444; }

pre {
    font-family: \"lucida console\",\"monaco\",\"andale mono\",\"bitstream vera sans mono\",\"consolas\",monospace;
    font-size: 12px;
    border-left: 2px solid #777;
    padding-left: 1em;
}

#footer {
    font-size: 10px;
}

span.key { 
    color: #449;
    font-weight: bold;
    width: 120px;
    display: inline;
}

span.value {
    color: #494;
}

/* these are for the message boxes */

pre.content {
    background-color: #eee;
    color: #000;
    padding: 1em;
    margin: 0;
    border: 1px solid #aaa;
    border-top: 0;
    margin-bottom: 1em;
}

div.title {
    font-family: \"lucida console\",\"monaco\",\"andale mono\",\"bitstream vera sans mono\",\"consolas\",monospace;
    font-size: 12px;
    background-color: #aaa;
    color: #444;
    font-weight: bold;
    padding: 3px;
    padding-left: 10px;
}

pre.content span.nu {
    color: #889;
    margin-right: 10px;
}

pre.error {
    background: #334;
    color: #ccd;
    padding: 1em;
    border-top: 1px solid #000;
    border-left: 1px solid #000;
    border-right: 1px solid #eee;
    border-bottom: 1px solid #eee;
}

",

"404.html" => 
    Dancer::Renderer->html_page( 
        "Error 404", 
        '<h2>Page Not Found</h2><p>Sorry, this is the void.</p>',
        'error'),

"500.html" => 
    Dancer::Renderer->html_page( 
        "Error 500", 
        '<h2>Internal Server Error</h2>'
                 . '<p>Wooops, something went wrong</p>',
        'error'),

'config.yml' => 

'layout: "main"
logger: "file"

',

'development.yml' => 
'log: "debug"
warnings: 1
show_errors: 1
auto_reload: 1

',

'production.yml' => 
'log: "warning"
warnings: 0
show_errors: 0
auto_reload: 0

',
    };
}

=pod 

=head1 NAME

dancer - helper script to create new Dancer applications

=head1 DESCRIPTION

Helper script for providing a bootstrapping method to quickly and easily create
the framework for a new Dancer application.

=head1 USAGE

dancer [options] -a <appname>

=over

=item -h, --help            : print what you are currently reading
=item -a, --application     : the name of your application
=item -p, --path            : the path where to create your application
                              (current directory if not specified)

=back

=head1 EXAMPLE

Here is an application created with dancer:

    $ dancer -a mywebapp
    + mywebapp 
    + mywebapp/config.yml
    + mywebapp/views
    + mywebapp/views/layouts
    + mywebapp/views/layouts/main.tt
    + mywebapp/views/index.tt
    + mywebapp/environments
    + mywebapp/environments/production.yml
    + mywebapp/environments/development.yml
    + mywebapp/mywebapp.pm
    + mywebapp/mywebapp.pl
    + mywebapp/app.psgi
    + mywebapp/favicon.ico

The application is ready to serve:

    $ cd mywebapp
    $ ./mywebapp.pl
    >> Listening on 127.0.0.1:3000
    == Entering the development dance floor ...

=head1 AUTHOR

This script has been written by Sebastien Deseille
<sebastien.deseille@gmail.com> and Alexis Sukrieh 
<sukria@cpan.org>.

=head1 SOURCE CODE

See L<Dancer> for more information.

=head1 LICENSE

This module is free software and is published under the same
terms as Perl itself.

=cut

# The following ugly data is the default favicon.ico file to write; uuencoded
# using pack 'u*'.
__DATA__
M```!``$`$!````$`"`!H!0``%@```"@````0````(`````$`"```````````
M``````````````````````!D12\`:4LV`&I,-P!K3CD`;$XY`&Q/.@!M3SH`
M;5`Z`&U0.P!N4#L`;5`\`&U1.P!N43L`;E$\`&Y1/0!N4CT`;U(]`&]2/@!O
M4SX`<E9!`')60@!S6$0`=UQ(`'E>2@!Y8$P`>F!-`'QB3@!]8U``?611`(!G
M5`"`:%4`A&M:`(9M7`"+=&,`C79E`)!Z:0"2?6P`KIZ2`+"AE0"\L*8`P[>M
M`,S"NP#/Q[\`T\O$`-7-QP#P[^L`]_;U`/K[^0#]_OT`____````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-#0T-
M"`T-#0T-#0T-#0T-#0T2&@@-#0T-#0T-#0T-#0T!#QD(#0T-#0T-#0T("`<`
M)08-#0T-#0T(#0@-#1$F(04-"`T-#0T-#0,-#0T-%AL7"@P-#0T-#0<=$`T0
M(B0>%!`+"0T-#0T(%0@-!"`L+B\H#@@(#0T-"`T(#0T(!0@8,"LC'PT(#0T-
M#0T-#0T("`,G,2T'"`@-#0T-#0T-#0T-`API*@T-#0T-#0T-#0T-#0T)"`(3
M"`T-#0T-#0T-#0T-#0@-"`T-#0T-#0T-#0T-#0T-#0T-#0T-#0``````````
M````````````````````````````````````````````````````````````
+````````````````
