package TO::Applet;
use TO;
use System;
use PDM::ConfigFile;
use Modules;
use strict;
use Grouping;
sub leaf { 0 }

sub statusFunc {
  my($type, $name, $arg, $node) = @_;
  my($enc, $port, $disk, $other, $d, $x);
  my($encInfo, $portInfo, $diskInfo, $otherInfo, $i, $u) ;

  my $Tests  = $arg->{tests};

  my $sf = "TO::" . uc($type);

  if ($arg->{state}) {
    ($enc, $port, $disk, $other, $encInfo,
          $portInfo, $diskInfo, $otherInfo) = $sf->statusFunc($name, $arg->{state}, $arg, $node);
  }

  my $pis = $Tests->{$name};
  my($testInfo);

  if ($pis) {
     my($running, $fail, $ok) = (0,0,0);
     foreach my $pi (@$pis) { # processInfo
        my $st = $pi->status();
        if ($st eq "O") {
           $running++;
        } elsif ($st eq "S") {
           $ok++;
        } else {
           $fail++;
        }
     }
     $testInfo = "$running,$ok,$fail" ;
  }
  return ($enc, $port, $disk, $other, $encInfo,
          $portInfo, $diskInfo, $otherInfo, $testInfo);
}


#  genAppletData( { topo   => $topo, HIST  => 1, NOFUNC => 1,
#                   FILTER =>      , clear => 1, zoom   => 2    });
#
#  $to is optional
sub genAppletData {
  my($class, $q, $parms_format, $linkInfo, $to)= @_;
  my($o1);
  my $sf = $q->{NOFUNC}? undef: \&statusFunc;  # skip statusFunc

  if ($q->{HIST}) {
     $to = Util->deserialize("topohist/$q->{topo}");
  } elsif (!$to) {
     $to = TO->readExistingTopo($q->{topo});    # merge topos
     if (!$to) {
       print "getApplet: $TO::ERR" if ($TO::ERR);
       return;
     }
  }

# merge states
  my($State) = State->read();  # merge state
  require Scheduler;
  my($Tests) = Scheduler->processMap("ST"); # hash of readInfo lists

# q->{email} decides if the applet is inside an email or not.

  Modules->load("TO");
  my($zoom,$orient, $objs, $running, $format, $dev_cnt) = TO::Applet::stream($to, {
                      name => $q->{topo},
		      zoom => $q->{zoom},
                  history  => $q->{HIST},
                statusFunc => $sf,
                     tests => $Tests,
                  linkInfo => $linkInfo,
                    filter => $q->{FILTER},
                     state => $State,
                  fibreLog => [],
                  counters => undef, clear => $q->{clear}});

  if ($parms_format) {
    my($out2) = "";
    my($cnt) = 1;
    $zoom = $q->{zoom} if (defined($q->{zoom}));
    if ($zoom >= 0) {
      $out2 .= "<param name=zoom value=\"$zoom\">\n";
    }
    $out2 .= "<param name=orientation value=\"$orient\">\n";
    $out2 .= "<param name=xyLayout value=\"$format\">\n" if ($format);
    foreach my $o (split(/\n/, $objs) ) {
      $o1 = $o; $o1 =~ s/'/ /g;
      $out2 .= "<param name=object$cnt value=\"$o1\">\n";
      $cnt++;
    }
    return ($out2, $running, $zoom);
  } elsif (wantarray) {
    return ($objs, $to, $dev_cnt, $zoom, $orient );
  } else {
    return $objs;
  }
}



# ->TO::Applet::stream($to, 
#                { status => \&function,  clear => 1, fibreLog => $errorlist });
#
my (%STAT, %LK, $sw_cnt);

