#!/usr/bin/perl -w

use strict;
use warnings 'all';
use Getopt::Long;
use Term::ReadKey;
use File::Path 'make_path';
use DBI;
use Cwd 'cwd';


my (
  $appName,
  $domain,
  $dbName,
  $dbHost,
  $dbUser,
  $dbPass,
  $email,
);

my $cwd = cwd();

my $result = GetOptions(
  "app=s"     => \$appName, # Required
  "domain=s"  => \$domain,  # Required
  "email=s"   => \$email,   # Required
  "db=s"      => \$dbName,
  "user=s"    => \$dbUser,
  "host=s"    => \$dbHost,
);

$appName && $domain && $email or die "Usage: $0 --app=AppName --domain=domain.com --email=you\@your-email.com [--host=dbhost --db=dbname  --user=dbusername]\n";
$dbHost ||= "localhost";

if( $dbName && $dbUser )
{
  print STDERR "Enter your database password: ";
  ReadMode('noecho');
  chomp($dbPass = <STDIN>);
  ReadMode('restore');
  print "\n";
}# end if()

my @DSN = (
  "DBI:mysql:$dbName:$dbHost",
  $dbUser,
  $dbPass
);

my $drh = DBI->install_driver("mysql");
my $rc = $drh->func('createdb', $dbName, $dbHost, $dbUser, $dbPass, 'admin');

my $dbh = eval { DBI->connect( @DSN, {RaiseError => 1} ) };
if( $@ )
{
  (my $error = $@) =~ s/\sat\s\Q$0\E\s+line.*//;
  die "[ERROR]: $error\n";
}# end if()

eval {
  $dbh->do(q~
  DROP TABLE IF EXISTS asp_sessions
  ~);
  $dbh->do(q~
  CREATE TABLE asp_sessions (
    session_id   char(32) NOT NULL primary key,
    session_data blob,
    created_on   datetime default NULL,
    modified_on  datetime default NULL
  ) ENGINE=MyISAM DEFAULT CHARSET=latin1
  ~);
  $dbh->disconnect();
  1;
} or die $@;


# Write our configs:
make_path("$domain/conf");
unless( -f "$domain/conf/asp4-config.json" )
{
  warn "$domain/conf/asp4-config.json\n";
  open my $ofh, '>', "$domain/conf/asp4-config.json"
    or die "Cannot open '$domain/conf/asp4-config.json' for writing: $!";
  my $json = generic_config();
  $json =~ s/\%CWD\%/$cwd/igs;
  $json =~ s/\%domain\%/$domain/igs;
  $json =~ s/\%appName\%/$appName/igs;
  $json =~ s/\%dbName\%/$dbName/igs;
  $json =~ s/\%dbHost\%/$dbHost/igs;
  $json =~ s/\%dbUser\%/$dbUser/igs;
  $json =~ s/\%dbPass\%/$dbPass/igs;
  $json =~ s/\%email\%/$email/igs;
  print $ofh $json;
  close($ofh);
}# end unless()


unless( -f "$domain/conf/httpd.conf" )
{
  warn "$domain/conf/httpd.conf\n";
  open my $ofh, '>', "$domain/conf/httpd.conf"
    or die "Cannot open '$domain/conf/httpd.conf' for writing: $!";
  my $conf = generic_httpconf();
  $conf =~ s/\%CWD\%/$cwd/igs;
  $conf =~ s/\%domain\%/$domain/igs;
  $conf =~ s/\%appName\%/$appName/igs;
  $conf =~ s/\%dbName\%/$dbName/igs;
  $conf =~ s/\%dbHost\%/$dbHost/igs;
  $conf =~ s/\%dbUser\%/$dbUser/igs;
  $conf =~ s/\%dbPass\%/$dbPass/igs;
  $conf =~ s/\%email\%/$email/igs;
  print $ofh $conf;
  close($ofh);
}# end unless()


