#!/usr/bin/env perl
use warnings;
use Switch;
use YAML;

# List of availabl e actions
my @list_of_actions = qw(pack ping clean uninstall deploy install start stop check);


#Global Variables
use vars qw( $topology $path $java $grm $hash $shared @ignore);

# default values for global variables
$topology = "default"; #default.yml file
$java = "/usr/lib/jvm/java-6-sun/";

# The empty values come from default topology
$path = "";
$grm = "";
$hash = "";
$shared = "";
@ignore = [];


######## TO-DO ##############

# IMPROVEMENTS 
# - Folder Names
# - Start Services (can't solve)
# - Script Folder

# ARCHITECTURAL
# - Must include taktuk
# - Install diferent archtectures
# - Integrade NameService ports

# FAPESP PAGE
# - Colocar tudo de volta na internet
# - Link Para Docs

#Loads the topology from $topology.yml file
sub load_topology{
    
    #Tries to open the yml file.
	open my $fh, '<', $topology.'.yml'
		or die "FAILURE: can't open $topology.yml file: $!";
    
    # Reads the file
	my $yml = do { local $/; <$fh> }; 
    #Loads the yml file into a hash reference 
	my $machines = Load($yml);
	#Dereferences into the hash 
    my %mach = %$machines; 
    
    if (exists $mach{"java"}) {
        # Global path
        $path = delete($mach{"path"}); 
    } else {
        printf("FAILURE: path not declared properly, check $topology.yml file\n");
    }
    
    if (exists $mach{"java"}) {
        # Global GRM
        $grm = delete($mach{"grm"}); 
    } else {
        printf("FAILURE: grm not declared properly, check $topology.yml file\n");
    }
    
    # List of ignored modules for installation
    @ignore = delete($mach{"ignore"}) if (exists $mach{"ignore"});
    # Java path
    $java = delete($mach{"java"}) if (exists $mach{"java"}); 
    # Nfs or other shared filesystem path
    $shared = delete($mach{"shared"}) if (exists $mach{"shared"}); 
    
    # Hash of machines
    $hash = \%mach; 
}

sub taktuk{
    # Gets the parameters of the subroutine invocation
    my ($out,$cmd,$machines,$files) = @_;
    
    #Dereferences machines
    my @machines = @$machines;
    # list of taktuk machines
    my $tkmachines = join(" -m ",@machines);
    
    #Taktuk string for file colection
    my $tkfiles = "";
    
    #if it has the fourt argument, it means that the fourt is a list of files
    $tkfiles = "-S '".join(",",@$files)."'" if (@_ == 4);

    # Runs taktuk and return the exit status
    return system("./taktuk-3.6.1/taktuk $out -s $tkfiles -m $tkmachines $cmd");
}

# Tar Warning is annoying
sub action_pack{
    printf("Packing Integrade and Deps... \n");
    if ( -e "packs/deploy.tar.gz") {
        printf("Deps exists");
    } else {
        system("cd packs/deps && make clean > /dev/null && cd -  > /dev/null");
        system("cd packs > /dev/null && tar czvf deploy.tar.gz --exclude .svn  ../../integrade > /dev/null && cd  -  > /dev/null");
        printf("Packing Deps\n");
    }
    if ( -e "packs/deps.tar.gz") {
        printf("Integrade exists\n");
    } else {
        system("cd packs  > /dev/null&& tar czvf deps.tar.gz --exclude .svn deps > /dev/null && cd - > /dev/null");
        printf("Packing Integrade\n");        
    }
    return 0;
}

sub action_ping{
    my @machines = keys(%$hash);    
    my $command = "broadcast exec [ '
        hostname
        ' ]";
    printf "Look if all hosts are available...\n ";
    my $out = " -o error='\"ERR  - \$line\\n\"' -o output='\"\$line\\n\"' -o status=' '";
    my $return_value = taktuk($out,$command,\@machines);
    printf "DONE\n";
    return $return_value;
}

sub action_clean{
    print("Cleaning local files.... ");
    my $return_value = system("rm -Rf packs/deps.tar.gz packs/deploy.tar.gz");
    print("DONE\n");
    my @machines = keys(%$hash);    
    my $command = "broadcast exec [ '
        rm -Rf deploy.log deploy.tar.gz deps.tar.gz /tmp/log
        ' ]";
    print("Clening remote files... ");
    my $out = " -o error='\"ERR  - \$line\\n\"' -o output=' ' -o status=' '";
    $return_value += taktuk($out,$command,\@machines);
    print("DONE\n");
    return $return_value;
}

