package HBAApi;
use System;
use strict;

#--------------------------------------------------------
#  run({cache_delay => #mins): will return the output already in the cache or call
#         hba_topo and return a new output.
#  HBAApi.timeout = 60
#  return ($err, $rc);
#  works on:
#  dis_hba    : HBA Api
#  dis_luxadm : libg_fc.so
#  dis_devinfo: devinfo
#--------------------------------------------------------


sub run {
  my($class, $arg) = @_;
  my $renv = System->get_renv();
  my $disco_level = defined $arg->{disco_level} ?  $arg->{disco_level} :
                             $renv->{disco_level};

  my $run_inband = 0;
  
  my $program = System->get_home() . "/sbin/hba_present";
			     
  system("$program > /tmp/dis_hba.out 2>&1");
  open(O, "/tmp/dis_hba.out");
  my @lines =<O>; close(O); unlink "/tmp/dis_hba.out";
  if ("@lines" =~ /Found driver/) {
    $run_inband = 1;
  }

  my($hba_list, $switch_list);
  if($run_inband){
     my $lib_flag = System->get_home()  . "/DATA/lux_lib_bad";
     if (!-f $lib_flag ) {

        my($err, $L) = $class->run1({
                    cache_delay => $renv->{"hbaapi.dis_hba.delay"} || 5,
                       command => "dis_hba" ,
                        option => $arg->{option},
                   });
        ($hba_list, $switch_list) = $class->parse($L);
     }else{
        Debug->print1("-> Skipping running HBA API inband commands due to downlevel/missing files. Please rerun ras_install.");
     }

#
# dis_luxadm
#
     my $lib_flag = System->get_home()  . "/DATA/lux_lib_bad";
     if (!-f $lib_flag ) {

        if ($disco_level > 1 ) {
           my($err, $L) = $class->run1({
                   cache_delay => $renv->{"hbaapi.dis_luxadm.delay"} || 60,
                       command => "dis_luxadm" ,
                        option => $arg->{option},
                   });
          my($lux_hbas) = $class->parse($L);
          $class->merge($hba_list, $lux_hbas);
        }
     }else{
        Debug->print1("-> Skipping running SUNWsan inband commands due to downlevel/missing files. Please rerun ras_install.");
     }
        
  }
#
# dis_devinfo
#
  if (!$arg->{option}) {
     my($err, $L) = $class->run1({
                   cache_delay => $renv->{"hbaapi.dis_devinfo.delay"} || 30,
                       command => "dis_devinfo" ,
                        option => $arg->{option},
                   });

     my($devinfo_hbas) = $class->parse($L);
     foreach my $hba (@$devinfo_hbas) {
        my $devs = $hba->{deviceInfo};
        foreach my $d (@$devs) {
           $d->{PortWWN} = lc($d->{PortWWN});
           $d->{NodeWWN} = lc($d->{NodeWWN});
        }
     }
     HBAApi->merge($hba_list, $devinfo_hbas);
  }

  open(O, ">". System->get_home() . "/DATA/HBAApi/dis_complete.hash");
  require Data::Dumper;
  print O Data::Dumper::Dumper($hba_list);
  close(O);
  return ($hba_list, $switch_list);

}
  
sub switches {
   my($class) = @_;
  my $renv = System->get_renv();

  my $run_inband = 0;
  
  my $program = System->get_home() . "/sbin/hba_present";
			     
  system("$program > /tmp/dis_hba.out 2>&1");
  open(O, "/tmp/dis_hba.out");
  my @lines =<O>; close(O); unlink "/tmp/dis_hba.out";
  if ("@lines" =~ /Found driver/) {
    $run_inband = 1;
  }
  my ($err, $L);
  if($run_inband){
     ($err, $L) = $class->run1({
                   cache_delay => $renv->{"hbaapi.dis_hba.delay"} || 5,
                       command => "dis_hba" ,
                   });
  }
  my($hba_list, $switch_list) = $class->parse($L);
  return $switch_list;
}
  

sub run1 {
  my($class, $arg) = @_;
  my $option = $arg->{option};
  my $mins   = $arg->{cache_delay} || 5;
  my $command= $arg->{command} || "dis_hba";
  my $renv   = System->get_renv();
  my $to     = $renv->{"timeout.discman"} || 500;

$DB::single=1;
  my $BASE  = System->get_home() . "/DATA/HBAApi";
  mkdir $BASE, 0777 if (!-d $BASE);
  my $FILE  = "$BASE/$command";

  my $option2 = $option;
  $option2   =~ s/\W/_/g;
  my $FILE0 = "$FILE$option2";

  my $age  = Util->getFileAge($FILE0, "m"); 
  if (!defined($age) || $age > $mins) {
     Debug->print1("-> INBAND TOPO $command $option");
     my $com = System->get_home() . "/sbin/$command $option";

     my($err, $out, $stderr) = Util->run_command($com, "", $to);
     if ($err || $stderr) {
       Debug->errNoRepeat("HBAApi_$command" => $renv->{hostname}, 24, $err);
       return ($err);
     }
     push(@$out, "#INFO command $command\n");
     open(W, ">$FILE0"); 
     print W join("\n", @$out) ; close(W);
  } 
  open(O, $FILE0);
  my @L = <O>; close(O);
  return (undef,\@L);
}

#--------------------------------------------------------
# parse(): will parse the output of hba_topo
#          and return lists of HBA and SWITCH
#--------------------------------------------------------

