# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html

package xCAT::PPCvitals;
use strict;
use Getopt::Long;
use xCAT::PPCcli qw(SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR);
use xCAT::PPCpower;
use xCAT::Usage;

##########################################################################
# Parse the command line for options and operands
##########################################################################
sub parse_args {

    my $request = shift;
    my $command = $request->{command};
    my $args    = $request->{arg};
    my %opt     = ();
    my @rvitals = qw(temp voltage power lcds state rackenv all);

    #############################################
    # Responds with usage statement
    #############################################
    local *usage = sub {
        my $usage_string = xCAT::Usage->getUsage($command);
        return ([ $_[0], $usage_string ]);
    };
    #############################################
    # Process command-line arguments
    #############################################
    if (!defined($args)) {
        return (usage("No command specified"));
    }
    #############################################
    # Checks case in GetOptions, allows opts
    # to be grouped (e.g. -vx), and terminates
    # at the first unrecognized option.
    #############################################
    @ARGV                     = @$args;
    $Getopt::Long::ignorecase = 0;
    Getopt::Long::Configure("bundling");

    if (!GetOptions(\%opt, qw(V|verbose))) {
        return (usage());
    }
    ####################################
    # Check for "-" with no option
    ####################################
    if (grep(/^-$/, @ARGV)) {
        return (usage("Missing option: -"));
    }
    ####################################
    # Unsupported command
    ####################################
    my ($cmd) = grep(/^$ARGV[0]$/, @rvitals);
    if (!defined($cmd)) {
        return (usage("Invalid command: $ARGV[0]"));
    }

    if ($ARGV[0] =~ /^rackenv$/) {
        if ($request->{hwtype} =~ /^hmc$/) {
            return (usage("Command $ARGV[0] is not valid when the nodes' hcp is hmc"));
        }
    }

    ####################################
    # Check for an extra argument
    ####################################
    shift @ARGV;
    if (defined($ARGV[0])) {
        return (usage("Invalid Argument: $ARGV[0]"));
    }
    ####################################
    # Set method to invoke
    ####################################
    $request->{method} = $cmd;
    return (\%opt);
}


##########################################################################
# Returns Frame voltages/currents
##########################################################################
sub enumerate_volt {

    my $exp = shift;
    my $d   = shift;

    my $mtms = @$d[2];
    my $volt = xCAT::PPCcli::lshwinfo($exp, "frame", $mtms);
    my $Rc   = shift(@$volt);

    ####################################
    # Return error
    ####################################
    if ($Rc != SUCCESS) {
        return ([ RC_ERROR, @$volt[0] ]);
    }
    ####################################
    # Success - return voltages
    ####################################
    return ([ SUCCESS, @$volt[0] ]);
}


##########################################################################
# Returns cage temperatures
##########################################################################
sub enumerate_temp {

    my $exp     = shift;
    my $frame   = shift;
    my %outhash = ();

    ####################################
    # Get cage information for frame
    ####################################
    my $filter = "type_model_serial_num,temperature";
    my $cages  = xCAT::PPCcli::lshwinfo($exp, "sys", $frame, $filter);
    my $Rc     = shift(@$cages);

    ####################################
    # Expect error
    ####################################
    if ($Rc == EXPECT_ERROR || $Rc == RC_ERROR) {
        return ([ $Rc, @$cages[0] ]);
    }
    ####################################
    # Save frame by CEC MTMS in cage
    ####################################
    foreach (@$cages) {
        my ($mtms, $temp) = split /,/;
        $outhash{$mtms} = $temp;
    }
    return ([ SUCCESS, \%outhash ]);
}

##########################################################################
# Returns refcode
##########################################################################
sub enumerate_lcds {

    my $exp      = shift;
    my $d        = shift;
    my $mtms     = @$d[2];
    my $Rc       = undef;
    my $value    = undef;
    my $nodetype = @$d[4];
    my $lpar_id  = @$d[0];
    my @refcode  = ();

    my $values = xCAT::PPCcli::lsrefcode($exp, $nodetype, $mtms, $lpar_id);
    foreach $value (@$values) {

        #Return error
        $Rc = shift @$value;
        if (@$value[0] =~ /refcode=(\w*)/) {
            my $code = $1;
            if (!$code)
            {
                push @refcode, [ $Rc, "blank" ];
            }
            else
            {
                push @refcode, [ $Rc, $code ];
            }
        }
    }

    return \@refcode;
}


