#!/usr/bin/perl -w

#use strict;
use App::Rad;
use Cwd 'getcwd';

BEGIN {
  eval "use SweetPea 2.33 ();";
  if ($@) {
    die print <<END;
  To use the this builder please install SweetPea (web application framework)
  which is being reported as missing or broken. Please install via CPAN using
  one of the following commands.
  
    cpan SweetPea
    -or-
    perl -MCPAN -e 'CPAN::install(SweetPea)'
    -or-
    perl -MCPANPLUS -e 'CPAN::install(SweetPea)'
END
  }
}

my $usage = {
  'make' => q/
make - build application structure with boiler-plate code
    
Usage:
    sweetpea make [--argument, ]
    e.g. sweetpea make --script
    
Argument(s):
    script - makes a sweetpea script without the application structure
/,
  'model' => q/
model - create a model with boiler-plate code
    
Usage:
    sweetpea model [--argument, ]
    e.g. sweetpea model --name=user\/profile
    creates User\/Profile.pm
    
Argument(s):
    name - defines the name and placement of the Model class
/,
  'view' => q/
view - create a view with boiler-plate code
    
Usage:
    sweetpea view [--argument, ]
    e.g. sweetpea view --name=email\/plain
    creates Email\/Plain.pm
    
Argument(s):
    name - defines the name and placement of the View class
/,
  'ctrl' => q/
ctrl - create a controller with boiler-plate code
    
Usage:
    sweetpea ctrl [--argument, ]
    e.g. sweetpea ctrl --name=catalog\/products
    creates Catalog\/Products.pm
    
Argument(s):
    name - defines the name and placement of the Controller class
/,
};

# boiler-plate code ---------------------------------------------------------

my $module = 'package'; # prevents cpan from catalogging embedded code
my $head1  = '=head1';
my $cut    = '=cut';
my $bpcode = {};
   $bpcode->{'htaccess'} = <<'EOF';
DirectoryIndex .pl
AddHandler cgi-script .pl .pm .cgi
Options +ExecCGI +FollowSymLinks -Indexes

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule (.*) .pl/$1 [L]
EOF
   $bpcode->{'router'} = <<EOF;
#!$^X -w

BEGIN {
    use FindBin;
    use lib \$FindBin::Bin . '/sweet';
    use lib \$FindBin::Bin . '/sweet/application';
}

use SweetPea $SweetPea::VERSION;

# run application

my  \$s = sweet;
    
    exit \$s->run if \$s->request_method;
    
    # test from the cli
    \$s->routes({
      
      # write to console  
      '/root/_shutdown'   => sub {
        shift->output('debug', 'cli');
      }
        
    })->test(\$ARGV[0]);    
EOF
   $bpcode->{'script'} = <<EOF;
#!$^X -w

BEGIN {
    use FindBin;
    use lib \$FindBin::Bin . '/sweet';
    use lib \$FindBin::Bin . '/sweet/application';
}

use SweetPea $SweetPea::VERSION;

# weightless application

my  \$s = sweet;
    \$s->routes({
        
        '/:url' => sub {
            my \$s = shift;
            
            \$s->html("<h1>SweetPea is alive and well</h1>");
            \$s->html("You passed an inline url param: " . \$s->param('url') )
            if \$s->param('url');
        }
        
    });
    
    exit \$s->run if \$s->request_method;
    
    # test from the cli
    \$s->routes({
      
      # write to console  
      '/root/_shutdown'   => sub {
        shift->output('debug', 'cli');
      }
        
    })->test(\$ARGV[0]);    
EOF
   $bpcode->{'tester'} = <<EOF;
#!$^X -w

BEGIN {
    use FindBin;
    use lib \$FindBin::Bin . '/sweet';
    use lib \$FindBin::Bin . '/sweet/application';
}

use SweetPea $SweetPea::VERSION;

# application debugger & testing unit
# experimental !! handles get, not sure if it handles post, etc
# usage: perl .t /route param=value
# try: perl .t /test id=1