sub parse {
  my($class, $L) = @_;
  my (@in, %R);
  my(%COUNT, %HASH, @SWITCH, @HBA, %ADAPTER);
  $COUNT{ADAPTER}      = $COUNT{HBA}  = $COUNT{DEVICE} = -1;
  $COUNT{INTERCONNECT} = $COUNT{PORT} = -1;
  my $arg = {};

  foreach my $l (@$L) {
     chomp($l);
     if ($l =~ /^#INFO (\w+) (.+)/) {
        $arg->{$1} = $2;
        next;
     }
     if ($l =~ /^\s*ADAPTER\s*$/) {
        push(@in,"ADAPTER");
        %ADAPTER = ();
        next;

     } elsif ($l =~ /^\s*HBA\s*$/) {
        push(@in, "HBA");
        $COUNT{HBA}++; $COUNT{DEVICE}= -1;
        foreach my $el (keys %ADAPTER) {
           $HBA[$COUNT{HBA}]{"adapter_$el"} = $ADAPTER{$el};
        }
        next;

     } elsif ($l =~ /^\s*DEVICEPORT\s*$/) {
        push(@in,  "DEVICE");
        $COUNT{DEVICE}++;
        next;

     } elsif ($l =~ /^\s*INTERCONNECT\s*$/) {
        push(@in, "INTERCONNECT");
        $COUNT{INTERCONNECT}++; $COUNT{PORT}= -1;
        next;

     } elsif ($l =~ /^\s*PORT\s*$/) {
        push(@in, "PORT");
        $COUNT{PORT}++;
        next;

     } elsif ($l =~ /^\s*\/(\w+)/) { # close a section
        my $section = pop(@in);
        if ($section eq "INTERCONNECT") {
          my $sw = $COUNT{INTERCONNECT};
          $class->cleanInterconnect($SWITCH[$sw]);
        }
        next;

     } elsif (index($l, "=") > 0) {
        my($name, $val) = split(/\s*\=\s*/, $l, 2);
        if ($#in >= 0) {
          my $top = $in[$#in];
          $name = Util->ltrim($name);
          $val  = Util->rtrim($val);
          $name =~ s/\s+//g;
          if ($top eq "DEVICE") {
             my $hba = $COUNT{HBA};
             my $dev = $COUNT{DEVICE};
             $HBA[$hba]{deviceInfo}[$dev]{$name} = $val;

          } elsif ($top eq "HBA") {
             my $hba = $COUNT{HBA};
             $HBA[$hba]{$name} = $val;

          } elsif ($top eq "ADAPTER") {
             $ADAPTER{$name} = $val;

          } elsif ($top eq "INTERCONNECT") {
             my $sw = $COUNT{INTERCONNECT};
             $SWITCH[$sw]{info}{$name} = $val;

          } elsif ($top eq "PORT") {
             my $sw = $COUNT{INTERCONNECT};
             my $port= $COUNT{PORT};
             $SWITCH[$sw]{portInfo}[$port]{$name} = $val;
          }
        }
     }  
  }

# FIX SWITCHES for discman 2.3 compatibiliy

  foreach my $sw (@SWITCH) {
    my $pi = $sw->{portInfo};
    foreach my $p (@$pi) {
      if (exists $p->{PortType}) {
         $p->{sw_PortType} =  $p->{PortType};
      }
      if (exists $p->{PortNumber}) {
         $p->{sw_PortNum} =  $p->{PortNumber};
      }
    }
  }

  open(O, ">" . System->get_home() . "/DATA/HBAApi/$arg->{command}.hash");
  require Data::Dumper;
  print O Data::Dumper::Dumper(\@HBA);
  close(O);
  return (\@HBA, \@SWITCH);
}

sub cleanInterconnect {
  my($class, $switch) = @_;
  my $ip = $switch->{info}{MgmtAddress0};
  $ip =~ s/^\w+\:\/\///;
  if ($ip) {
     $ip =~ s/\///;
     $switch->{info}{IPAddress} = $ip;
  }
}

# HASH include ADAPTER info and all DEVICEPORT info
sub newHBA {
  my($class, $H) = @_;
}


# HASH include switch and all ports.
sub newSwitch {
  my($class, $H) = @_;
}


# MERGE HBAs information
sub merge {
  my($class, $hba_list, $new) = @_;

  foreach my $new_hba (@$new) {
     my $found;
     foreach my $hba (@$hba_list) {
         if ($new_hba->{NodeWWN} eq $hba->{NodeWWN}) {
            $found = 1;
            # MERGE HBA
            foreach my $el (keys %$new_hba) {
              next if ($el eq "deviceInfo");
              $hba->{$el} = $new_hba->{$el} if (!exists $hba->{$el});
            }
            my $new_devs = $new_hba->{deviceInfo};
            my $devs     = $hba->{deviceInfo};
            foreach my $new_dev (@$new_devs) {
                my $found2;
                foreach my $dev (@$devs) {
                  if ($new_dev->{PortWWN} eq $dev->{PortWWN}) {
                    $found2 = 1; 
                    # MERGE 
                    foreach my $el (keys %$new_dev) {

		       if($el eq "LogicalPath") {

		          if(exists($dev->{$el})){

			     my @LUNS = split(/\|/, $new_dev->{$el});
			     my $l;
                             for ($l = 0; $l <= $#LUNS; $l++ ) {
			        my $lun0 = $LUNS[$l];
			        if(!($dev->{$el} =~ /$lun0/)){
				   $dev->{$el} .=  "|$new_dev->{$el}";

				}
			     }
			   }else{
			      $dev->{$el} = $new_dev->{$el};
			   }
			}

                       $dev->{$el} = $new_dev->{$el} if (!exists $dev->{$el});
                    }
                    # /MERGE 
                    last;
                  }
                }
                push(@$devs, $new_dev) if (!$found2);
            }
            # /MERGE HBA
            last;
         }
     }
     push(@$hba_list, $new_hba) if (!$found) ;
  }
  my $done = 1;

}


1;