##########################################################################
# Returns voltages/currents
##########################################################################
sub voltage {

    my $request = shift;
    my $hash    = shift;
    my $exp     = shift;
    my $hwtype  = @$exp[2];
    my @result  = ();
    my $text    = "Frame Voltages: ";
    my @prefix  = (
        "Frame Voltage (Vab): %sV",
        "Frame Voltage (Vbc): %sV",
        "Frame Voltage (Vca): %sV",
        "Frame Current  (Ia): %sA",
        "Frame Current  (Ib): %sA",
        "Frame Current  (Ic): %sA",
    );

    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {
            #################################
            # No frame command on IVM
            #################################
            if ($hwtype eq "ivm") {
                push @result, [ $name, "$text Not available", 1 ];
                next;
            }
            #################################
            # Voltages available in frame
            #################################
            if (@$d[4] ne "bpa") {
                push @result, [ $name, "$text Only available for BPA", 0 ];
                next;
            }
            my $volt = enumerate_volt($exp, $d);
            my $Rc = shift(@$volt);

            #################################
            # Output error
            #################################
            if ($Rc != SUCCESS) {
                push @result, [ $name, "$text @$volt[0]", $Rc ];
                next;
            }
            #################################
            # Output value
            #################################
            my @values = split /,/, @$volt[0];
            my $i = 0;

            foreach (@prefix) {
                my $value = sprintf($_, $values[ $i++ ]);
                push @result, [ $name, $value, $Rc ];
            }
        }
    }
    return (\@result);
}


##########################################################################
# Returns temperatures for CEC
##########################################################################
sub temp {

    my $request = shift;
    my $hash    = shift;
    my $exp     = shift;
    my $hwtype  = @$exp[2];
    my @result  = ();
    my %frame   = ();
    my $prefix  = "System Temperature:";

    #########################################
    # Group by frame
    #########################################
    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {
            my $mtms = @$d[5];

            #################################
            # No frame commands for IVM
            #################################
            if ($hwtype eq "ivm") {
                push @result, [ $name, "$prefix Not available (No BPA)", 0 ];
                next;
            }
            #################################
            # Temperatures not available
            #################################
            if (@$d[4] !~ /^(fsp|cec|lpar)$/) {
                my $text = "$prefix Only available for CEC/LPAR";
                push @result, [ $name, $text, 0 ];
                next;
            }
            #################################
            # Error - No frame
            #################################
            if ($mtms eq "0") {
                push @result, [ $name, "$prefix Not available (No BPA)", 0 ];
                next;
            }
            #################################
            # Save node
            #################################
            $frame{$mtms}{$name} = $d;
        }
    }

    while (my ($mtms, $h) = each(%frame)) {
        #################################
        # Get temperatures this frame
        #################################
        my $temp = enumerate_temp($exp, $mtms);
        my $Rc   = shift(@$temp);
        my $data = @$temp[0];

        while (my ($name, $d) = each(%$h)) {
            my $mtms = @$d[2];

            #############################
            # Output error
            #############################
            if ($Rc != SUCCESS) {
                push @result, [ $name, "$prefix $data", $Rc ];
                next;
            }
            #############################
            # CEC not in frame
            #############################
            if (!exists($data->{$mtms})) {
                push @result, [ $name, "$prefix CEC '$mtms' not found", 1 ];
                next;
            }
            #############################
            # Output value
            #############################
            my $cel   = $data->{$mtms};
            my $fah   = ($cel * 1.8) + 32;
            my $value = "$prefix $cel C ($fah F)";
            push @result, [ $name, $value, $Rc ];
        }
    }
    return (\@result);
}


##########################################################################
# Returns system power status (on or off)
##########################################################################
sub power {
    return (xCAT::PPCpower::state(@_, "Current Power Status: ", 1));
}

##########################################################################
# Returns system state
##########################################################################
sub state {
    return (xCAT::PPCpower::state(@_, "System State: "));
}
###########################################################################
# Returns system LCD status (LCD1, LCD2)
##########################################################################
sub lcds {
    my $request  = shift;
    my $hash     = shift;
    my $exp      = shift;
    my $hwtype   = @$exp[2];
    my @result   = ();
    my $text     = "Current LCD:";
    my $prefix   = "Current LCD%d: %s";
    my $rcode    = undef;
    my $refcodes = undef;
    my $Rc       = undef;
    my $num      = undef;
    my $value    = undef;

    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {

            #Support HMC only
            if ($hwtype ne 'hmc') {
                push @result, [ $name, "$text Not available(NO HMC)", 1 ];
                next;
            }
            $refcodes = enumerate_lcds($exp, $d);
            $num = 1;
            foreach $rcode (@$refcodes) {
                $Rc = shift(@$rcode);
                $value = sprintf($prefix, $num, @$rcode[0]);
                push @result, [ $name, $value, $Rc ];
                $num = $num + 1;
            }
        }
    }
    return \@result;
}


##########################################################################
# Returns all vitals
##########################################################################
sub all {

    my @values = (
        @{ temp(@_) },
        @{ voltage(@_) },
        @{ state(@_) },
        @{ power(@_) },
        @{ lcds(@_) },
    );

    my @sorted_values = sort { $a->[0] cmp $b->[0] } @values;
    return (\@sorted_values);
}


1;