sub action_uninstall{
    my @machines = keys(%$hash);    
    my $command = "broadcast exec [ '
        rm -Rf deploy.log deploy.tar.gz deps.tar.gz $path/integrade/ $path/deps /tmp/log $path/deploy.tar.gz $path/deps.tar.gz 
        ' ]";
    print("Removing Integrade... ");
    my $out = " -o error='\"ERR  - \$line\\n\"' -o output=' ' -o status=' '";
    my $return_value = taktuk($out,$command,\@machines);
    print("DONE\n");
    return $return_value;
}

sub action_deploy{
    my $command = "broadcast exec [ 'mkdir /tmp/log' ]";    
    my $out = " -o error='\"ERR  - \$line\\n\"' -o output=' ' -o status=' '";
    my @files = ("packs/deploy.tar.gz:deploy.tar.gz","packs/deps.tar.gz:deps.tar.gz");
    my @machines;
    my $return_value;
    if ($shared eq ""){
        printf("Deploying all machines.... ");    
        @machines = keys(%$hash);
        $return_value = taktuk($out,$command,\@machines,\@files);
    } else {
        printf("Deploying only $grm (shared mode).... ");
        @machines = ($grm);
        $return_value = taktuk($out,$command,\@machines,\@files);
    }
    printf("DONE\n");
    return $return_value;
}

sub action_install{
    printf("Installing (This may take a while)...\n");
    my $out = " -o error='\"WARNING  - \$line\\n\"' -o output=' ' -o status=' '";
    my $install_path;
    my @machines_deps;
    
    #decides if dependencies will be installed localy or via shared file system
    
    if ($shared eq ""){
        printf("Running dependencies for all nodes....");
        @machines_deps = keys(%$hash);
        $install_path = $path;
    } else {
        printf("Running dependencies for $grm....");
        @machines_deps = ($grm);
        $install_path = $shared;
    }
    
    my @machines_make = keys(%$hash);
    my $command_make = "broadcast exec [ '
        cp deploy.tar.gz $path >> /tmp/log/deploy-`hostname`.log &&
        cd $path && tar zxvf deploy.tar.gz >> /tmp/log/tar-`hostname`.log &&
        ln -s $install_path/deps $path/integrade/deps &&
        cd $path/integrade &&
        ./scripts/tkmake.sh `pwd` $grm $java >> /tmp/log/tkmake-`hostname`.log
        ' ]";
    
    my $command_deps = "broadcast exec [ '
        cp deps.tar.gz $install_path >> /tmp/log/deploy-`hostname`.log &&
        cd $install_path && tar zxvf deps.tar.gz >> /tmp/log/tar-`hostname`.log &&
        cd deps &&
        make clean &&
        make all
        ' ]";
    
    taktuk($out,$command_deps,\@machines_deps);
    printf("DONE\n Building integrade....");
    taktuk($out,$command_make,\@machines_make);
    printf("DONE\n Cleaning information files...");
    my $command_clean = "broadcast exec [ '
        rm -Rf $path/deploy.tar.gz $path/deps.tar.gz deploy.tar.gz deps.tar.gz 
    ' ]";
    taktuk($out,$command_clean,\@machines_make);
    printf("DONE\n")
}

# issue grm first

sub action_start{
    my @machines = keys(%$hash);
    my $command = "broadcast exec [ '
        cd $path/integrade ;
        ./scripts/tkstart.sh `pwd` $grm
        ' ]";
    my $out = " -o error='\"WARNING  - \$line\\n\"' -o output=' ' -o status=' '";
    print "Integrade is beeing started.... (because of bug in Integrade scripts this call never finishes...)\n";
    print "Type CTRL+C\n";
    return taktuk($out,$command,\@machines);
}

sub action_stop{
    my @machines = keys(%$hash);
    my $command = "broadcast exec [ '
        $path/integrade/stopservices.sh all
        ' ]";
    my $out = " -o error='\"WARNING  - \$line\\n\"' -o output=' ' -o status=' '";
    print "Stopping all services...... ";
    my $return_value = taktuk($out,$command,\@machines);
    print "DONE\n";
    return $return_value;
}

sub action_check{
    print "$topology.yml > GRM $grm of ".join(" ",keys(%$hash))." in $path with java $java\n"; 
    return 0;
}

sub action_all{
    action_check();
    action_ping();
    action_clean();
    action_pack();
    action_deploy();
    action_install();
}

sub main{
    #print("Welcome to ig-deployer v0.1. Powered by TakTuk.\n");
    my $numArgs = $#ARGV + 1;
    

    if ($numArgs >= 1){
        
        if ($numArgs >= 2){
            $topology = $ARGV[1];
        }
                
        load_topology();
        
        my $obj = bless {};

        if (my $call = $obj->can("action_".$ARGV[0])) {
            $call->();
        }else{
            print "Action $ARGV[0] does not exist.\n";
            print "Availabe actions are ".join(", ",@list_of_actions)."\n";
        }
    } else {
        print("Say a command..\n");
    }
}

main();