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

package xCAT::FSPinv;
use strict;
use Getopt::Long;
use xCAT::PPCcli qw(SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR);
require xCAT::Usage;
require xCAT::PPCinv;
require xCAT::FSPUtils;
use XML::Simple;

#use Data::Dumper;

##########################################
# Maps fsp-api attributes to text
##########################################
my @licmap = (
    [ "ecnumber",                     "Release Level  " ],
    [ "activated_level",              "Active Level   " ],
    [ "installed_level",              "Installed Level" ],
    [ "accepted_level",               "Accepted Level " ],
    [ "curr_ecnumber_a",              "Release Level A" ],
    [ "curr_level_a",                 "Level A        " ],
    [ "curr_power_on_side_a",         "Current Power on side A" ],
    [ "curr_ecnumber_b",              "Release Level B" ],
    [ "curr_level_b",                 "Level B        " ],
    [ "curr_power_on_side_b",         "Current Power on side B" ],
    [ "curr_ecnumber_primary",        "Release Level Primary" ],
    [ "curr_level_primary",           "Level Primary  " ],
    [ "curr_power_on_side_primary",   "Current Power on side Primary" ],
    [ "curr_ecnumber_secondary",      "Release Level Secondary" ],
    [ "curr_level_secondary",         "Level Secondary" ],
    [ "curr_power_on_side_secondary", "Current Power on side Secondary" ]
);

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

    #    xCAT::PPCinv::parse_args(@_);
    my $request = shift;
    my $command = $request->{command};
    my $args    = $request->{arg};
    my %opt     = ();

    #    my @rinv    = qw(bus config model serial firm all);
    my @rinv = qw( deconfig firm );

    #############################################
    # 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 x))) {
        return (usage());
    }
    ####################################
    # Check for "-" with no option
    ####################################
    if (grep(/^-$/, @ARGV)) {
        return (usage("Missing option: -"));
    }
    ####################################
    # Unsupported command
    ####################################
    my ($cmd) = grep(/^$ARGV[0]$/, @rinv);
    if (!defined($cmd)) {
        return (usage("Invalid command: $ARGV[0]"));
    }
    ####################################
    # Check for an extra argument
    ####################################
    shift @ARGV;
    if (defined($ARGV[0])) {
        return (usage("Invalid Argument: $ARGV[0]"));
    }
    if (exists($opt{x}) and $cmd !~ /^deconfig$/) {
        return (usage("Option '-x' can't work with '$cmd'"));
    }
    ####################################
    # Set method to invoke
    ####################################
    $request->{method} = $cmd;
    return (\%opt);


}




##########################################################################
# Returns FSP/BPA firmware information
##########################################################################
sub firmware {

    my $request = shift;
    my $hash    = shift;
    my @result;

    # print "in FSPinv \n";
    #print Dumper($request);
    #print Dumper($hash);

    ####################################
    # FSPinv with firm command is grouped by hardware control point
    # In FSPinv, the hcp is the related fsp.
    ####################################

    # Example of $hash.
    #VAR1 = {
    #	          '9110-51A*1075ECF' => {
    #                                   'Server-9110-51A-SN1075ECF' => [
    #    	                                                          0,
    #		                                                          0,
    #                           									  '9110-51A*1075ECF',
    #							                            		  'fsp1_name',
    #				                            					  'fsp',
    #									                               0
    #									                               ]
    #					                }
    # 	   };

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

            #####################################
            # Command only supported on FSP/BPA/LPARs
            #####################################
            if (@$d[4] !~ /^(cec|frame|fsp|bpa|lpar|blade)$/) {
                push @result,
                  [ $name, "Information only available for CEC/FSP/Frame/BPA/LPAR", RC_ERROR ];
                next;
            }
            #################
            #For support on  Lpars, the flag need to be changed.
            ##########
            if (@$d[4] eq "lpar") {
                @$d[4] = "fsp";
                @$d[0] = 0;
            }
            my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "list_firmware_level");
            my $Rc   = @$values[2];
            my $data = @$values[1];

            #print "values";
            #print Dumper($values);
            #####################################
            # Return error
            #####################################
            if ($Rc != SUCCESS) {
                push @result, [ $name, $data, $Rc ];
                next;
            }

            #####################################
            # Format fsp-api results
            #####################################
            my $val;
            foreach $val (@licmap) {
                if ($data =~ /@$val[0]=(\w+)/) {
                    push @result, [ $name, "@$val[1]: $1", $Rc ];
                }
            }
        }
    }
    return (\@result);
}


##########################################################################
# Returns firmware version
##########################################################################
sub firm {
    return (firmware(@_));
}

##########################################################################
# Returns serial-number
##########################################################################
sub serial {
}

sub vpd {
}

sub bus {
}

