# 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;