#!/usr/bin/perl -w
# $Id$
# print active job properties

use strict;
use warnings;
use Data::Dumper;
#use OAR::IO;
use Getopt::Long;
#use OAR::Version;
use OAR::Conf qw(init_conf dump_conf get_conf is_conf);
use OAR::Stat;

my $Old_umask = sprintf("%lo",umask());
umask(oct("022"));

# Read config
init_conf($ENV{OARCONFFILE});
my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD");
my $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE");


#Try to load XML module
my $XML_enabled = 1;
unless (eval "use XML::Dumper qw(pl2xml);1"){
    $XML_enabled = 0;
}

#Try to load YAML module
my $YAML_enabled = 1;
unless (eval "use YAML;1"){
    $YAML_enabled = 0;
}

#Try to load JSON module
my $JSON_enabled = 1;
unless (eval "use JSON;1"){
    $JSON_enabled = 0;
}

# suitable Data::Dumper configuration for serialization
$Data::Dumper::Purity = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 1;
$Data::Dumper::Deepcopy = 1;

### Variables declaration ###
my $Printed_jobs = 0;

my @job_ids;
my $array_id;
my $full_view;
my $state;
my $sql_property;
my $XML_mode;
my $YAML_mode;
my $JSON_mode;
my $DUMPER_mode;
my $gantt_query;
my $accounting_query;
my $events_query;
my $properties_query;
my $usage;
my $version;

my $user;
### END Variables declaration ###


### Main ###

# parse command line option
Getopt::Long::Configure ("gnu_getopt");
GetOptions ("help|h" => \$usage,
            "version|V" => \$version,
            "job|j=i"  => \@job_ids,
            "full|f"  => \$full_view,
            "state|s"  => \$state,
            "user|u:s" => \$user,
            "array:i" => \$array_id,
            "gantt|g=s"   => \$gantt_query,
            "events|e" => \$events_query,
            "properties|p" => \$properties_query,
            "accounting=s"   => \$accounting_query,
            "sql=s"   => \$sql_property,
            "xml|X" => \$XML_mode,
            "yaml|Y" => \$YAML_mode,
            "json|J" => \$JSON_mode,
            "dumper|D" => \$DUMPER_mode
           );

if ($usage){
    &print_usage;
    exit(0);
}

if ($version){
    &print_oar_version;
    exit(0);
}

if (defined($XML_mode) and $XML_enabled != 1){
    warn("XML module not available on the system. Ask your administrator to install it if needed.\n");
    exit(1)
}
if (defined($YAML_mode) and $YAML_enabled != 1){
    warn("Yaml module not available on the system. Ask your administrator to install it if needed.\n");
    exit(1);
}
if (defined($JSON_mode) and $JSON_enabled != 1){
    warn("Json module not available on the system. Ask your administrator to install it if needed.\n");
    exit(1);
}

$user = $ENV{OARDO_USER} if (defined($user) and ($user eq ''));

if (defined($array_id) && $array_id !=0 && (scalar @job_ids > 0)){
    warn("/!\\ ERROR : Conflicting Job IDs and Array IDs (--array and -j cannot be used together)\n");
    exit(1);
}

OAR::Stat::open_db_connection() or die "DB connection error, exiting.\n";

if (defined ($gantt_query)) {
    &print_gantt;
} elsif (defined ($accounting_query)) {
    &show_accounting;
} elsif (defined ($events_query)) {
    &print_events;
} elsif (defined ($properties_query)) {
	&print_properties;
} elsif (defined ($state)) {
    &print_state;
} else {
    &print_job;
}

OAR::Stat::close_db_connection();
exit(0);

### END Main ###


### Print Methods ###

