#!/usr/bin/perl -w
#
# Show, create, modify or delete admission rules
#

use strict;
use warnings;
use OAR::IO;
use OAR::Conf qw(init_conf get_conf is_conf);
use Getopt::Long;
use OAR::Version;
use OAR::Tools;
use File::Temp qw/ tempfile /;

my $base;
my $action;
my $showopt="short";
my $rule_id;
my $priority;
my $enabled;
my $edit;
my $filename;

sub usage() {
    my ($command) = $0 =~ /([^\/]+)$/;
    my $usage = <<EOS;
Usage: 
  $command --show-all [options]
    show all rules
  $command --show <rule-id> [options]
    show rule #rule-id
  $command --new [options]
    create a new rule
  $command --modify <rule-id> [options]
    modify rule #rule-id
  $command --delete <rule-id>
    delete rule #rule-id

  Show, create, modify or delete admission rules.

Options:
  -S, --show-all
    -Y, --enabled              show enabled admission rules only
    -N, --disabled             show disabled admission rules only

  -S, --show-all
  -s, --show [rule-id]
    -I, --id                   show #rule-id only
    -f, --full                 show full script
    -H, --no-header            show script only
    -w, --to-file [filename]   write script to file (%% replaced by #rule-id)

  -n, --new
  -m, --modify <rule-id>
    -e, --edit [cmd]           edit script using editor or cmd if provided
    -r, --from-file <filename> read script from file instead of running editor
    -P, --priority <priority>  set priority for rule
    -Y, --enabled              enable admission rule
    -N, --disabled             disable admission rule

  -d, --delete <rule-id>
    no option

  -h, --help                   print this
  -V, --version                print OAR version
  
EOS
    return $usage;
}

sub print_rule($$) {
    my $r = shift;
    my $showopt = shift;
    my $str_enabled = ($r->{enabled} eq "NO")?"DISABLED/":"";
    if ($showopt eq "ids") {
        print $r->{id}."\n";
    }
    elsif ($showopt eq "short") {
        my @lines = (split("\n",$r->{rule}),"");
        print <<EOS;
--- ($str_enabled$r->{priority}) RULE #$r->{id}
$lines[0]
---
EOS
    }
    elsif ($showopt eq "full") {
        print <<EOS;
--- ($str_enabled$r->{priority}) RULE #$r->{id}
$r->{rule}
---
EOS
    }
    elsif ($showopt eq "no-header") {
        if (defined($filename)) {
            my $f;
            my $fh;
            if ($filename eq "") {
                ($fh, $f) = tempfile("OAR-admission-rule.$r->{id}.XXXXXX", TMPDIR => 1, UNLINK => 0) or die "Error: cannot create temp file\n";
            } else {
                $f = $filename;
                $f =~ s/%%/$r->{id}/;
                (-e $f) and die "Error: cannot write rule #$r->{id} to file $f, file already exists\n";
                open($fh, ">", $f) or die "Error: could not open $f\n";
            }
            print $fh $r->{rule};
            close($fh);
            print "Admission rule #$r->{id} written to $f\n";
        }
        else {
            print $r->{rule};
        }
    }
}

sub get_all_from_db() {
    # $enabled is the global variable here (--Y|--N)
    my @rules = OAR::IO::list_admission_rules($base, $enabled);
    return @rules;
}

sub get_from_db($) {
    my $rule_id = shift;
    my $r = OAR::IO::get_admission_rule($base,$rule_id);
    if (not defined($r)) {
        die "Error: could not retrieve admission rule from database\n";
    }
    return $r;
}

sub edit($$) {
    my $rule_id = shift;
    my $rule = shift;
    my ($fh, $filename) = tempfile("OAR-admission-rule.".((defined($rule_id))?$rule_id:"new")."XXXXXX", TMPDIR => 1, UNLINK => 1) or die "Error: cannot create temp file\n";
    if (defined($rule)) {
        print $fh $rule or die "Error: cannot write current rule to temp file\n";
    }
    close $fh or die "Error: cannot close temp file\n";
    if ($edit eq "") { 
        if (exists($ENV{EDITOR})) {
            $edit = $ENV{EDITOR};
        }
        elsif (qx/which editor/ ne "") {
            $edit = "editor";
        }
        else {
            $edit = "vi";
        }
    }
    system($edit, $filename);
    if ($? == -1) {
        die "Error: failed to execute: $!\n";
    }
    elsif ($? & 127) {
        die "Editor process died with signal " . ($? & 127) . "\n";
    }
    elsif (($? >> 8) != 0) {
        die "Editor process exited with value " . ($? >> 8) . "\n";
    }
    return $filename;
}

sub read_file($) {
    my $filename = shift;
    my $fh;
    open($fh, "<", $filename) or die "Error: could not open $filename\n";
    my $rule;
    { local $/ = undef; $rule = <$fh>; }
    close $fh or die "Error: cannot close file\n";
    return $rule;
}

sub update_rule($$$$) {
    my $rule_id = shift;
    my $priority = shift;
    my $enabled = shift;
    my $rule = shift;
    my $r = OAR::IO::update_admission_rule($base, $rule_id, $priority, $enabled, $rule);
    defined($r) or die "Error: could not update rule #$rule_id in database\n";
}

sub add_rule($$$) {
    my $priority = shift;
    my $enabled = shift;
    my $rule = shift;
    my $r = OAR::IO::add_admission_rule($base, $priority, $enabled, $rule);
    defined($r) or die "Error: could not add rule in database\n";
    return($r);
}

sub delete_rule($) {
    my $rule_id = shift;
    my $r = OAR::IO::delete_admission_rule($base, $rule_id);
    defined($r) or die "Error: admission rule #$rule_id could not be deleted\n";
}

Getopt::Long::Configure("gnu_getopt");

GetOptions( 
            "show-all|S" => sub { $action = shift; },
            "show|s=i" => sub { $action = shift; $rule_id = shift; },
            "new|n" => sub { $action = shift; },
            "modify|m=i" => sub { $action = shift; $rule_id = shift; },
            "delete|d=i" => sub { $action = shift; $rule_id = shift; },
            "id|I" =>  sub { $showopt = shift; },
            "no-header|H" =>  sub { $showopt = shift; },
            "full|f" =>  sub { $showopt = shift; },
            "edit|e:s" => sub { shift; $edit = shift; },
            "from-file|r=s" => sub { shift; $filename = shift; },
            "to-file|w:s" => sub { shift; $filename = shift; },
            "priority|P=i" => \$priority,
            "enabled|Y" => sub { $enabled = 1; },
            "disabled|N" => sub { $enabled = 0; },
            "version|V" => sub { print("OAR version : ".OAR::Version::get_version()."\n"); exit(0) },
            "help|h"   => sub { print usage(); exit(0); }
          ) or die "\n".usage();

defined($action) or die "Error: syntax error\n\n".usage();

$base = OAR::IO::connect();

if ($action eq "show-all") {
    foreach my $r (get_all_from_db()) {
        print_rule($r, $showopt);
    }
}
elsif ($action eq "show") {
    (defined $enabled) and die "Error: syntax error\n\n".usage();
    if (defined($filename)) {
        $showopt="no-header";
    } 
    my $r = get_from_db($rule_id);
    print_rule($r, $showopt);
}
elsif ($action eq "new") {
    my $r;
    my $rule = "";
    if (defined($edit) and defined($filename)) {
        die "Error: you cannot use both --edit and --from-file at a same time\n\n".usage();
    }
    if (not defined($edit) and not defined($filename)) {
        $edit="";
    }
    if (not defined($filename)) {
        $filename = edit($rule_id, $rule);
    }
    $rule = read_file($filename);
    my $rule_id = add_rule(defined($priority)?$priority:0, defined($enabled)?$enabled:1, $rule);
    print "New admission rule created: #$rule_id\n";
}
elsif ($action eq "modify") {
    my $r;
    my $rule = "";
    $r = get_from_db($rule_id);
    $rule = $r->{rule};
    (defined($edit) and defined($filename)) and die "Error: you cannot use both --edit and --from-file at a same time\n\n".usage();
    if (defined($edit)) {
        $filename = edit($rule_id, $rule);
    }
    if (defined($filename)) {
        $rule = read_file($filename);
    }
    (defined($priority) or defined($enabled) or defined($edit) or defined($filename)) or die "Error: no modification was requested\n\n".usage();
    update_rule($rule_id, defined($priority)?$priority:$r->{priority}, defined($enabled)?$enabled:(($r->{enabled} eq 'NO')?0:1), $rule);
    print "Admission rule modified: #$rule_id\n";
}
elsif ($action eq "delete") {
    delete_rule($rule_id);
    print "Admission rule deleted: #$rule_id\n";
}
else {
    die "Unknown action !\n";
}

OAR::IO::disconnect($base);

exit(0);

__END__
