#!/usr/bin/env perl

use strict;
use File::Path;
use File::Basename;
use File::Spec;
use Carp;
use File::Copy;

use constant spl_file       => "$ENV{HOME}/sparrow/sparrow.list";
use constant sparrow_root   => "$ENV{HOME}/sparrow";
our $EDITOR = $ENV{'EDITOR'};

unless (@ARGV) {

    usage();
    exit;
}



my $object = shift @ARGV;

# initialization part 

init_sparrow_env();

if ($object eq  'plg'){

    my $action = shift @ARGV;

    if ( $action eq 'list') {

        my $options = join ' ', @ARGV;

        if ($options =~ /--local/){
            show_local_plugins();
        } else {
            show_plugins();
        }

    } elsif ( $action eq 'install'){

        install_plugin( shift @ARGV );        

    }elsif( $action  eq 'update'){

        update_plugin( shift @ARGV );        
    }
}

if ($object eq  'project'){

    my $name = shift @ARGV;

    my $action = shift @ARGV;

    if ( $action eq 'create') {

        create_project($name);

    } elsif ( $action eq 'add_site' ){

        my $sid = shift @ARGV;
        my $base_url = shift @ARGV;

        add_site_to_project($name, $sid, $base_url);

    }elsif( $action  eq 'add_plg'){

        add_plugin_to_project($name,shift @ARGV);

    }elsif( $action  eq 'info'){

        project_info($name);

    }elsif( $action  eq 'check_site'){

        my $sid = shift @ARGV;
        my $pid = shift @ARGV;

        check_site($name,$sid,$pid);

    }elsif( $action  eq 'swat_setup'){

        my $sid = shift @ARGV;

        swat_setup($name,$sid);

    }else{ # default action

        project_info($name);

    }

}

if ( $object eq 'projects' ){

    show_projects();

}


## functions ###

sub read_plugin_list {

    my @list;
    my %list;

    my $mode = shift || 'as_array';

    open F, spl_file or confess $!;

    while ( my $i = <F> ){
        chomp $i;
        next unless $i=~/\S+/;
        my @foo = split /\s+/, $i;
        push @list, { name => $foo[0], url => $foo[1] } ;
        $list{$foo[0]} = { name => $foo[0], url => $foo[1] };
    }
    close F;

    my $retval;

    if ($mode eq 'as_hash'){
        $retval = \%list;
    }else{
        $retval = \@list;
    }

    return $retval;

}

sub show_plugins {

    my $list = read_plugin_list();

    print "available swat plugins:\n\n";

    for my $p (@{$list}){
        print "$p->{name} | $p->{url}\n";
    }
}

sub show_projects {

    print "sparrow project list:\n\n";

    my $root_dir = sparrow_root.'/projects/';

    opendir(my $dh, $root_dir) || croak "can't opendir $root_dir: $!";

    for my $p (sort { -M $root_dir.$a <=> -M $root_dir.$b } grep { ! /^\.{1,2}$/ } readdir($dh)){
        print basename($p),"\n";
    }

    closedir $dh;
}


sub show_local_plugins {

    print "locally installed swat plugins:\n\n";

    my $root_dir = sparrow_root.'/plugins';

    opendir(my $dh, $root_dir) || croak "can't opendir $root_dir: $!";

    for my $p (grep { ! /^\.{1,2}$/ } readdir($dh)){
        print basename($p),"\n";
    }

    closedir $dh;

}

sub install_plugin {

    my $pid = shift;

    my $list = read_plugin_list('as_hash');


    if ($list->{$pid}){
        if (-d sparrow_root."/plugins/$pid"){
            print "plugin $pid already exist!\n";
            print "you should remove plugins/$pid manualy and run `sparrow install $pid` to reinstall it\n";
            exit(1) 
        }
        print "installing plugin $pid ...\n";
        execute_shell_command("git clone $list->{$pid}->{url} ".sparrow_root."/plugins/$pid && carton");
    }else{
        confess "can't find plugin $pid at sparrow list file\n";
    }
        
}

sub update_plugin {

    my $pid = shift;

    my $list = read_plugin_list('as_hash');

    if ($list->{$pid}){
        print "updating plugin $pid ...\n";
        execute_shell_command("cd ".(sparrow_root)."/plugins/$pid && git pull && carton");
    }else{
        confess "can't find plugin $pid at sparrow list file\n";
    }
        
}

sub create_project {

    my $project = shift;

    if ( -d sparrow_root."/projects/$project" ){
        print "project $project already exists - nothing to do here ... \n\n"
    } else {
        mkpath sparrow_root."/projects/$project";
        mkpath sparrow_root."/projects/$project/plugins";
        mkpath sparrow_root."/projects/$project/sites";
        print "project $project is successfully created\n\n"
    }


}

sub project_info {

    my $project = shift;

    print "project $project info:\n\n";

    print "plugins:\n\n";

    my $root_dir = sparrow_root."/projects/$project/plugins";

    opendir(my $dh, $root_dir) || croak "can't opendir $root_dir: $!";

    for my $p (grep { ! /^\.{1,2}$/ } readdir($dh)){
        print "\t", basename($p),"\n";
    }

    closedir $dh;


    print "\n\n\sites:\n\n";

    my $root_dir = sparrow_root."/projects/$project/sites";

    opendir(my $dh, $root_dir) || croak "can't opendir $root_dir: $!";

    for my $s (grep { ! /^\.{1,2}$/ } readdir($dh)){
        my $base_url = site_base_url($project,basename($s));
        print "\t", basename($s)," [$base_url] \n";
    }

    closedir $dh;


}