# Test page:
make_path("$domain/htdocs");
unless( -f "$domain/htdocs/index.asp" )
{
  warn "$domain/htdocs/index.asp\n";
  open my $ofh, '>', "$domain/htdocs/index.asp"
    or die "Cannot open '$domain/htdocs/index.asp' for writing: $!";
  print $ofh <<'ASP';
<html>
<body>
<h1>ASP4 Test Page</h1>
<p>
  The date and time is <%= scalar(localtime()) %>.
</p>
<p>
  You have visited this page <%= $Session->{count}++ %> time(s) recently.
</p>
</body>
</html>
ASP
  close($ofh);
}# end unless()


(my $hPath = lc($appName)) =~ s/::/\//g;
my $hClass = lc($appName);
make_path("$domain/handlers/$hPath");
unless( -f "$domain/handlers/$hClass/echo.pm" )
{
  open my $ofh, '>', "$domain/handlers/$hPath/echo.pm"
    or die "Cannot open '$domain/handlers/$hPath/echo.pm' for writing: $!";
  print $ofh <<"CODE";

package $hClass\::echo;

use strict;
use warnings 'all';
use base 'ASP4::FormHandler';
use vars __PACKAGE__->VARS;
use Data::Dumper;

sub run
{
  my (\$s, \$context) = \@_;
  
  \$Response->Write("<h1>Form Contents:</h1><pre>" . Dumper(\$Form) . "</pre>");
}# end run()

1;# return true:

CODE
  close($ofh);
}# end unless()

# Only write the base Model class if we have Class::DBI::Lite
my $CDBIL_Version = 0;
eval {
  require Class::DBI::Lite;
  $CDBIL_Version = $Class::DBI::Lite::VERSION = $Class::DBI::Lite::VERSION;
};
if( $dbName && $CDBIL_Version )
{
  make_path("$domain/lib/$appName");
  unless( -f "$domain/lib/$appName/Model.pm" )
  {
    warn "$domain/lib/$appName/Model.pm\n";
    open my $ofh, '>', "$domain/lib/$appName/Model.pm"
      or die "Cannot open '$domain/lib/$appName/Model.pm' for writing: $!";
    print $ofh <<"CODE";

@{[ 'package' ]} @{['']} $appName\::Model;

use strict;
use warnings 'all';
use base 'Class::DBI::Lite::mysql';
use ASP4::ConfigLoader;

my \$Config = ASP4::ConfigLoader->load();
my \$conn = \$Config->data_connections->main;
__PACKAGE__->connection(
  \$conn->dsn,
  \$conn->username,
  \$conn->password
);

1;# return true:

\=pod

\=head1 NAME

$appName\::Model - Base class for all $appName entity classes.

\=head1 SYNOPSIS

  # In your class:
  
  package $appName\::Thing;
  
  use strict;
  use warnings 'all';
  use base '$appName\::Model';
  
  __PACKAGE__->set_up_table('things');
  
  1;# return true:

\=head1 DESCRIPTION

This module was generated by $0 on @{[ scalar(localtime()) ]}.

B<***IT IS SAFE to make changes to this file, as it will not be overwritten.***>.

\=head1 SEE ALSO

L<Class::DBI::Lite>

\=cut

CODE
    close($ofh);
  }# end unless()
}# end if()


unless( -d "$domain/t" )
{
  warn "$domain/t/010-basic/010-compile.t\n";
  make_path("$domain/t/010-basic");
  open my $ofh, '>', "$domain/t/010-basic/010-compile.t"
    or die "Cannot open '$domain/t/010-basic/010-compile.t' for writing: $!";
  print $ofh <<"TEST";
#!/usr/bin/perl -w

use strict;
use warnings 'all';
use Test::More 'no_plan';
use ASP4::API;
my \$api; BEGIN { \$api = ASP4::API->new }


@{[ $dbName ? qq(use_ok('$appName\::Model');) : '' ]}

ok( my \$res = \$api->ua->get("/"), "Got '/'.");
ok( \$res->is_success, "GET / is successful.");
ok( \$res->content, "Got some content also.");

for( 0..10 )
{
  like \$res->content, qr(visited this page \$_ time), "Simple session counter: \$_ visits recorded.";
  \$res = \$api->ua->get("/");
}# end for()

\$res = \$api->ua->post("/handlers/$hClass.echo", {
  hello => "world"
});
like \$res->content, qr('hello'\\s+\\=\>\\s+'world'), "/handlers/$hClass.echo?hello=world works";


# More tests can go here or in other files.

TEST
  close($ofh);
}# end unless()