sweet->routes({
  
  # write to console  
  '/root/_shutdown'   => sub {
    my \$s = shift;
    \$s->output('debug', 'cli');
  }
    
})->test(\$ARGV[0]);
EOF
   $bpcode->{'root'} = <<EOF;
$module Controller::Root;

$head1 NAME

Controller::Root - Root Controller / Landing Page (Should Exist).

$cut

sub _startup {
    my ( \$self, \$s ) = \@_;
}

sub _begin {
    my ( \$self, \$s ) = \@_;
}

sub _index {
    my ( \$self, \$s ) = \@_;
    \$s->redirect('/sweet/welcome');
}

sub _end {
    my ( \$self, \$s ) = \@_;
}

sub _shutdown {
    my ( \$self, \$s ) = \@_;
}

1;
EOF
   $bpcode->{'sweet'} = <<EOF;
$module Controller::Sweet;

$head1 NAME

Controller::Sweet - SweetPea Introduction and Welcome Page.

$cut

$head1 DESCRIPTION

This function displays a simple information page the application defaults
to before development. This module should be removed before development.

$cut
EOF
   $bpcode->{'sweet'} .= <<'EOF';
# actions

sub welcome {
    my ( $self, $s ) = @_;
    
    # body
    my $url  = $s->url->{root};
    my $ver  = $SweetPea::VERSION;
    my $pl   = $s->path('/.pl');
    
    $s->html(qq(
    <h1>Welcome Young GrassHopper, SweetPea v$ver is working.&nbsp;
    <a href="$url"><span style="float:right;color:white">Start Over</span></a>
    </h1>
    <div id="container">
    <div class="section">
    <h2>Getting Started!</h2>
    <span class="text">First, you should examine the default structure of the
    application (if the application was generated using "sweetpea make") which
    should look similar to the following:</span>
    
    <pre>
    <span class="legend">figure 1.a</span>
    /static                 ## static content (html, css) is stored here
    /sweet                  ## application files are stored here
        /application        ## MVC files are stored here
            /Controller     ## controllers are stored here
                Root.pm     ## default controller (should always exist)
                Sweet.pm    ## new application welcome page controller
            /Model          ## models are stored here
                Schema.pm   ## new application boiler-plate model 
            /View           ## views are stored here
                Main.pm     ## new application boiler-plate view
        /sessions           ## auto-generated session files are stored here
        /templates          ## templates and layouts can be stored here
        App.pm              ## module for loading plugins (other modules)
    /.htaccess              ## enables pretty-urls on apache w/mod-rewrite
    /.pl                    ## default dispatcher (controller/action router)
    </pre>
    
    <span class="text">Second, you might want to familiarize yourself with the
    dispatcher $pl, which should look similar to the following:</span><br/>
    
    <pre>
    <span class="legend">figure 1.b</span>
    <a name="line1"> 1</a> #!/usr/bin/perl -w
    <a name="line2"> 2</a> 
    <a name="line3"> 3</a> BEGIN {
    
    <a name="line4"> 4</a>     use FindBin;
    <a name="line5"> 5</a>     use lib \$FindBin::Bin . '/sweet';
    <a name="line6"> 6</a>     use lib \$FindBin::Bin . '/sweet/application';
    
    <a name="line7"> 7</a> }
    <a name="line8"> 8</a> 
    <a name="line9"> 9</a> use SweetPea 2.32;
    <a name="line10">10</a> 
    <a name="line11">11</a> # run application
    
    <a name="line12">12</a> sweet-&gt;run;
    </pre>

    <span class="text">All the magic happens around line 12 ( "the sweet method
    instantiates a new SweetPea object and the run method runs the application"
    ). This easy to read and understand coding style is consistant throughout
    the SweetPea API. As you would probably imagine, if you wanted to alter how
    the applications is run, this would likely happen between the sweet and
    run method calls. One method that you will likely implement between the
    sweet and run method calls is the routes method which is responsible for
    mapping urls to actions (sub routines) manually.</span><br/>
    
    <pre>
    <span class="legend">figure 1.c</span>
    <a name="line1"> 1</a> #!/usr/bin/perl -w
    <a name="line2"> 2</a> 
    <a name="line3"> 3</a> BEGIN {
    
    <a name="line4"> 4</a>     use FindBin;
    <a name="line5"> 5</a>     use lib \$FindBin::Bin . '/sweet';
    <a name="line6"> 6</a>     use lib \$FindBin::Bin . '/sweet/application';
    
    <a name="line7"> 7</a> }
    <a name="line8"> 8</a> 
    <a name="line9"> 9</a> use SweetPea 2.32;
    <a name="line10">10</a> 
    <a name="line11">11</a> # manual routing engaged :\)
    
    <a name="line12">12</a> sweet-&gt;routes({
    <a name="line13">13</a>     
    <a name="line14">14</a>     '/:url' =&gt; sub {
    
    <a name="line15">15</a>         my \$s = shift;
    <a name="line16">16</a>         \$s-&gt;html(&quot;&lt;h1&gt;SweetPea is alive and well&lt;/h1&gt;&quot;);    
    <a name="line17">17</a>         \$s-&gt;html(&quot;You passed an inline url param: &quot; . \$s-&gt;param('url') )
    <a name="line18">18</a>         if \$s-&gt;param('url');
    <a name="line19">19</a>     }
    <a name="line20">20</a>     
    
    <a name="line21">21</a> })-&gt;run;
    </pre>
    
    <h2>Now go forth and create....</h2>
    
    <code class="text">
    
    ... and if you need help use the following resources.<br/>
    
    get the latest source code on
    <a href="http://github.com/awnstudio/SweetPea/"
    target="_blank">github</a>
    <br/>
    
    read POD (documentation), check install reports, open tickets and more on
    <a href="http://search.cpan.org/dist/SweetPea" target="_blank">CPAN</a>
    <br/>
    
    </code>
    
    <br/>));
}