sub add_plugin_to_project {

    my $project = shift or croak "usage: add_plugin_to_project(project,plugin)";
    my $pid = shift or croak "usage: add_plugin_to_project(project,plugin)";


    unless ( -d sparrow_root."/plugins/$pid" ){
        print "plugin $pid is not installed yet. run `sparrow plg install $pid` to install it\n";
        exit(1);
    }

    unless ( -d sparrow_root."/projects/$project" ){
        print "project $project does not exist. run `sparrow project $project create` to create it it\n";
        exit(1);
    }

    if ( -l sparrow_root."/projects/$project/plugins/$pid" ){

        print "projects/$project/plugins/$pid already exist - nothing to do here ... \n\n";

    }else{

        symlink File::Spec->rel2abs(sparrow_root."/plugins/$pid"), File::Spec->rel2abs(sparrow_root."/projects/$project/plugins/$pid") or 
        croak "can't create symlink projects/$project/plugins/$pid ==> plugins/$pid";

        print "plugin $pid is successfully added to project $project\n\n";
    }

}

sub add_site_to_project {

    my $project = shift or croak "usage: add_site_to_project(project,site,base_url)";
    my $sid = shift or croak "usage: add_site_to_project(project,site,base_url)";
    my $base_url = shift or croak "usage: add_site_to_project(project,site,base_url)";

    if (-d sparrow_root."/projects/$project/sites/$sid" ){

        set_site_base_url($project,$sid,$base_url);

        print "site $sid is successfully updated at project $project\n\n";

    }else{

        mkpath sparrow_root."/projects/$project/sites/$sid";
        set_site_base_url($project,$sid,$base_url);
        print "site $sid is successfully added to project $project\n\n";
        
    }

}

sub swat_setup {

    my $project = shift or croak "usage: swat_setup(project,site)";
    my $sid = shift or croak "usage: swat_setup(project,site)";

    if (-d sparrow_root."/projects/$project/sites/$sid" ){

        croak "please setup your preferable editor via EDITOR environment variable\n" unless $EDITOR;
        exec "$EDITOR ".sparrow_root."/projects/$project/sites/$sid/swat.my";
    }else{
        croak "site $sid does not exist at project $project. use `sparrow project $project add_side $sid \$base_url' to create a site \n\n";
    }

}


sub check_site {

    my $project = shift or croak "usage: check_site(project,site,plugin)";
    my $sid = shift or croak "usage: check_site(project,site,plugin)";
    my $pid = shift or croak "usage: check_site(project,site,plugin)";

    my $site_base_url = site_base_url($project,$sid);


    if ( -f sparrow_root."/projects/$project/sites/$sid/swat.my" ){
        copy( sparrow_root."/projects/$project/sites/$sid/swat.my", sparrow_root."/projects/$project/plugins/$pid"); 
    }

    exec "cd ".sparrow_root."/projects/$project/plugins/$pid && carton exec swat ./ $site_base_url";

}


sub usage {

    print "usage: sparrow project|plg action args\n";
    print "where action: create|list|install|update|add_site|check_site|swat_setup. and args depend on action\n";
    print "examples:\n";
    print "\tsparrow plg list\n";
    print "\tsparrow plg list --local\n";
    print "\tsparrow plg install nginx\n";
    print "\tsparrow plg update tomcat\n";
    print "\tsparrow project foo create\n";
    print "\tsparrow project foo add_plg nginx\n";
    print "\tsparrow project foo add_plg tomcat\n";
    print "\tsparrow project foo add_plg tomcat\n";
    print "\tsparrow project foo add_site nginx_proxy 127.0.0.1\n";
    print "\tsparrow project foo add_site tomcat_app 127.0.0.1:8080\n";
    print "\tsparrow project foo check_site nginx_proxy nginx\n";
    print "\tsparrow project foo check_site tomcat_app nginx\n";
    print "\tsparrow project foo swat_setup nginx_proxy /path/to/swat.ini\n";
    print "\tsparrow projects\n";
    print "\n";
    print "follow doc site - https://github.com/melezhik/sparrow to get more\n";

}


sub site_base_url {

    my $project = shift or croak "usage: site_base_url(project,site)";
    my $sid = shift or croak "usage: site_base_url(project,site)";

    open F, sparrow_root."/projects/$project/sites/$sid/base_url" or croak "can't open file projects/$project/sites/$sid/base_url to read";
    my $base_url = <F>;
    chomp $base_url;
    close F;
    $base_url;
    
}

sub set_site_base_url {

    my $project = shift or croak "usage: set_site_base_url(project,site,base_url)";
    my $sid = shift or croak "usage: set_site_base_url(project,site,base_url)";
    my $base_url = shift or croak "usage: set_site_base_url(project,site,base_url)";

    open F, ">", sparrow_root."/projects/$project/sites/$sid/base_url" or croak "can't open file to write: projects/$project/sites/$sid/base_url";
    print F $base_url;
    close F;
    
}

sub execute_shell_command {

    my $cmd = shift;

    # warn $cmd;

    croak "failed execute: $cmd" unless system($cmd) == 0;

    
}

sub init_sparrow_env {


    mkpath sparrow_root;
    mkpath sparrow_root.'/plugins';
    mkpath sparrow_root.'/projects';

    print "# sparrow environment initialzed at ".sparrow_root, "\n";
}