sub deconfig {

    my $request = shift;
    my $hash    = shift;
    my @result;

    # print "in FSPinv \n";
    #print Dumper($request);
    #print Dumper($hash);

    ####################################
    # FSPinv with deconfig command is grouped by hardware control point
    # In FSPinv, the hcp is the related fsp.
    ####################################

    # Example of $hash.
    #VAR1 = {
    #	          '9110-51A*1075ECF' => {
    #                                   'Server-9110-51A-SN1075ECF' => [
    #    	                                                          0,
    #		                                                          0,
    #                           									  '9110-51A*1075ECF',
    #							                            		  'fsp1_name',
    #				                            					  'fsp',
    #									                               0
    #									                               ]
    #					                }
    # 	   };

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

            #####################################
            # Command only supported on FSP/BPA/LPARs
            #####################################
            if (@$d[4] !~ /^(cec|fsp)$/) {
                push @result,
                  [ $name, "Deconfigured resource information only available for CEC/FSP", RC_ERROR ];
                next;
            }
            #################
            #For support on  Lpars, the flag need to be changed.
            ##########
            #if(@$d[4] eq "lpar")	{
            #    @$d[4] = "fsp";
            #    @$d[0] = 0;
            #}
            my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "get_cec_deconfigured");
            my $Rc   = @$values[2];
            my $data = @$values[1];

            #print "values";
            #print Dumper($values);
            #####################################
            # Return error
            #####################################
            if ($Rc != SUCCESS) {
                push @result, [ $name, $data, $Rc ];
                next;
            }


            #####################################
            # Format fsp-api results
            #####################################
            #my $decfg = XMLin($data);
            my $decfg;
            eval {
                $decfg = XMLin($data);
            };
            if ($@) {
                push @result, [ $name, "Error: there are some unreadable XML data from the firmware. It can't be parsed by 'xcatd'.", -1 ];
                return (\@result);
            }
            if (exists($request->{opt}->{x})) {
                push @result, [ $name, "\n" . $data, -1 ];
                next;
            }

            #print "decfg";
            #print Dumper($decfg);
            my $node = $decfg->{NODE};
            if (defined($node) && exists($node->{Location_code})) {
                my $Call_Out_Hardware_State;
                my $Call_Out_Method;
                my $Location_code;
                my $RID;
                my $TYPE;
                my $dres;
                if (ref($node->{GARDRECORD}) eq "ARRAY") {
                    $dres = $node->{GARDRECORD};
                } elsif (ref($node->{GARDRECORD}) eq "HASH") {
                    push @$dres, $node->{GARDRECORD};
                } else {
                    push @result, [ $name, "NO Deconfigured resources", 0 ];
                    return (\@result);
                }
                push @result, [ $name, "Deconfigured resources", 0 ];
                push @result, [ $name, "Location_code                RID   Call_Out_Method    Call_Out_Hardware_State   TYPE", 0 ];
                push @result, [ $name, "$node->{Location_code}         $node->{RID}", 0 ];

                #foreach my $unit(@{$node->{GARDRECORD}}) {
                foreach my $unit (@$dres) {
                    while (my ($key, $unit3) = each(%$unit)) {

                        if ($key eq "GARDUNIT") {
                            if (ref($unit3) eq "HASH") {
                                $Call_Out_Hardware_State = $unit3->{Call_Out_Hardware_State};
                                $Call_Out_Method = $unit3->{Call_Out_Method};
                                $Location_code   = $unit3->{Location_code};
                                $RID             = $unit3->{RID};
                                $TYPE            = $unit3->{TYPE};

                                push @result, [ $name, "$Location_code     $RID    $Call_Out_Method            $Call_Out_Hardware_State            $TYPE", 0 ];
                            } elsif (ref($unit3) eq "ARRAY") {

                                foreach my $unit4 (@$unit3) {
                                    $Call_Out_Hardware_State = $unit4->{Call_Out_Hardware_State};
                                    $Call_Out_Method = $unit4->{Call_Out_Method};
                                    $Location_code = $unit4->{Location_code};
                                    $RID           = $unit4->{RID};
                                    $TYPE          = $unit4->{TYPE};
                                    push @result, [ $name, "$Location_code     $RID    $Call_Out_Method            $Call_Out_Hardware_State            $TYPE", 0 ];
                                }
                            }
                        }
                    }


                }

            } else {
                push @result, [ $name, "NO Deconfigured resources", 0 ];
            }


        }
    }
    return (\@result);


}

##########################################################################
# Returns machine-type-model
##########################################################################
sub model {
}

##########################################################################
# Returns all inventory information
##########################################################################
sub all {

    my @result = (
        @{ deconfig(@_) },
        @{ firmware(@_) }
    );
    return (\@result);
}


1;