# auto-routines

sub _begin {
    my ( $self, $s ) = @_;
    $s->html(qq(
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//
        EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>SweetPea is Alive and Well - Let's get started!</title>
    <style>
    body{color:#818F08;font-family:Bitstream Vera Sans,Trebuchet MS,
    Verdana,Tahoma,Arial,helvetica,sans-serif}h1{background-color:
    #818F08;color:#FFF;display:block;font-size:.85em;font-weight:400;
    left:0;margin:0;padding:10px 10px 10px 15px;position:absolute;
    right:0;top:0}h2{background-color:#EFEEEE;font-size:1em;
    padding:5px}#container{font-size:.8em;font-weight:400;left:0;
    padding:10px 10px 10px 15px;position:absolute;right:0;top:40px}
    .issue{color:red}.highlight{background-color:#EFEEEE;padding:1px}
    a {color:#818F08} a:hover {color:black} pre {font-size:1.25em;
    border: 1px solid #EFEEEE; padding:5px; background-color:#FAFAFA; }
    pre a { padding-right: 15px; border-right: 1px solid #ccc; } .legend
    { float:right; background-color:#CCC; padding:5px; color:white;
    position:absolute; right:0px; } .text { color:black; font-size:1.2em; }
    pre { color:green }
    </style>
    </head>
    <body>));
}

sub _end {
    my ( $self, $s ) = @_;
    $s->html(qq(
    </body>
    </html>));
}
1;
EOF
   $bpcode->{'model'} = <<EOF;
$module Model::Schema;
use strict;
use warnings;

1;
EOF
   $bpcode->{'view'} = <<EOF;
$module View::Main;
use strict;
use warnings;

1;
EOF
   $bpcode->{'app'} = <<EOF;
$module App;

use warnings;
use strict;

$head1 NAME

App - Loads modules and provides accessors to SweetPea.

$cut

sub plugins {
    my \$s = pop \@_;

    # load modules using the following procedure, they will be available to
    # the application as \$s->nameofobject.

    # Note! CGI (cgi), CGI::Cookie (cookie), and CGI::Session (session) 
    # plugins/modules are pre-loaded and available. 

    # e.g. \$s->plug( 'nameofobject', sub { shift; return Module::Name->new(\@_) } );

    return \$s;
}

1;    # End of App
EOF


# builders ------------------------------------------------------------------

sub makeapp {
    my $path          = getcwd();
    my $app_structure = {};
    my $output        = '';

    #htaccess file
    $app_structure = {
      "$path/.htaccess"                             => $bpcode->{htaccess},
      "$path/.pl"                                   => $bpcode->{router},
      "$path/sweet/application/Controller/Root.pm"  => $bpcode->{root},
      "$path/sweet/application/Controller/Sweet.pm" => $bpcode->{sweet},
      "$path/sweet/application/Model/Schema.pm"     => $bpcode->{model},
      "$path/sweet/application/View/Main.pm"        => $bpcode->{view},
      "$path/sweet/App.pm"                          => $bpcode->{app},
      "$path/sweet/sessions"                        => "",
      "$path/sweet/templates"                       => "",
      "$path/static"                                => ""
      };

    # make application structure
    foreach my $fod ( keys %{$app_structure} ) {
        unless ( -e $fod ) {
            if ( $fod =~ /\.\w{1,}$/ ) {
                $fod =~ s/^$path//;
                my @folders = split /\//, $fod;
                if (@folders) {
                    my $file  = pop @folders;
                    my $fpath = $path;
                    foreach (@folders) {
                        $fpath .= "$_/";
                        mkdir( $fpath, 0755 ) if $fpath =~ /sweet/;
                        mkdir( $fpath, 0755 ) if $fpath !~ /sweet/;
                    }
                    if ( $fod =~ /sweet/ ) {
                        open IN, ">$path$fod" || die print "Can't create $file, $!";
                        print IN $app_structure->{"$path$fod"};
                        close IN;
                        my $mode = '0755';
                        chmod $mode, "$path$fod";
                        $output .= "Created file $fod (chmod 755) ...\n";
                    }
                    else {
                        open IN, ">$path$fod" || die print "Can't create $file, $!";
                        print IN $app_structure->{"$path$fod"};
                        close IN;
                        chmod 0755, "$path$fod";
                        $output .= "Created file $fod (chmod 755) ...\n";
                    }
                }
                else {
                    $fod =~ s/^$path//;
                    open IN, ">$fod" || die print "Can't create $fod, $!";
                    print IN $app_structure->{$fod};
                    close IN;
                    chmod 0755, "$fod";
                    $output .= "Created file $fod (chmod 755) ...\n";
                }
            }
            else {
                $fod =~ s/^$path//;
                my @folders = split /\//, $fod;
                if (@folders) {
                    my $fpath = $path;
                    foreach (@folders) {
                        $fpath .= "$_/";
                        mkdir( $fpath, 0755 );
                    }
                }
                else {
                    mkdir( $path, 0755 );
                }
            }
        }
    }

    # secure sessions, and templates folders
    chmod 0755, "$path/sweet/sessions";
    chmod 0755, "$path/sweet/templates";
    
    $output .= <<EOF;

Attention *nix users, you may need to fix permissions for generated files.
If needed run: sudo chmod -R 0755
EOF
    
    return $output;
}

sub makemodl {
    my $data       = shift;
    my $controller = shift;
    my $output     = shift;
    my $root_path  = getcwd . "/sweet/application/Model/";
    my ( $module_name, $module_path ) = ();
    $controller =~ s/\.pm$//;
    $controller =~ s/[a-zA-Z0-9]\://g;
    if ($controller) {

        if ( $controller =~ /[\:\\\/\-]/ ) {
            my @folders = split /[\:\\\/\-]/, $controller;
            @folders = map( ucfirst, @folders );
            $module_name = "Model::" . join( "::", @folders );
            $controller = pop(@folders) . ".pm";

            # make folders
            my $tpath = $root_path;
            foreach my $path (@folders) {
                unless ( -e "$tpath$path" ) {
                    mkdir "$tpath$path";
                    chmod 0755, "$tpath$path";
                }
                $tpath = "$tpath$path/";
            }
            $module_path = $root_path . join( "/", @folders ) . "/$controller";
        }
        else {
            $module_name = "Model::" . ucfirst $controller;
            $module_path = $root_path . ucfirst($controller) . ".pm";
        }

        # create controller
        if ( not -e $module_path ) {
            open FILE,
              ">$module_path" || die print "Error creating $controller, $!";
            print FILE "package $module_name;\n";
            print FILE "\n";
            print FILE "=head1 NAME\n";
            print FILE "\n";
            print FILE "$module_name - Model Description.\n";
            print FILE "\n";
            print FILE "=cut\n";
            print FILE "\n";
            print FILE "$data\n" if $data;
            print FILE "\n";
            print FILE "1;";
            close FILE;
            chmod 0755, $module_path;
            $output .= "Created $controller (chmod 755) ...\n";
        }
        else {
          $output .= "Aborted, $controller already exists!\n";
        }
    }
    else {
        $output .= "Failed making model $controller.\n";
        exit;
    }
}

sub makectrl {
    my $data       = shift;
    my $controller = shift;
    my $output     = shift;
    my $root_path  = getcwd . "/sweet/application/Controller/";
    my ( $module_name, $module_path ) = ();
    $controller =~ s/\.pm$//;
    $controller =~ s/[a-zA-Z0-9]\://g;
    if ($controller) {

        if ( $controller =~ /[\:\\\/\-]/ ) {
            my @folders = split /[\:\\\/\-]/, $controller;
            @folders = map( ucfirst, @folders );
            $module_name = "Controller::" . join( "::", @folders );
            $controller = pop(@folders) . ".pm";

            # make folders
            my $tpath = $root_path;
            foreach my $path (@folders) {
                unless ( -e "$tpath$path" ) {
                    mkdir "$tpath$path";
                    chmod 0755, "$tpath$path";
                }
                $tpath = "$tpath$path/";
            }
            $module_path = $root_path . join( "/", @folders ) . "/$controller";
        }
        else {
            $module_name = "Controller::" . ucfirst $controller;
            $module_path = $root_path . ucfirst($controller) . ".pm";
        }

        # create controller
        if ( not -e $module_path ) {
            open FILE,
              ">$module_path" || die print "Error creating $controller, $!";
            print FILE "package $module_name;\n";
            print FILE "\n";
            print FILE "=head1 NAME\n";
            print FILE "\n";
            print FILE "$module_name - Controller Description.\n";
            print FILE "\n";
            print FILE "=cut\n";
            print FILE "\n";
            print FILE "sub _begin {\n";
            print FILE "    my ( \$self, \$s ) = \@_;\n";
            print FILE "}\n";
            print FILE "\n";
            print FILE "sub _index {\n";
            print FILE "    my ( \$self, \$s ) = \@_;\n";
            print FILE "}\n";
            print FILE "\n";
            print FILE "sub _end {\n";
            print FILE "    my ( \$self, \$s ) = \@_;\n";
            print FILE "}\n";
            print FILE "\n";
            print FILE "$data\n" if $data;
            print FILE "\n";
            print FILE "1;";
            close FILE;
            chmod 0755, $module_path;
            $output .= "Created $controller (chmod 755) ...\n";
        }
        else {
            $output .= "Aborted, $controller already exists!\n";
        }
    }
    else {
        $output .= "Failed making controller $controller.\n";
        exit;
    }
}

sub makeview {
    my $data       = shift;
    my $controller = shift;
    my $output     = shift;
    my $root_path  = getcwd . "/sweet/application/View/";
    my ( $module_name, $module_path ) = ();
    $controller =~ s/\.pm$//;
    $controller =~ s/[a-zA-Z0-9]\://g;
    if ($controller) {

        if ( $controller =~ /[\:\\\/\-]/ ) {
            my @folders = split /[\:\\\/\-]/, $controller;
            @folders = map( ucfirst, @folders );
            $module_name = "View::" . join( "::", @folders );
            $controller = pop(@folders) . ".pm";

            # make folders
            my $tpath = $root_path;
            foreach my $path (@folders) {
                unless ( -e "$tpath$path" ) {
                    mkdir "$tpath$path";
                    chmod 0755, "$tpath$path";
                }
                $tpath = "$tpath$path/";
            }
            $module_path = $root_path . join( "/", @folders ) . "/$controller";
        }
        else {
            $module_name = "View::" . ucfirst $controller;
            $module_path = $root_path . ucfirst($controller) . ".pm";
        }

        # create controller
        if ( not -e $module_path ) {
            open FILE,
              ">$module_path" || die print "Error creating $controller, $!";
            print FILE "package $module_name;\n";
            print FILE "\n";
            print FILE "=head1 NAME\n";
            print FILE "\n";
            print FILE "$module_name - View Description.\n";
            print FILE "\n";
            print FILE "=cut\n";
            print FILE "\n";
            print FILE "$data\n" if $data;
            print FILE "\n";
            print FILE "1;";
            close FILE;
            chmod 0755, $module_path;
            $output .= "Created $controller (chmod 755) ...\n";
        }
        else {
            $output .= "Aborted, $controller already exists!\n";
        }
    }
    else {
        $output .= "Failed making view $controller.\n";
        exit;
    }
}

sub makefile {
    my $data       = shift;
    my $controller = shift;
    my $output     = '';
    my $root_path  = getcwd . "/";
    my ( $module_name, $module_path ) = ();
    if ($controller) {
        if ( $controller =~ /[\\\/\-]/ ) {
            my @folders = split /[\\\/\-]/, $controller;
            $controller = pop(@folders);

            # make folders
            my $tpath = $root_path;
            foreach my $path (@folders) {
                unless ( -e "$tpath$path" ) {
                    mkdir "$tpath$path";
                    chmod 0755, "$tpath$path";
                }
                $tpath = "$tpath$path/";
            }
            $module_path = $root_path . join( "/", @folders ) . "/$controller";
        }
        else {
            $module_path = $root_path . $controller;
        }

        # create controller
        if ( not -e $module_path ) {
            open FILE,
              ">$module_path" || die print "Error creating $controller, $!";
            print FILE "$data\n" if $data;
            close FILE;
            chmod 0755, $module_path;
            $output .= "Created file /$controller (chmod 755) ...\n";
        }
        else {
            $output .= "Aborted, $controller already exists!\n";
        }
    }
    else {
        $output .= "Failed making $controller.\n";
    }
    return $output;
}

# cli -----------------------------------------------------------------------

sub make {
  my $c = shift;
  if ($c->options->{script}) {
    return 
    makefile($bpcode->{script}, '.pl')          .
    makefile($bpcode->{htaccess}, '.htaccess')  . <<EOF;

Attention *nix users, you may need to fix permissions for generated files.
If needed run: sudo chmod -R 0755
EOF
  }
  else {
    return makeapp();
  }
}

sub model {
  my $c = shift;
  if ($c->options->{name}) {
    return makemodl("", $c->options->{name})
  }
  else {
    return $usage->{model};
  }
}

sub view {
  my $c = shift;
  if ($c->options->{name}) {
    return makeview("", $c->options->{name})
  }
  else {
    return $usage->{view};
  }
}

sub ctrl {
  my $c = shift;
  if ($c->options->{name}) {
    return makectrl("", $c->options->{name})
  }
  else {
    return $usage->{ctrl};
  }
}

sub help {
  my $c = shift;
  my $u =
  q/
Usage: sweetpea command [arguments]

Available Commands:
    ctrl        create a boiler-plate SweetPea controller
    help        show syntax and available commands
    make        build application structure with boiler-plate code
    model       create a boiler-plate SweetPea model
    view        create a boiler-plate SweetPea view
    
* Get more help, use sweetpea help --cmd=command, e.g. sweetpea help --cmd=make
/;
  
  if ($c->options->{cmd} && $usage->{$c->options->{cmd}}) {
    return $usage->{$c->options->{cmd}};
  }
  else {
    return $u;
  }
}

App::Rad->run;

__END__

=head1 NAME

SweetPea - Rapid Application Development Utility

=head1 SYNOPSIS

sweetpea [options] [params]

 Options:
   see sweetpea help

=head1 DESCRIPTION

B<sweetpea.pl> is used to generate the initial application structure and any
additional Models, Views, and/or Controllers you may need.

* For POD documentation on the SweetPea module, try perldoc SweetPea.pm

=cut