sub print_usage{
    print <<EOS;
Usage: oarstat [-X|-Y|-D|-f|-s] [-j job_id|--array array_id] [--sql SQL_properties] [-u user] [--array]
       oarstat [-e|-p] [-j jobid|--array array_id] 
       oarstat [-X|-Y|-J|-D] --gantt "YYYY-MM-DD hh:mm:ss, YYYY-MM-DD hh:mm:ss"
       oarstat --accounting "YYYY-MM-DD, YYYY-MM-DD"

Print job information

Options:
  -j, --job                 show informations only for the specified job
  -f, --full                show full informations
  -s, --state               show only the state of a job (optimized query)
  -u, --user                show informations for this user only
      --array               show informations for the specified array_job(s) and
                            toggle array view in
  -g, --gantt               show job informations between two date-times
  -e, --events              show job events
  -p, --properties          show job properties
      --accounting          show accounting informations between two dates
      --sql                 restricts display by applying the SQL where clause
                            on the table jobs (ex: "project = 'p1'")
  -D, --dumper              print result in DUMPER format
  -X, --xml                 print result in XML format
  -Y, --yaml                print result in YAML format
  -J, --json                print result in JSON format
  -V, --version             print OAR version number
  -h, --help                show this help screen
EOS
}

sub print_events {
	if(defined($array_id) &&  $array_id !=0){
        push(@job_ids, OAR::Stat::get_array_job_ids($array_id));
    }
	
    my $events = OAR::Stat::get_events(\@job_ids);
    if (defined( $events )) {
      foreach my $event_hashref (@$events) {
          printf(
            "%s| %s| %s: %s\n",
            OAR::Stat::local_to_sql( $event_hashref->{'date'} ),
            $event_hashref->{'job_id'},
            $event_hashref->{'type'},
            $event_hashref->{'description'}
          );
      }
    }
    else {
        warn("No job specified\n");
        OAR::Stat::close_db_connection();
        exit(1);
    }
}

sub print_gantt {
  my $hist = OAR::Stat::get_gantt($gantt_query);
  if (defined($hist)){
    if (defined($DUMPER_mode)) {
        print(Dumper($hist));
    }elsif (defined($XML_mode)) {
        my $dump = new XML::Dumper;
        $dump->dtd;
        print($dump->pl2xml($hist));
    }elsif (defined($YAML_mode)) {
        print(YAML::Dump($hist));
	}elsif (defined($JSON_mode)){
		print(JSON->new->pretty(1)->encode($hist));
	}else{
        $Data::Dumper::Purity   = 1;
        $Data::Dumper::Terse    = 1;
        $Data::Dumper::Indent   = 1;
        $Data::Dumper::Deepcopy = 0;
        print( Dumper($hist) );
    }
  }else{
      warn("Bad syntax for --gantt\n");
      OAR::Stat::close_db_connection();
      exit(1);
  }
}

sub print_job {
    my $jobs;
    if ( $#job_ids < 0 ) {
        if ( defined($sql_property) ) {
            $jobs = OAR::Stat::get_jobs_with_given_properties($sql_property);
		}elsif(defined($array_id) && $array_id !=0){
			$jobs = OAR::Stat::get_array_subjobs($array_id);
        }else{
            $jobs = OAR::Stat::get_all_jobs_for_user($user);
        }
    }
    elsif ( $#job_ids >= 0 ) {
        $jobs = OAR::Stat::get_specific_jobs(\@job_ids);
    }
    my %data_to_print;
    foreach my $g (@$jobs) {
		$data_to_print{ $g->{job_id} } = OAR::Stat::get_job_data($g,$full_view);
    }
    print_job_data( \%data_to_print, $jobs );
}