warn "="x60, "\n";
warn "    Running Initial Test Suite...\n";
warn "="x60, "\n";
chdir($domain);
`asp4 /` =~ m{You have visited this page}
  or die "Warning: ASP script contents (/index.asp) not what we expected!";
exec("prove -rv t");


sub generic_config {
  <<'EOF';
{
  "system": {
    "post_processors": [
    ],
    "libs": [
      "@ServerRoot@/lib"
    ],
    "load_modules": [
    ],
    "env_vars": {
    },
    "settings": {
    }
  },
  "errors": {
    "error_handler":    "ASP4::ErrorHandler",
    "mail_errors_to":   "%email%",
    "mail_errors_from": "root@localhost",
    "smtp_server":      "localhost"
  },
  "web": {
    "application_name": "%appName%",
    "application_root": "@ServerRoot@",
    "www_root":         "@ServerRoot@/htdocs",
    "handler_root":     "@ServerRoot@/handlers",
    "page_cache_root":  "/tmp/PAGE_CACHE",
    "handler_resolver": "ASP4::HandlerResolver",
    "handler_runner":   "ASP4::HandlerRunner",
    "filter_resolver":  "ASP4::FilterResolver",
    "request_filters": [
    ],
    "disable_persistence": [
    ]
  },
  "data_connections": {
    "session": {
      "manager":          "ASP4::SessionStateManager",
      "cookie_name":      "session-id",
      "cookie_domain":    "*",
      "session_timeout":  30,
      "dsn":              "DBI:mysql:%dbName%:%dbHost%",
      "username":         "%dbUser%",
      "password":         "%dbPass%"
    },
    "main": {
      "dsn":              "DBI:mysql:%dbName%:%dbHost%",
      "username":         "%dbUser%",
      "password":         "%dbPass%"
    }
  }
}
EOF
}


sub generic_httpconf {
  <<'EOF';

# Load up some important modules:
PerlModule DBI
PerlModule DBD::mysql
PerlModule ASP4::ModPerl

# Admin website:
<VirtualHost *:80>

  ServerName    %domain%
  DocumentRoot  %CWD%/%domain%/htdocs
  
  # Set the directory index:
  DirectoryIndex index.asp
  
  # All *.asp files are handled by ASP4::ModPerl
  <Files ~ (\.asp$)>
    SetHandler  perl-script
    PerlResponseHandler ASP4::ModPerl
  </Files>
  
  # !IMPORTANT! Prevent anyone from viewing your GlobalASA.pm
  <Files ~ (\.pm$)>
    Order allow,deny
    Deny from all
  </Files>
  
  # All requests to /handlers/* will be handled by their respective handler:
  <Location /handlers>
    SetHandler  perl-script
    PerlResponseHandler ASP4::ModPerl
  </Location>
  
</VirtualHost>

EOF
}


=pod

=head1 NAME

asphelper - Generate an ASP4 skeleton web application

=head1 USAGE

  asphelper --app=AppName --domain=domain.com --email=you\@your-email.com [--host=dbhost --db=dbname  --user=dbusername]

If you specify C<--dbname> and C<--dbuser> it will ask you for a database password - completely optional.

=head1 DESCRIPTION

The C<asphelper> program offers a way to get up-and-running quickly with a new ASP4 web application.

After successfully answering its questions, C<asphelper> will generate a skeleton web application
including config files, full directory structure and a simple unit test.

Use the resulting application as a starting-point for your own development.

B<NOTE:> If L<Class::DBI::Lite> is installed, a base Model class will be created based on
L<Class::DBI::Lite>.  See L<Class::DBI::Lite> for details on how to use it.

=cut