sub stream {
 my($to, $arg) = @_;
 my($l, %XY, @a);
 my($encStatus, $portStatus, $diskStatus, $otherStatus);
 my($encInfo, $portInfo, $diskInfo, $otherInfo) ;
 $sw_cnt = -1;

 my($statusFunc) = $arg->{statusFunc};
 my($linkInfo)   = $arg->{linkInfo};

 my($writeFlag) = 0;
 my($fibrelog) = $arg->{fibreLog};
 my($history) = $arg->{history}; # use tmphist
 my($FILTER) = $arg->{filter};
 %STAT = ();
 %LK = ();
 my $ruser = System->get_ruser();
 my $exclude = "DS";

 my($renv, $devs, $config_hosts, $notifs) = PDM::ConfigFile->read;

 my %SOL;
 $SOL{$renv->{hostname}} = $renv->{solution_model} if ($renv->{solution} =~ /^se/);
 foreach my $h (@$config_hosts) {
    $SOL{$h->{hostname}} = $h->{solution_model} if ($h->{solution} =~ /^se/);
 }
 my(%SW);
 foreach my $sw (@$devs) {
    $SW{$sw->{wwn}} = $sw->{name};
 }
 my($out) = "";
 my($outL) = "";
 my($outI) = ""; # internal links in switches

# exclude =DS : exclude systems
# exclude =C  : exclude rack Components

 #$to->seCrossConnect();

 my($switches) = $to->switchList(  {mgmtExclude => $exclude});
 my($storages) = $to->storageList({mgmtExclude => $exclude});
 my($hosts)    = $to->hostList(    {mgmtExclude => $exclude});

 my($clear)      = $arg->{clear};
 $l = undef;
 @a = ();
 %XY = ();

 my($F) = System->get_home() . "/DATA/tmp" . ($history? "hist":"") . "/topoxy_$arg->{name}$FILTER$ruser->{isCustomer}";
 my $zoom = -1;
 my $default_orient = 'x';
 my ($t1,$t2, $format);
 if (open(O, $F)) {
   while ($l = <O>) {
     chop($l);
     @a = split(/\|/, $l);
     if (substr($a[0] ,0,2) eq "z=") {
        ($t1, $zoom, $t2, $default_orient) = split(/[\=\|]/, $l);
     } else {
       $XY{$a[3]} = [$a[4],$a[5]];
     }
   }
   close(O);
 } else {
   $zoom = $arg->{zoom} || 3;
   $format = $renv->{solution} eq "N" ? "D" : "R";   # default or rack
 }
 my($cnt) = 1;
 my($curr_x, $curr_y, $x, $s, $ports, $name, $x0, $y0, $h, $label2, $from, $to2, $y2);
 my($save_cnt, $y, @b, $leaf_cnt);

 $curr_x       = 150;
 $curr_y       =  $renv->{solution} eq "se2" ? 150: 25;
 my $curr_ve_x = 250;
 my $curr_ve_y =  20;
 my $ST_XINC   = 150;

 my $hubs      = $to->hubList();
 my $orient    = "x";

 my(%MAP);
 if (0) {
 for ($x=0; $x <= $#$switches; $x++) {
    my($sw) = $switches->[$x];
    #$orient = "h" if (substr($sw->{info}{name},0,2) eq "ve");
    $MAP{$sw->{info}{name}} = "s$x";
 }
 for ($x=0; $x <= $#$hubs; $x++) {
    my($sw) = $hubs->[$x];
    $MAP{$sw->{info}{name}} = "u$x";
 }
 for ($x=0; $x <= $#$hosts; $x++) {
    my($sw) = $hosts->[$x];
    $MAP{$sw->{info}{name}} = "h$x";
 }
 for ($x=0; $x <= $#$storages; $x++) {
    my($sw) = $storages->[$x];
    $MAP{$sw->{info}{name}} = "a$x";    # a5k:wwn => 2
 }
 }
 my($FILTER_LIST,$GR, $GR_MAP, $ZONE_ID);
 my($FILTER_TYPE, $FILTER_ID) = split(/\:/,$FILTER,2);

 ($ZONE_ID, $FILTER_LIST, $GR, $GR_MAP) = $to->filter_list($FILTER);

#
# SWITCHES
#
 my($graySwitches);
 my $grayPorts = {};
 my($hook_cnt) = 0;
 my $GRL = {};
 my $GRL2 = {};
 my ($running, $zone_filter);

 for ($x=0; $x <= $#$switches; $x++) {
    $s     = $switches->[$x];
    next if (substr($s->{info}{type},0,4) eq "hbas" && !$renv->{show_hba_devices});
    if ($FILTER_TYPE eq "zone") {
       my ($port_list, $node_list) = $s->includeZone($FILTER_ID);
       next if (!$port_list);
       $zone_filter .= $node_list;
    }
    next if ($FILTER_LIST && index($FILTER_LIST, $s->name()) < 0);
    my($o1,$o2, $run) = &draw_switch($orient,$linkInfo, $to, $s, 
                 \$curr_x, \$curr_y, \$curr_ve_x, \$curr_ve_y,
                 $ZONE_ID, $FILTER_ID, $GR, $GRL, $GRL2, $grayPorts, \$graySwitches,
                 \%SW, \%XY, $arg, $statusFunc, 0, \$leaf_cnt, \$outI);
    $out .= $o1;
    $outL .= $o2;
    $running = 1 if ($run);

 }
 $FILTER_LIST = $zone_filter if ($FILTER_TYPE eq "zone");

 # look for switches that the included switches are pointing to and add them
 # to show primary switches

 if (0) {
 foreach my $sw (keys %$grayPorts) {
    if (index($graySwitches, $sw) < 0) {
        # print "found gray: $sw<br>";
        my $s = $to->nodeByName($sw);
        next if (!$s);
        my($o1,$o2) = &draw_switch($orient,$linkInfo, $to, $s, \$curr_x, \$curr_y, \$curr_ve_x, \$curr_ve_y, 
                       $ZONE_ID, $FILTER_ID, $GR, $GRL, $GRL2, $grayPorts, \$graySwitches,
                      \%SW, \%XY, $arg, $statusFunc, 1, \$leaf_cnt, \$outI);
        $out .= $o1;
        $outL .= $o2;
    }
 }}

 my($hbas, $hInfo,  $map);
 $portInfo = undef;
 #$curr_x = 150; $curr_y = 20;

#
# HOSTS
#
 $curr_x = 20; $curr_y = 50;
 $encInfo = "";
 my $testInfo;
 
 for ($x=0; $x <= $#$hosts; $x++) {
    $h     = $hosts->[$x];
    next if ($FILTER_TYPE eq "zone" && $FILTER_LIST && index($FILTER_LIST, $h->name()) < 0);
    next if ($FILTER_LIST && index($FILTER_LIST, $h->name()) < 0);

    $hbas  = $hosts->[$x]{port};
    $hInfo = $hosts->[$x]{portInfo};
    $ports = $#$hbas + 1;
#    $ports = 8 if ($ports < 8);
    $name  = $h->{info}{name};   # host:name
    my ($rest, $name_only) = split(/\:/, $h->{info}{name}, 2);
    
    my $label = $h->{info}{model};
    if ($SOL{$name_only}) {
      $label = $SOL{$name_only} . "-SP";
      $h->{info}{se} = 1;
    } else {
      my $ixx = index($label, ",");
      $label = substr($label,$ixx+1) if ($ixx > 1);
      $label =~ s/000$/K/;
    }
  
    my $name0 = $name_only;
    my $in1 = index($name0, ".");
    $name0 = substr($name0, 0, $in1) if ($in1 > 0);

    $x0 = $XY{$name}[0] ;
    $y0 = $XY{$name}[1] ;
    if (!$x0) {
      $x0 = $curr_x; $y0 = $curr_y;
      $curr_y += 130;
    }
    $encStatus = $portStatus = $portInfo = "";
    my($portStatus2,$portInfo2);
    if ($statusFunc) {
      ($encStatus, $portStatus2, $diskStatus, $otherStatus,
       $encInfo, $portInfo2, $diskInfo, $otherInfo, $testInfo) = 
            &$statusFunc("Host", $name, $arg, $h);
    }
    my @P2 = split(/,/, $portStatus2);
    my @I2 = split(/,/, $portInfo2);
    for ($y=0; $y <= $#$hbas; $y++) {
       my($p1, $p2, $p3) =  split(/:/, $hInfo->[$y]{BoardSlotPort});
       my($p4) = $hInfo->[$y]{LogicPath};
       my $in;
       $in = "[";
       $in .= "Board:$hInfo->[$y]{IOBoardSlot} " if (exists $hInfo->[$y]{IOBoardSlot});
       $in .= "Slot:$hInfo->[$y]{SlotOnIO} " if (exists $hInfo->[$y]{SlotOnIO});
       $in .= "Port:$hInfo->[$y]{CardPort} " if (exists $hInfo->[$y]{CardPort});
       $in .= "]";
       $in .= "Path:$p4";
       if ($hbas->[$y]) {
         $portStatus .= "$P2[$y],";
         $portInfo .= "$in $I2[$y],";
       } else {
         $portStatus .=  "-1,";
         $portInfo .= "$in,";
       }
    }
    chop($portStatus);
    chop($portInfo) if ($portInfo);

    my $rc =  &group_sev($FILTER_ID, $GR, $GRL, $name, "", $portStatus, "", "");
    next if ($rc == 1);
    $running = 1 if (&running($testInfo));
    if ($rc >= 2) {
       # $out .= "h$x|v|h,Sun,$ports|$name=$name0,|$x0|$y0||$portStatus|||$encInfo|$portInfo||otherInfo|0,0,0\n";
       my $ic = $h->iconLayout();
       $out .= "$name|v|h,Sun,$ports,,$ic|$name=$name0,$label|$x0|$y0|$encStatus|$portStatus||$otherStatus|$encInfo|$portInfo||$otherInfo|0,0,0\n";
    }

#07|e|f|0|0|0|cable|06,1|03,6||

    for ($y=0; $y <= $#$hbas; $y++) {
        if ($hbas->[$y]) {
           @b = split(/:/, $hbas->[$y]);
           my $tn = $to->nodeByName($hbas->[$y]);
           next if (!$tn);
           my $rname = $hInfo->[$y]{RegisterName} ;
           # $from = "h$x,$y";
           $from = "$name,$y";
           # $to2   = $MAP{"$b[0]:$b[1]"} . ",$b[2]";
           $to2   = "$b[0]:$b[1],$b[2]";
           #next if ($tn->class0() ne "switch" && $renv->{use_hub});
           if (substr($rname, 0, 6) eq "pseudo") {
#             $out .= "00|e|M|2/pl,pseudo-link: This link is just a place-holder|$from|$to2\n";
           } else {
             # $out .= &add_link($from, $to2, $hook_cnt, $x0+60, $y0+$y*9);
             if ($GR) {
               $outL .= &group_lnk($FILTER_ID,$GRL2, $GR,  $name, $y, "$b[0]:$b[1]", $b[2]);
             } else {
               next if (&link_done($from, $to2));
               if ($linkInfo and exists($linkInfo->{$to2}) ) {
                 $outL .= "00|e|E|$linkInfo->{$to2},K,_,1|$from|$to2\n";
               } elsif (substr($rname,0,4) eq 'scsi')  {
                 $outL .= "00|e|S|scsi|$from|$to2\n";
               } else {
                 $outL .= "00|e|f||$from|$to2\n";
               }
             }
           }
           $hook_cnt++;
        }
    }
 }

#
# STORAGES
#
 my($type, $dInfo, $pInfo);
 $diskInfo = $testInfo = undef;
 $curr_x = $leaf_cnt == 0 ? 200 : 400; 
 $curr_y = 25;
 my(%PG, $part1, $part2, $pg);

 for ($x=0; $x <= $#$storages; $x++) {
    $pg = "";
    $s = $storages->[$x];
     next if (substr($s->{info}{type},0,4) eq "hbas" && !$renv->{show_hba_devices});

    next if ($FILTER_LIST && index($FILTER_LIST, $s->name()) < 0);
    $type = $s->{info}{type};
    $name = $s->{info}{name};
    my $ix = index($name, ":");
    my $key = substr($name, $ix+1);
    my $dName = $SW{$key} || $s->{info}{BoxName} || $s->{info}{PortWWN};
    my $ix2   = index($dName,".");
    if ($ix2 >= 0 && $dName !~ /^\d+\.\d+\./) {
      $dName = substr($dName,0,$ix2);
    }
    my($ses) = 2;
    my($cat);
    $label2 = "";

    eval {
       ($cat, $label2, $ses) = $s->iconLayout();
    };

    $dInfo  = $TO::Storages->[$x]{diskInfo};

    $encStatus = $portStatus = $diskStatus = $otherStatus = $encInfo = $portInfo = $diskInfo = $otherInfo = $testInfo = "";
    if ($statusFunc) {
      ($encStatus, $portStatus, $diskStatus, $otherStatus,
       $encInfo, $portInfo, $diskInfo, $otherInfo, $testInfo) = 
            &$statusFunc($type, $name, $arg, $s);
    }
    my $rc = &group_sev($FILTER_ID, $GR, $GRL, $name, 
                 $encStatus, $portStatus, $diskStatus, $otherStatus);
    next if ($rc == 1);
    $running = 1 if (&running($testInfo));
    if ($rc >= 2) {
       $x0 = $XY{$name}[0] ; # arra
       $y0 = $XY{$name}[1] ;
       if (!$x0) {
         $x0 = $curr_x; $y0 = $curr_y;
         $curr_y += 130;
         if ($curr_y > 1000) {
            $curr_x += $ST_XINC; $curr_y = 25;
         }
       }
       my $label3 = Util->shortHostname($s->info("host"));
       # $out .= "a$x|v|$cat|$name=$dName,$label2|$x0|$y0|$encStatus|$portStatus|$diskStatus|$otherStatus|$encInfo|$portInfo|$diskInfo|$otherInfo|$testInfo\n";
       $out .= "$name|v|$cat|$name=$dName,$label2,$label3|$x0|$y0|$encStatus|$portStatus|$diskStatus|$otherStatus|$encInfo|$portInfo|$diskInfo|$otherInfo|$testInfo\n";
       $leaf_cnt++;
    }

    $ports = $storages->[$x]{port};
    $pInfo = $storages->[$x]{portInfo};
    for ($y=0; $y <= $#$ports; $y++) {
       if ($ports->[$y]) {
           @b = split(/:/, $ports->[$y]);

           my($ix) = rindex($pInfo->[$y]{LGroup} , "(");
           my $nameXX = substr($pInfo->[$y]{LGroup}, $ix+1,-1);

           $from =  "$b[0]:$b[1],$b[2]";
           $to2   = "$name,$y";

           # $out .= &add_link($from, $to2, $hook_cnt, $x0-20, $y0+$y*20+15);
           if ($GR) {
             $outL .= &group_lnk($FILTER_ID,$GRL2, $GR, $name, $y, "$b[0]:$b[1]", $b[2]);
           } else {
             next if (&link_done($from, $to2));
             if ($linkInfo and exists($linkInfo->{$from}) ) {
               $outL .= "00|e|E|$linkInfo->{$from},K,_,1|$from|$to2\n";
             } elsif ($pInfo->[$y]{portType} eq 'scsi') {
               $outL .= "00|e|S|scsi|$from|$to2\n";
             } else {
               $outL .= "00|e|f||$from|$to2\n";
             }
           }
           $hook_cnt++;
           $cnt++;
       }
    }
  }

#
# HUBS
#

 for ($x=0; $x <= $#$hubs; $x++) {
    $s = $hubs->[$x];
    $name = $s->{info}{name};
    next if ($FILTER_LIST && index($FILTER_LIST, $s->name()) < 0);
    my $label =  $s->{info}{userLabel} || $name;
    $ports = $hubs->[$x]{port};

    $x0 = $XY{$name}[0] ;
    $y0 = $XY{$name}[1] ;
    if (!$x0) {
      $x0 = $curr_x; $y0 = $curr_y;
      $curr_y += 100;
    }
    my $hub_out = "$name|v|u,Hub|$name=$label|$x0|$y0||||||portinfo|diskInfo|encinfo\n";
    my ($dones, $hub_links);
    for ($y=0; $y <= $#$ports; $y++) {
        if ($ports->[$y]) {
           @b = split(/:/, $ports->[$y]);
           $name = $hubs->[$x]{info}{name};
           $from = "$name,$y";
           $to2  = "$b[0]:$b[1],$b[2]";
           if (&link_done($from, $to2)) {
              $dones++; next;
           } else {
              $hub_links .= "00|e|f||$from|$to2\n";
           }
        }
    }
    if ($dones) {  # add hub if there is at least one connection to it
      $leaf_cnt++;
      $out .= $hub_out;
      $outL .= $hub_links;
    }
  }

  if ($#$storages + $#$switches > 50) {
     $zoom = 3;
  }
#  $outI = "" if ($leaf_cnt > 20);

#
#  linkState
#
 if (!$linkInfo) {
    $outL .= &linkState($arg, \%MAP, \$cnt, $to);
 }
 if ($GR) {
    my $out2 = &group_print(\%XY, $GRL, $FILTER_ID, $GR_MAP);
    open(O, ">/tmp/appletStream");
    print O $out; 
    print O $out2; 
    print O $outL; 
    print O $outI; 
    close(O);
    return ($zoom, $default_orient, $out . $out2 . $outL . $outI, $running, $format, 
             $#$storages + $#$switches + $#$hosts);
 } else {
    open(O, ">/tmp/appletStream");
    print O $out; 
    print O $outL; 
    print O $outI; 
    close(O);
    return ($zoom, $default_orient,  $out . $outL . $outI, $running, $format, 
             $#$storages + $#$switches + $#$hosts);
 }
}


sub link_done {
  my($from, $to) = @_;
  
  my $ix = rindex($from, ",");
  if ($ix > 0) {
    my $from1 = $from;
    $from1 = substr($from,0,$ix);
    $ix = rindex($to, ",");
    my $to1 = $to;
    if ($ix > 0) {
      $to1 = substr($to,0,$ix) ;
      return 1 if ($to1 eq $from1);
    }
  }
  

  my $ft = $from gt $to ? "$to:$from" : "$from:$to";
  return 1 if ($LK{$ft});
  $LK{$ft} = 1;
  return 0;
}


#
# add Link errors from the State DB
#
sub linkState {
   my($arg, $MAP, $cnt, $TO) = @_;
   my $outL;
   my($map1, $map2, %LNK);

   my $State = State->new();
   my $comps = $State->hash();

   foreach my $el (keys %$comps) {
     my $comp = $comps->{$el};
     foreach my $topic (keys %$comp) {
         next if ($topic !~ /port.(\d+).fc_link/);
         my $node1 = "$el:$1";
         my $c = $comp->{$topic};
         my $node2 = $TO->nodeportTarget($node1);
         if ($node1 le $node2) {
           my($lnk) = $LNK{"$node1|$node2"};
           $LNK{"$node1|$node2"} = [$lnk->[0]+$c->[3], $lnk->[1]+0, 
              $c->[1], $c->[0]];
         } else {
           my($lnk) = $LNK{"$node2|$node1"};
           $LNK{"$node2|$node1"} = [$lnk->[0]+0, $lnk->[1]+$c->[3], 
              $c->[1], $c->[0]];
         }
      }
   }
   foreach my $l (keys %LNK) {
     my($node1, $node2) = split(/\|/, $l);
  
     my($type1, $k1, $p1) = split(/:/, $node1);
     my($type2, $k2, $p2) = split(/:/, $node2);
     my $sev0  = $LNK{$l}[3];
     next if ($sev0 == -2);
     my $info2 = $LNK{$l}[2];
     my($etype, $sev, $cnt0, $info1);
  #
  #  e | E or L | severity, link-info, textarea-info2, node1/node2 | ...
  #      E=error, L=link
  #               severity = blank,0,1,2
  #                         show in the box
  #                                     show at the bottom
  #
     $etype = "";
     $info2 =~ s/\n/ /g;
    
     if ($LNK{$l}[2] =~ / MPXIO:/) {  # check severity=1, means regular link error,
        $etype = "E"; # link-Error or Mpxio-error
        $sev = $LNK{$l}[3]; 
        $info1 = "mpx";
     } else {
        $etype = "E";
        $sev = $sev0;
        $info1 = "fc";
        $info2 = "(" . $LNK{$l}[0] . "/" . $LNK{$l}[1] . ") " . $info2;
     }
  
     # next if ($LNK{$l}[0] + $LNK{$l}[1] <= 0);
  
      $outL .= "$$cnt|e|$etype|$info1,$info2,$node1/$node2,$sev|$type1:$k1,$p1|$type2:$k2,$p2\n";
      #$outL .= "$cnt|e|$etype|info,info1,info2,$sev|$type1:$k1,$p1|$type2:$k2,$p2\n";
     $$cnt = $$cnt + 1;
   }
   return $outL;
}


# 1 : exclude
# 2 : no grouping                         , show leaf
# 3 : nothing found in grouping->{members}, show leaf

sub gr_node {
  my($GR, $name, $FILTER_ID) = @_;

  return (3,$name) if (!$GR);
  my $id = $GR->{$name};

  return (3, $name) if (!$id);
  my $short = substr($id, 0, length($FILTER_ID));

  if ($short ne $FILTER_ID) {      # skip node
     return (1, $name) ;
  }

  return (2, $name) if ($id eq $FILTER_ID);         # don't group
  my $rc = 0;

  my($group) = $id;
  my @F = split(/\./, $FILTER_ID); # system.lab1.rack1
  my @G = split(/\./, $group);
  $group = join(".", @G[0..$#F+1]);
  chop($group) if (substr($group,-1) eq ".");
  return ($rc, "group:$group");
}


sub group_sev {
  my($FILTER_ID, $GR, $GRL, $name, $encStatus, $portStatus, 
      $diskStatus, $otherStatus, $info) = @_;

  my ($rc, $node) = &gr_node($GR, $name, $FILTER_ID);
  return $rc if ($rc);

  my $i = ",$encStatus,$portStatus,$diskStatus,$otherStatus,";
  my $st = -2;
  if (index($i, ",2") >= 0 || index($i, ",3") >= 0) {
    $st = 2;
  } elsif (index($i, ",1") >= 0) {
    $st = 1;
  }
  if (!exists($GRL->{$node})) {
    $GRL->{$node} = [$st,1];
  } else {
    my $p = $GRL->{$node};
    $p->[1]++;
    if ($p->[0] < $st) {
       $p->[0] = $st;
    }
  }
  return 0;
}

sub group_lnk {
  my($FILTER_ID, $GRL2, $GR, $from, $from_p, $to, $to_p) = @_;
  my($rc);

  ($rc, $from) = &gr_node($GR, $from, $FILTER_ID);

  return if ($rc == 1) ;     # skip;
  $from_p = "1" if ($rc == 0); # grouped

  ($rc, $to) = &gr_node($GR, $to, $FILTER_ID);
  return if ($rc == 1) ;     # skip;
  $to_p = 1 if ($rc == 0);   # grouped

  return "" if ($from eq $to);
  my $k;
  if ("$from,$from_p" gt "$to,$to_p") {
     $k = "$to,$to_p" . "$from,$from_p";
  } else {
     $k = "$from,$from_p" .  "$to,$to_p";
  }
  if (!$GRL2->{$k}) {
    $GRL2->{$k} = 1;
    return "" if (&link_done("$from,$from_p", "$to,$to_p"));
    return "00|e|f||$from,$from_p|$to,$to_p\n";
  } else {
    return "";
  }
}

sub group_print {
  my($XY, $GRL, $FILTER_ID, $MAP) = @_;
  my($out);
  my($F1, $F2) = split(/\./, $FILTER_ID);
  my $startx = 100;
  my $starty = 60;
  foreach my $k (keys %$GRL) {
     my($x0, $y0);
     if ($XY->{$k}[0]) {
       $x0 = $XY->{$k}[0];
       $y0 = $XY->{$k}[1];
     } else {
       $x0 = $startx; 
       $y0 = $starty;
       $starty += 50;
     }
     my $k0 = substr($k,6);
     my $ix = index($k0,":");
     my $color = $MAP->{$k0}[2] || "E0F0E0";
     my $style = $MAP->{$k0}[1];
     my $desc  = $MAP->{$k0}[0] || $style;
     my $grl = $GRL->{$k};
     my $gif = $style? "$style.gif" : "standard.gif";
     $out .= "$k|v|g,$style,$grl->[1],,$color/$gif|$k=" . substr($k0,$ix+1) . ",$desc|$x0|$y0||,$grl->[0]||||||||\n";
  }
  return $out;
}


sub add_link {
  my($from, $to, $hook, $x, $y) = @_;
  my($out);
#  $out .= "x$hook|v|x||$x|$y|\n";

  $out .= "00|e|f||$from|$to\n";
#  $out .= "00|e|f||x$hook,0|$to\n";
  return $out;
}


sub draw_switch {
    my($orient,$linkInfo, $to, $s, $curr_x, $curr_y, $curr_ve_x, 
       $curr_ve_y, $ZONE_ID, $FILTER_ID, $GR, $GRL, $GRL2,
       $grayPorts, $graySwitches, $SW, $XY, $arg, 
       $statusFunc, $force, $leaf_cnt, $outI) = @_;

    my($y, $x0, $y0, $out, $outL);
    my($dports) = $s->{dport};
    my($zc, $ZONE_PORT_LIST, $ALL_ZONE_PORT);
    my $zones = "Z";
    if ($#$dports < 0 || $s->{zones}{hd}) { # suspicious
       my $P = $s->{port};
       my $PI = $s->{portInfo};
       my @DONE = ();
       if (exists($s->{zones}{hd})) {
          my $hds = $s->{zones}{hd};
          my $x;
          for ($x=0; $x <= $#$hds; $x++) {
             $ALL_ZONE_PORT = " " . $s->{zones}{hd}[$x] . " ";
             if ($ZONE_ID) {
                next if ($s->{zones}{hd_name}[$x] ne $ZONE_ID);
                $ZONE_PORT_LIST =  " " . $s->{zones}{hd}[$x] . " ";
             }
             if ($hds->[$x]) {
               my @zs = split(/ /, $hds->[$x]);
               foreach my $z (@zs) {
                 next if ($z > 256);
                 if ($P->[$z] || 
                    (defined($PI->[$z]{PortWWN}) && defined($PI->[$z]{LocalPortWWN}) &&
                      $PI->[$z]{PortWWN} ne $PI->[$z]{LocalPortWWN}) ) {
                   $DONE[$z] = 1;
                   $zones .= "$z-";
                 }
               }
               $zones .= "/" if (substr($zones,-1) ne "/");
             }
          }
       }
       my $x;
       for ($x=0; $x <= $#$P; $x++) {
          if ($P->[$x] && !$DONE[$x] && 
                   (!$ZONE_ID || index(" $ALL_ZONE_PORT "," $x ") < 0)) {
              $zones .= "$x-";
              $DONE[$x] = 1;
          }
       }
       for ($x=0; $x <= $#$PI; $x++) {
          if (defined($PI->[$x]{PortWWN}) && !$DONE[$x] &&
                   (!$ZONE_ID || index(" $ALL_ZONE_PORT "," $x ") < 0)) {
            $zones .= "$x-";
          }
       }

    } else {
      my(%Z, %Z1);
      for ($y=0; $y <= $#$dports; $y++) {  # iport => dport1,dport2 ...
         if ($dports->[$y]) {
           $Z{$dports->[$y]} .= "$y-";
         }
      }
      foreach my $z (sort keys %Z) {
         my @s = split(/\-/, $z . $Z{$z});
         $Z1{join("-", sort @s)} = 1;
      }
      foreach my $z (keys %Z1) {
         $zones .= $z . "/";
      }
    }
   
    chop($zones) if (substr($zones, -1) eq "/");

    if (0) {
    if ($#$dports < 0) {   # fake the dport to draw ports in the switch
       my($dports) = $s->{port};
       my $cnt = 0;
       for ($y=0; $y <= $#$dports; $y++) {
           next if (!$dports->[$y]);
           $zones .= "$y-";
           $cnt++;
           if ($cnt %2 == 0) {
              chop($zones) if (substr($zones,-1) eq "-");
              $zones .= "/";
           }
       }
       $zones .= " ";
    }
    }
    my $max2 = $#{$s->{portInfo}} + 1;
    my $running;
    my $name   = $s->{info}{name};
    my $label2 = $s->{info}{ProductID} ;

    my($dName);
    $dName = $s->{info}{BoxName} || $SW->{$s->{info}{sw_WWN}} || $s->{info}{sw_ipAddr};

    my($encStatus, $portStatus, $diskStatus, $otherStatus, $testInfo);
    my($encInfo, $portInfo, $diskInfo, $otherInfo);
    if ($statusFunc) {
      ($encStatus, $portStatus, $diskStatus, $otherStatus,
       $encInfo, $portInfo, $diskInfo, $otherInfo, $testInfo) = 
          &$statusFunc($s->type(), $name, $arg, $s);
    }
    my $rc;

  if ($force) {
       $rc = 2;
    } else {
       $rc = &group_sev($FILTER_ID, $GR, $GRL, $name,
                    $encStatus, $portStatus, $diskStatus, $otherStatus, $s->{info});
    }
    return () if ($rc == 1);
    $running = 1 if (&running($testInfo));
    if ($rc >= 2) {
       $x0 = $XY->{$name}[0] ;
       $y0 = $XY->{$name}[1] ;
       if (!$x0) {
         if ($name eq "ve" || substr($name,0,3) eq "sve") {
           $x0 = $$curr_ve_x; 
           $y0 = $$curr_ve_y;
           $$curr_ve_y += 110;
         } else {
           if ($sw_cnt++ % 2 == 0) {
              $x0 = $$curr_x + 120;
              $y0 = $$curr_y;
           } else {
              $x0 = $$curr_x;
              $y0 = $$curr_y;
              $$curr_y += 110;
           }
         }
         
       }
        # $out .= "s$x|v|s,XXX,$zones,$max2,F0D8B8|$name=$dName,$label2|$x0|$y0|$encStatus|$portStatus|$diskStatus|$otherStatus|$encInfo|$portInfo|diskinfo|$otherInfo|$testInfo\n";

       my $color = $force? "F2F2F2":"F0D8D8";

       my $layout = $s->iconLayout($orient);
       my $label3 = "(" . Util->shortHostname($s->info("host")) . ")";
       $out .= "$name|v|s,XXX,$zones,$max2,$layout|$name=$dName,$label2,$label3|$x0|$y0|$encStatus|$portStatus|$diskStatus|$otherStatus|$encInfo|$portInfo|diskinfo|$otherInfo|$testInfo\n";
       $$graySwitches .= "$name,";
       $$leaf_cnt = $$leaf_cnt + 1;
    }

    my $ports = $s->{port};

    for ($y=0; $y <= $#$ports; $y++) {
        next if ($ZONE_PORT_LIST &&   index($ZONE_PORT_LIST, " $y ") <  0 &&
                                      index($ALL_ZONE_PORT,  " $y ") >= 0 );
        if ($ports->[$y]) {
           my @b = split(/:/, $ports->[$y]);

           my $tn = $to->nodeByName($ports->[$y]);
           next if (!$tn);
           next if (substr($tn->class(), 0, 6) ne "switch" && !$force); # do switch2switch only
           $grayPorts->{"$b[0]:$b[1]"} = 1 if ($rc >= 2);

           # $from =  $MAP{"$b[0]:$b[1]"} . ",$b[2]";
           my $from   =  "$b[0]:$b[1],$b[2]";
           # $to2  = "s$x,$y";
           my $to2    = "$name,$y";
           # $out .= &add_link($from, $to2, $hook_cnt, $x0- 20, $y0+ $y*20+ 15);
           if ($GR && !$force) {
              $outL .= &group_lnk($FILTER_ID,$GRL2, $GR, "$b[0]:$b[1]", $b[2], $name, $y);
           } else {
              if ($linkInfo and exists($linkInfo->{$to2}) ) {
                my $ln1 = ($from ge $to2)? "$to2:$from": "$from:$to2";
                if ($linkInfo->{$to2} >= $STAT{$ln1}) {
                  $outL .= "00|e|E|$linkInfo->{$to2},K,_,1|$from|$to2\n";
                  $STAT{$ln1} = $linkInfo->{$to2};
                }
              } else {
                $outL .= "00|e|f||$from|$to2\n";
              }
           }
           #$cnt++;
        }
    }

    return ($out, $outL, $running);
}

sub running {
  my($t) = @_;

  my $ix = index($t,",");
  return (substr($t,0,$ix) > 0);
}



1;