sub print_job_data($$){
    my $data = shift;
    my $job_array = shift;

   
    if (defined($DUMPER_mode)){
        print(Dumper($data));
    }elsif(defined($XML_mode)){
        my $dump = new XML::Dumper;
        $dump->dtd;
        print($dump->pl2xml($data));
    }elsif(defined($YAML_mode)){
        print(YAML::Dump($data));
    }elsif(defined($JSON_mode)){
        print(JSON->new->pretty(1)->encode($data));
    }else{
        my %hashestat = (
			'Waiting' => 'W',
			'toLaunch' => 'L',
			'Launching' => 'L',
			'Hold'        => 'H',
			'Running' => 'R',
			'Terminated' => 'T',
			'Error' => 'E',
			'toError' => 'E',
			'Finishing' => 'F',
			'Suspended' => 'S',
			'Resuming' => 'S',
			'toAckReservation' => 'W'
        );
		
		foreach my $job_info (@{$job_array}){
			if (defined($full_view)){
                print("Job_Id: $job_info->{job_id}\n");
                $job_info->{job_name} = '' if (!defined($job_info->{job_name}));
                print("    job_array_id = $job_info->{array_id}\n");
                print("    job_array_index = $job_info->{array_index}\n");
                print("    name = $job_info->{job_name}\n");
                print("    project = $job_info->{project}\n");
                print("    owner = $job_info->{job_user}\n");
                print("    state = $job_info->{state}\n");
                print("    wanted_resources = $data->{$job_info->{job_id}}->{wanted_resources}\n");
                print("    types = ".join(", ",@{$data->{$job_info->{job_id}}->{types}})."\n");
                print("    dependencies = ".join(" ",@{$data->{$job_info->{job_id}}->{dependencies}})."\n");
                print("    assigned_resources = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_resources}})."\n");
                print("    assigned_hostnames = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_network_address}})."\n");
                print("    queue = $job_info->{queue_name}\n");
                $job_info->{command} = '' if (!defined($job_info->{command}));
                print("    command = $job_info->{command}\n");
                if (defined($job_info->{exit_code})){
                    my $exit_code = $job_info->{exit_code} >> 8;
                    my $exit_num = $job_info->{exit_code} & 127;
                    my $exit_core = $job_info->{exit_code} & 128;
                    print("    exit_code = $job_info->{exit_code} ($exit_code,$exit_num,$exit_core)\n");
                }
                print("    launchingDirectory = $job_info->{launching_directory}\n");
                print("    stdout_file = $data->{$job_info->{job_id}}->{stdout_file}\n") ;
                print("    stderr_file = $data->{$job_info->{job_id}}->{stderr_file}\n");
                print("    jobType = $job_info->{job_type}\n");
                print("    properties = $job_info->{properties}\n");
                print("    reservation = $job_info->{reservation}\n");
                if (defined $data->{$job_info->{job_id}}->{'reserved_resources'}) {
                    print("    reserved_resources = ");
                    my @tmp_array_ok;
                    my @tmp_array_ko;
                    for my $r (keys %{$data->{$job_info->{job_id}}->{'reserved_resources'}}) {
                        if ($data->{$job_info->{job_id}}->{'reserved_resources'}->{$r}->{'current_state'} eq "Alive") {
                            push (@tmp_array_ok,$r);
                        } else {
                            push (@tmp_array_ko,$r);
                        }
                    }
                    my $tmp_str_ok = join("+",sort ({ $a <=> $b } @tmp_array_ok));
                    my $tmp_str_ko = join("+",sort ({ $a <=> $b } @tmp_array_ko));
                    if ( $tmp_str_ok ne "" ) {
                        print $tmp_str_ok;
                    } else {
                        print("none");
                    }
                    if ( $tmp_str_ko ne "" ) {
                        print "+($tmp_str_ko)";
                    }
                    print("\n");
                }
                if (!defined($data->{$job_info->{job_id}}->{walltime})){
                    $data->{$job_info->{job_id}}->{walltime} = '';
                }else{
                    $data->{$job_info->{job_id}}->{walltime} = OAR::Stat::duration_to_sql($data->{$job_info->{job_id}}->{walltime});
                }
                print("    walltime = $data->{$job_info->{job_id}}->{walltime}\n");
                print("    submissionTime = ".OAR::Stat::local_to_sql($job_info->{submission_time})."\n");
                print("    startTime = ".OAR::Stat::local_to_sql($job_info->{start_time})."\n") if ($job_info->{start_time} > 0);
                print("    stopTime = ".OAR::Stat::local_to_sql($job_info->{stop_time})."\n") if ($job_info->{stop_time} > 0);
                if (defined($data->{$job_info->{job_id}}->{cpuset_name})){
                    print("    cpuset_name = $data->{$job_info->{job_id}}->{cpuset_name}\n");
                }
                if (defined($data->{$job_info->{job_id}}->{job_uid})){
                    print("    job_user = $data->{$job_info->{job_id}}->{job_user}\n");
                    print("    job_uid = $data->{$job_info->{job_id}}->{job_uid}\n");
                }
                if ((defined($job_info->{initial_request})) and
                    (($ENV{OARDO_USER} eq $job_info->{job_user}) or
                     ($ENV{OARDO_USER} eq "oar") or
                     ($ENV{OARDO_USER} eq "root"))
                   ){
                    print("    initial_request = $job_info->{initial_request}\n");
                }else{
                    print("    initial_request = \n");
                }
                print("    message = $job_info->{message}\n");
                if (!defined($data->{$job_info->{job_id}}->{scheduledStart})){
                    $data->{$job_info->{job_id}}->{scheduledStart} = "no prediction";
                }else{
                    $data->{$job_info->{job_id}}->{scheduledStart} = OAR::Stat::local_to_sql($data->{$job_info->{job_id}}->{scheduledStart});
                }
                print("    scheduledStart = $data->{$job_info->{job_id}}->{scheduledStart}\n");
                print("    resubmit_job_id = $job_info->{resubmit_job_id}\n");
                print("    events = ");
                foreach my $e (@{$data->{$job_info->{job_id}}->{events}}){
                    print("[".OAR::Stat::local_to_sql($e->{date})."] $e->{type}:$e->{description}");
                    print(" , ");
                }
                print("\n\n");
			}else{
				if(defined($array_id)){
                    if ($Printed_jobs == 0){
                        print <<EOS;
Job id    A. id     A. index  Name       User     Submission Date     S Queue
--------- --------- --------- ---------- -------- ------------------- - --------
EOS
                    }
                    #$job_info->{'command'} = '' if (!defined($job_info->{'command'}));                              
                    $job_info->{job_name} = '' if (!defined($job_info->{job_name}));            
                    printf("%-9.9s %-9.9s %-9.9s %-10.10s %-8.8s %-19.19s %1.1s %-8.8s\n",
                        $job_info->{'job_id'},
                        $job_info->{'array_id'},
                        $job_info->{'array_index'},
						$job_info->{'job_name'},
                        $job_info->{'job_user'},
                        OAR::Stat::local_to_sql($job_info->{'submission_time'}),
                        $hashestat{$job_info->{'state'}},
                        $job_info->{'queue_name'}
                    );                    
                    $Printed_jobs ++;
				}else{
					if ($Printed_jobs == 0){
						print <<EOS;
Job id     Name           User           Submission Date     S Queue
---------- -------------- -------------- ------------------- - ----------
EOS
					}
					
					#$job_info->{'command'} = '' if (!defined($job_info->{'command'}));
					$job_info->{job_name} = '' if (!defined($job_info->{job_name}));
					printf("%-10.10s %-14.14s %-14.14s %-19.19s %1.1s %-10.10s\n",
						$job_info->{'job_id'},
						$job_info->{'job_name'},
						$job_info->{'job_user'},
						OAR::Stat::local_to_sql($job_info->{'submission_time'}),
						$hashestat{$job_info->{'state'}},
						$job_info->{'queue_name'}
					);
					$Printed_jobs ++;
				}
			}
		}
	}
}

sub print_job_state_data($){
    my $data = shift;

    if (defined($DUMPER_mode)){
        print(Dumper($data));
    }elsif(defined($XML_mode)){
        my $dump = new XML::Dumper;
        $dump->dtd;
        print($dump->pl2xml($data));
    }elsif(defined($YAML_mode)){
        print(YAML::Dump($data));
    }elsif(defined($JSON_mode)){
        print(JSON->new->pretty(1)->encode($data));
    }else{
        foreach my $j (keys %$data){
           print "$j: ". $data->{$j} ."\n";
        }
    }
}

sub print_oar_version {
    print "OAR version : ".OAR::Stat::get_oar_version()."\n";
}

sub print_properties {
    if(defined($array_id) &&  $array_id !=0){
        push(@job_ids, OAR::Stat::get_array_job_ids($array_id));
    }

    if($#job_ids >= 0){
        my @resources;
        foreach my $j (@job_ids){
            push  (@resources, OAR::Stat::get_job_resources_properties($j));
        }
        foreach my $r (@resources){
            my $line = "";
            foreach my $p (keys(%{$r})){
                if(OAR::Tools::check_resource_system_property($p) != 1){
                    $r->{$p} = "" if (!defined($r->{$p}));
                    $line .= " $p = '$r->{$p}' ,"
                }
            }
            chop($line);
            print("$line\n") or exit(5);
        }
    } else {
        warn("No job specified\n");
        OAR::Stat::close_db_connection();
        exit(1);
    }
}

sub print_state {
    my %job_state;
    if ($#job_ids < 0){
        warn("--state can only be used with an id\n");
          exit(1);
    }elsif($#job_ids >= 0){
        foreach my $j (@job_ids){
            my $state_string = OAR::Stat::get_job_state($j);
            if (defined($state_string)){
                $job_state{$j}=$state_string;
            }
        }
    }
    print_job_state_data(\%job_state);
}

sub show_accounting {
    if ($accounting_query =~ m/\s*(\d{4}\-\d{1,2}\-\d{1,2})\s*,\s*(\d{4}\-\d{1,2}\-\d{1,2})\s*/m){
        my ($date1,$date2) = ($1." 00:00:00",$2." 00:00:00");
        my $login;
        my $Consumptions=OAR::Stat::get_accounting_summary(OAR::Stat::sql_to_local($date1),OAR::Stat::sql_to_local($date2),$user,$sql_property);
    
        # One user output
        if (defined($user)) {
            my $asked;
            my $used;
            if (defined($Consumptions->{$user}->{ASKED})) { $asked=$Consumptions->{$user}->{ASKED}; }
            else { $asked=0; }
            if (defined($Consumptions->{$user}->{USED})) { $used=$Consumptions->{$user}->{USED}; }
            else { $used=0; }
            if (!defined($Consumptions->{$user}->{begin})) {$Consumptions->{$user}->{begin}="No window found";}
            if (!defined($Consumptions->{$user}->{end})) {$Consumptions->{$user}->{end}="No window found";}
            print "Usage summary for user '$user' from $1 to $2:\n";
            print "-------------------------------------------------------------\n";
            printf ("%-28s %s\n","Start of the first window:",OAR::Stat::local_to_sql($Consumptions->{$user}->{begin}));
            printf ("%-28s %s\n","End of the last window:",OAR::Stat::local_to_sql($Consumptions->{$user}->{end}));
            printf ("%-28s %s ( %s)\n","Asked consumption:",$asked,OAR::Stat::get_duration($asked));
            printf ("%-28s %s ( %s)\n","Used consumption:",$used,OAR::Stat::get_duration($used));
            print "By project consumption:\n";
            $Consumptions=OAR::Stat::get_accounting_summary_byproject(OAR::Stat::sql_to_local($date1),OAR::Stat::sql_to_local($date2),$user);
            foreach my $project (keys %{$Consumptions}) {
                 print "  $project:\n";
                 $asked=$Consumptions->{$project}->{ASKED}->{$user};
                 $used=$Consumptions->{$project}->{USED}->{$user};
                 printf ("%-28s %s ( %s)\n","    Asked :",$asked,OAR::Stat::get_duration($asked));
                 printf ("%-28s %s ( %s)\n","    Used :",$used,OAR::Stat::get_duration($used));
                 if (my @last_karma=OAR::Stat::get_last_project_karma($user,$project,OAR::Stat::sql_to_local($date2))) {
                     printf ("%-28s %s\n","    Last Karma :",$last_karma[0]);
                 }
            }
    
        # All users array output
        }else{
            print <<EOS;
    User       First window starts  Last window ends     Asked (seconds)  Used (seconds)
    ---------- -------------------- -------------------- ---------------- ----------------
EOS
            foreach $login (keys %{$Consumptions}) {
                if (!defined($Consumptions->{$login}->{ASKED})) {$Consumptions->{$login}->{ASKED}=0;}
                if (!defined($Consumptions->{$login}->{USED})) {$Consumptions->{$login}->{USED}=0;}
                printf("%-10.10s %-19s  %-19s  %16s %16s\n",
                       $login,
                       OAR::Stat::local_to_sql($Consumptions->{$login}->{begin}),
                       OAR::Stat::local_to_sql($Consumptions->{$login}->{end}),
                       $Consumptions->{$login}->{ASKED},
                       $Consumptions->{$login}->{USED}
                );
            }
        }
        
    }else{
        print("Bad syntax for --accounting\n");
		OAR::Stat::close_db_connection();
        exit(1);
    }
}

### END Print Methods ###
