#!/usr/bin/perl

use strict;

use File::Find;
use Getopt::Long;
use File::Path qw(make_path remove_tree);
use File::Spec;
use Term::ANSIColor;
use Cwd;

my $project_root_dir  = $ENV{'OUTTHENTIC_ROOT'} || getcwd();
my $cwd = $ENV{'OUTTHENTIC_CWD'} || getcwd();
my $purge_cache   = 0;
my $format        = 'default';
my $debug_mod     = 0;
my $dump_config   = 0;
my $nocolor       = 0;
my $task_name     = '';
my $story_path;
my $match_l = $ENV{'OUTTHENTIC_MATCH'} || 200;
my $ini_file_path;
my $yaml_file_path;
my $json_file_path;
my @runtime_params;
my $args_file;

our $cli_args;

BEGIN { 
  my $i=0;  
  for my $a (@ARGV) {
    if ($a eq '--') {
      delete $ARGV[$i];
      $cli_args = join ' ', delete @ARGV[$i .. $#ARGV];
      last;
    }
    $i++;
  }  

}

GetOptions (

        "cwd=s"         => \$cwd,               # string
        "format=s"      => \$format,            # string
        "purge-cache"   => \$purge_cache,       # boolean
        "nocolor"       => \$nocolor,           # boolean
        "debug=i"       => \$debug_mod,         # numeric
        "match_l=i"     => \$match_l,           # numeric
        "root=s"        => \$project_root_dir,  # string
        "story=s"       => \$story_path,        # string
        "task=s"        => \$task_name,         # string
        "ini=s"         => \$ini_file_path,     # string
        "yaml=s"        => \$yaml_file_path,    # string
        "json=s"        => \$json_file_path,    # string
        "param=s"       => \@runtime_params,    # string
        "args-file=s"   => \$args_file,         # string
        "dump-config"   => \$dump_config,       # boolean


) or die("Error in command line arguments\n");


$nocolor ||= $ENV{SPARROW_NO_COLOR};

my $cache_root_dir = $ENV{SPARROW_ROOT} ? "$ENV{SPARROW_ROOT}/tmp/".$$ : "$ENV{HOME}/.outthentic/tmp/".$$;

remove_tree($cache_root_dir);
make_path($cache_root_dir);

$project_root_dir =  File::Spec->rel2abs($project_root_dir);

my %seen = ();

finddepth(\&wanted_downstreams, $project_root_dir."/modules" );

finddepth(\&wanted_upstreams, $project_root_dir );


sub wanted_upstreams {
    wanted($_,'upstream');
}

sub wanted_downstreams {
    wanted($_,'downstream');
}

sub wanted { 

    my $file = shift;

    my $story_type = shift;

    if ($file =~ /^story\.(bash|pl|pm|rb|py)$/ or $file =~ /^hook\.(bash|pl|rb|py)$/ ) {

        return if $file=~/^hook\./ and -e "$File::Find::dir/story.pl";
        return if $file=~/^hook\./ and -e "$File::Find::dir/story.bash";
        return if $file=~/^hook\./ and -e "$File::Find::dir/story.rb";
        return if $file=~/^hook\./ and -e "$File::Find::dir/story.py";

        return if $file eq 'story.pm' and -e "$File::Find::dir/story.pl";
        return if $file eq 'story.pm' and -e "$File::Find::dir/story.bash";
        return if $file eq 'story.pm' and -e "$File::Find::dir/story.rb";
        return if $file eq 'story.pm' and -e "$File::Find::dir/story.py";

        return if $seen{$File::Find::name};

        my $story_dir = $File::Find::dir;

        make_path("$cache_root_dir/$story_dir");

        my $tfile = "$cache_root_dir/$story_dir/sparrow.pl";

        open F, ">", $tfile or die $!;
    
        print F "package main;\n\n";
    
        print F "BEGIN { push \@INC, q{$project_root_dir/lib}; }\n";
    
        print F "use strict;\n";

        print F "use Outthentic;\n\n\n";
    
        print F "new_story();\n\n";
    
        print F "set_prop( cwd => q{$cwd} );\n";
        print F "set_prop( project_root_dir => q{$project_root_dir} );\n";
        print F "set_prop( cache_root_dir    => q{$cache_root_dir} );\n";
        print F "set_prop( ini_file_path    => q{$ini_file_path} );\n"    if $ini_file_path;
        print F "set_prop( yaml_file_path   => q{$yaml_file_path} );\n"  if $yaml_file_path;
        print F "set_prop( json_file_path   => q{$json_file_path} );\n"  if $json_file_path;
    
        print F "set_prop( story => q{$File::Find::dir} );\n";
        print F "set_prop( story_dir => q{$File::Find::dir} );\n";
        if (-e "$File::Find::dir/story.check"){
          print F "set_prop( story_check_file => q{$File::Find::dir/story.check} );\n";
        } else {
          print F "set_prop( story_check_file => '' );\n";
        }
        print F "set_prop( story_type => q{$story_type} );\n";
        print F "set_prop( format => q{$format} );\n";

        print F "set_prop( debug => $debug_mod );\n";
        print F "set_prop( nocolor => $nocolor );\n";
        print F "set_prop( match_l => $match_l );\n";
        print F "set_prop( runtime_params => q{".(join ':::', @runtime_params)."} );\n";
        print F "set_prop( args_file => q{$args_file} );\n";
        print F "set_prop( task_name => '$task_name' );\n";
        print F "set_prop( cli_args => q{$cli_args} );\n";

        print F "\nset_story();\n\n";
        print F "\npopulate_config();\n\n";

        if ($dump_config){
          print F "\ndump_config();\n\n";
          return
        }
    
        if ($story_type eq 'downstream') {
            print F "apply_story_vars();\n";
        }

        if (-e "$File::Find::dir/meta.txt"){
          print F "print_meta();\n\n" unless $format eq 'concise';
        }

        if ( -e "$File::Find::dir/story.pm" ){
            print F "eval { do_perl_hook('$File::Find::dir/story.pm') }; our \$STATUS=0, die \"perl hook error: \$@\" if \$@ ;\n\n";
        }elsif ( -e "$File::Find::dir/hook.pl" ){
            print F "eval { do_perl_hook('$File::Find::dir/hook.pl') };  our \$STATUS=0, die \"perl hook error: \$@\" if \$@ ;\n\n";
        }elsif ( -e "$File::Find::dir/hook.rb" ){
            print F "eval { do_ruby_hook('$File::Find::dir/hook.rb') };  our \$STATUS=0, die \"ruby hook error: \$@\" if \$@ ;\n\n";
        }elsif ( -e "$File::Find::dir/hook.py" ){
            print F "eval { do_python_hook('$File::Find::dir/hook.py') };  our \$STATUS=0, die \"python hook error: \$@\" if \$@ ;\n\n";
        }elsif ( -e "$File::Find::dir/hook.bash" ){
            print F "eval { do_bash_hook('$File::Find::dir/hook.bash') }; our \$STATUS=0, die \"bash hook error: \$@\" if \$@ ;\n\n";
        }

        if (-e "$File::Find::dir/story.check"){
          print F "eval { run_and_check(q{$File::Find::dir/story.check}) }; die \"asserts error: \$@\" if \$@ ;\n\n";
        } else {
          print F "eval { run_and_check() }; die \"asserts error: \$@\" if \$@ ;\n\n";
        }

        print F 'end_of_story();',"\n\n";

        print F "1;\n\n";    

        close F;

        $seen{$File::Find::name}=1;

   }

}

my $st;
my $exit_code;

if ($story_path){

    my $strun_cmd = "perl $cache_root_dir$project_root_dir/$story_path/sparrow.pl";
    $st = (system($strun_cmd)==0);
    $exit_code = sprintf "%d", $? >> 8;
}else{
    my $strun_cmd = "perl $cache_root_dir$project_root_dir/sparrow.pl";
    $st = (system($strun_cmd)==0);
    $exit_code = sprintf "%d", $? >> 8;
}

system("rm -rf $cache_root_dir") if $purge_cache;

unless ( $dump_config ) { # do not check story status in case we only dump config data
  #warn "state: $st";
  #warn "exit code: $exit_code";
  if ($st) {
    print $nocolor ? "STATUS\tSUCCEED\n" : colored(["green"],"STATUS\tSUCCEED\n")
      unless $format eq 'concise';
  }else{
    print $nocolor ? "STATUS\tFAILED ($exit_code)\n" :  colored(["red"],"STATUS\tFAILED ($exit_code)\n")
      unless $format eq 'concise';
    exit($exit_code);
  }
  
}
