package com.sun.solarisinstall.panels;

import com.sun.wizards.*;
import com.sun.wizards.core.*;
import com.sun.wizards.awt.*;
import com.sun.install.products.*;
import com.sun.solarisinstall.*;
import com.sun.solarisinstall.core.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import java.util.*;

import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;

/**
 * The ProductSelectionPanel allows the user to select which 
 *  copack CDs and products will be installed.
 */
public class ProductSelectionPanel extends ParentPanel implements TreeHandler
{
  /*
   * The cdNodes array for creating the nodes
   * to the tree
   */
  private Vector cdNodes = null;
  private CDNode rootNode = null;
  private JTree prodTree = null;
  private CheckNodeRenderer cnr = null;


  /*
   * Images for checks/arrows for the tree
   */
  private Image checkedImg;
  private Image uncheckedImg;
  private Image partialCheckedImg;
  private ImageIcon nodeCollapsedImg;
  private ImageIcon nodeExpandedImg;

  /*
   * The panel that this all resides on
   */
  private JPanel containerPanel;

  /*
   * The scroll pane used to display the CD/product buttons.
   */
  private JScrollPane jsp;


  /*
   * The scroll pane used to display the mouseover help text.
   */
  private transient ScrollPane scrollPane = null;
  private transient StaticSizePanel mousePanel = null;
  private FlowLabel mouseLabel = null;
  private boolean helpOn = false;
  private CDNode lastnode = null;

  private volatile boolean timerStopped = false;
  private volatile Thread timerThread = null;
  private transient Object threadLock = new Object();
  private long timeOut = 750;  // wait 750 ms before changing mouseover help

  /*
   * Prompt used during TTY mode
   */
  private String intro = null;

  /*
   * Localized separator
   */
  private static String decimalSeparator = null;


  /**
   * The panel that will display the lists of installable CDs
   * and products
   */
  private Vector CDnamesOrig = new Vector();
  private Vector CDnames = new Vector();
  private Vector allProductList = new Vector();

  /*
   * This flag is false prior to the panel's user interface creation
   */
  private boolean createdUI = false;



  /**
   * Creates a ProductSelectionPanel with no name.
   */
  public ProductSelectionPanel()
  {
  }

  /**
   * Creates a ProductSelectionPanel with the specified name,
   * and contains profile entries indexed by the specified key list.
   *
   * @param wizardState	The buildtime WizardState.
   * @param name		The name for this ProductSelectionPanel.
   */
  public ProductSelectionPanel(WizardState wizardState, String name)
  {
    super(wizardState, name);
  }

  /**
   * Creates a ProductSelectionPanel with the specified name, the specified
   * route and wizard manager.
   *
   * @param name	The name for this ProductSelectionPanel.
   * @param route	The route to the appropriate server side object.
   * @param wizardManager	The wizardManager responsible for this component.
   */
  public ProductSelectionPanel(String name, Route route,
                                  WizardTreeManager wizardManager)
  {
    super(name, route, wizardManager);
  }


  /**
   * This method creates the user interface.
   */
  public void createUI()
  {
    super.createUI();

    WizardTreeManager wizardManager = getTreeManager();

    Route objectRoute = getRoute().getChildServerRoute("profileObject");

    if (Def.DEBUG) {
       System.out.println("ProductSelectionPanel createUI");
    }

    Locale systemLocale =
         (Locale)wizardManager.callServerObjectMethod(
          getRoute(),
         "getData",
          new String[]{"com.sun.wizards.core.Route.class",
                      "java.lang.String.class"},
          new Object[] {null, InstallConstants.systemLocale});

    if (systemLocale == null) systemLocale = Locale.getDefault();
    decimalSeparator = new String(new char[] {(
             new DecimalFormatSymbols(systemLocale)).getDecimalSeparator()});

    setLayout(new BorderLayout());

    /*
     * Add the header
     */
    String heading = MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                             SolarisInstallResources.MSG_HDR_SELECT_COPACK);

    /*
     * Add the text at top of panel
     */
    FlowLabel label =
      new FlowLabel(intro = 
               MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                             SolarisInstallResources.MSG_SELECT_CDS_PRODS));


    if (wizardManager.hasGUI()) {
        add(new HeaderPanel(heading, null), "North");

        /*
         * Create the panel to lay all this out on.
         * This needs to be a JPanel to that the JScrollPane
         * that will be added to it will not fight with a
         * 'heavyweight' component. This will eliminate
         * the 'flickering' effect from events.
         */
        containerPanel = new JPanel(new BorderLayout());
        containerPanel.setBackground(this.getBackground());

        /*
         * Add the text to the panel
         */
        containerPanel.add(label, "North");
    }


    /*
     * get the entire product list (all CDs combined)
     * 
     * if products are added with the add button, allProductList
     * should be updated
     */
    allProductList =
           (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
                         "getProfileUtilProductList", null, null);

    /*
     * get list of CD names from media_kit.toc file
     */
    CDnamesOrig =
        (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
        "getCDList",
        null, null);

    CDnames = (Vector)CDnamesOrig.clone();

    /*
     * Here we actually set up the nodes that we need
     */
    cdNodes = new Vector();

    /*
     * Create the root node
     */
    rootNode = new CDNode("","");

    /*
     * For each CD, read its list of products.
     */
    Vector solProducts = new Vector();
    for (int index = 0; index < CDnames.size(); index ++) {
      /*
       * get product list
       */
      String cdname=(String)CDnames.elementAt(index);
      solProducts =
          (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
          "getCDProductList",
          new String[] {"java.lang.String.class"},
          new Object[] { cdname });

      /*
       * Make sure OS dependency checking hasn't removed all
       * products from cd before adding cd to panel
       */
      if ((solProducts == null) || (solProducts.size() == 0)) {
        if (Def.DEBUG) System.out.println("ProductSelectionPanel: " +
                         "removing " +cdname);
        continue;
      }

      /*
       * create a parent node for each cd
       */
      CDNode parentNode = new CDNode(cdname, solProducts);

      /*
       * set state (selected/deselected) flag
       */
      for (int i = 0; i < solProducts.size(); i++) {
        SolarisProduct prod = (SolarisProduct)solProducts.elementAt(i);
        Vector childrenNode = new Vector();
        String prodDefault = null;
        if (prod.isInstalledByDefault()) {
          prod.setState(true);
          prodDefault = "YES";
        } else {
          prod.setState(false);
          prodDefault = "NO";
        }
        /*
         * Add the children node to the tree
         */
        CDNode childNode = new CDNode(prod.getName(), prodDefault, parentNode);
        int size = SolarisFileSystem.getProductSizeinKB(prod);
        childNode.setSize(convertKBtoMB(size));
        parentNode.add(childNode);
      }

      /*
       * add CD Name/size to table. setSize actually calls
       * setUserObject. The UserObject originally holds just the 
       * name and we append the size. To get simply the name, we
       * can still call node.getName().
       */
      int size = getCDSize(parentNode);

      parentNode.setSize(convertKBtoMB(size));
      cdNodes.addElement(parentNode);

      /*
       * Add this to the root node
       */
      rootNode.add(parentNode);

    }


    /*
     * Do dependency checking
     *
     * There shouldn't be a problem here, unless someone set
     * the pd files/default profile up incorrectly or
     * unless doing upgrade.
     */
    DependError errorInfo = null;
    errorInfo = doDependChecking();

    /*
     * deselect products that fail depend checking
     */
    handleUpgradeDependErrs(errorInfo);


    if (wizardManager.hasGUI()) {
      /* 
       * Now set up the expand/collapse arrows for the tree
       */
      nodeCollapsedImg = new ImageIcon(wizardManager.getImage(getRoute(), 
                                      "collapsedArrow"));
      nodeExpandedImg = new ImageIcon(wizardManager.getImage(getRoute(), 
                                      "expandArrow"));
      UIManager.put("Tree.expandedIcon", nodeExpandedImg);
      UIManager.put("Tree.collapsedIcon", nodeCollapsedImg);

      /*
       * Create the JTree and the jScrollPane to put it in
       */
      prodTree = new JTree(rootNode);

      /*
       * Set up the icons that we need for the tree
       */
      checkedImg = wizardManager.getImage(getRoute(), "CheckedIcon");
      uncheckedImg = wizardManager.getImage(getRoute(), "UncheckedIcon");
      partialCheckedImg = wizardManager.getImage(getRoute(), 
                                                        "PartialCheckedIcon");

      /*
       * Set up the custom renderer for the JTree
       */
      cnr = new CheckNodeRenderer(checkedImg, uncheckedImg, partialCheckedImg,
                                     "CDNode");

      /*
       * Now make sure the background/foreground colors are set up
       * right for the nodes.
       */
      Color bgColor = containerPanel.getBackground();

      jsp = new JScrollPane(prodTree);

      prodTree.setCellRenderer(cnr);
      prodTree.getSelectionModel().setSelectionMode(
                       TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);

      /*
       * Add the listener for the checks
       */
      prodTree.addMouseListener(new NodeSelectionListener(prodTree, this));
      prodTree.addMouseMotionListener(new MouseOverListener(prodTree, this));

      /*
       * Add the JScrollPane to the main panel.
       */
      containerPanel.add(jsp,"Center");


      /*
       * Add a scrollpane
       */
      scrollPane = new ScrollPane();

      /*
       * Create a static size panel and add the scrollpane
       * with the mouse text to it.
       */
      //mousePanel = new StaticSizePanel(new Dimension(40,100));
      mousePanel = new StaticSizePanel(new Dimension(40,85));
      mousePanel.add(scrollPane);
      String mouseText = "   ";
      scrollPane.add(mouseLabel=new FlowLabel(mouseText));
      containerPanel.add(mousePanel, "South");

      add(containerPanel, "Center");
    }

    setCurrentNodeInfo();

    /*
     * Set selected values to default values thru the reset method.
     * First set createdUI flag (which reset checks).  If createdUI
     * was not set, reset would recursively call this createUI method.
     */
    createdUI = true;
    reset(null);

  }


  public void beginDisplay()
  {
    super.beginDisplay();
    WizardTreeManager wizardManager = getTreeManager();

    /*
     * Determine the settings for the buttons.
     */
    setCurrentNodeInfo();

    /*
     * Check if any new CDs have been added and update
     * cdnodes accordingly
     */
    checkForNewCDs();

    if (mouseLabel != null) {
      scrollPane.remove(mouseLabel);
    }

    if ( wizardManager.hasGUI()) {
      String mouseText = " ";
      scrollPane.add(mouseLabel=new FlowLabel(mouseText));
      helpOn = false;
    }

  }


  public void consoleInteraction()
  {
    String helpText =
             MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                       SolarisInstallResources.MSG_CLI_PRODSEL_SELECT_CDS_HELP);
          
    String nval =
             MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                               SolarisInstallResources.MSG_CLI_N);
          
     Object[] arg =  {nval};
     String prompt =
        MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
           SolarisInstallResources.MSG_CLI_PRODSEL_ENTER_CD_NUMBER, arg);
  
     boolean done = false;
     while (!done) {
        TTYDisplay.clear();
        TTYDisplay.showText(intro);
        TTYDisplay.showNewline();
        showTable();
        TTYDisplay.showNewline();
        String nodeToggle = TTYDisplay.queryValue(prompt, nval, helpText);

        if (nodeToggle.equals(nval)) {
          done = true;
        } else {
          editNode(nodeToggle);
        }
     }
  }
 

  /*
   * showTable displays just the CDs themselves
   * (not the products/children underneath).
   */
  private void showTable() {
    Vector tableData = new Vector();
    Vector row = null;

    /*
     * Determine what to display
     */
    int num = 0;

    for (int i=0; i < cdNodes.size(); i++ )  {
       CDNode cdnode = (CDNode) cdNodes.elementAt(i);
       row = new Vector();
       String name = (String) cdnode.getUserObject();

       boolean partialSelected = cdnode.isPartialSelected();
       boolean cdState = cdnode.isNodeSelected();
       if (cdnode.isCDNode()) {
         if (partialSelected)
           row.addElement("[-]");
         else
           row.addElement(cdState?"[X]":"[ ]");
       } else
         row.addElement(cdState?"[X]":"[ ]");

       row.addElement((new Integer(i)).toString());
       row.addElement(name);
       tableData.addElement(row);
    }
    TTYDisplay.displayTable(tableData);

  } 


  /*
   * showNode
   *   Displays the node (CD) that will be edited for
   *   individual products.
   *
   * Same as showTable, but only adds the needed nodes
   * to be displayed in displayTable. 
   */
  private void showNode(String node) 
  {
    Vector tableData = new Vector();
    Vector row = null;
    boolean nodeSelected = false;

    for (int i=0; i< cdNodes.size(); i++ )  {
      CDNode cdnode = (CDNode) cdNodes.elementAt(i);
      row = new Vector();

      /*
       * Only display the node and subs under
       * for the node we selected.
       *
       * Use getUserObject to get the name plus the size
       */
      String name = (String) cdnode.getUserObject();
      boolean partialSelected = cdnode.isPartialSelected();
      boolean cdState = cdnode.isNodeSelected();

      if ((new Integer(i)).toString().equals(node)) {
        if (partialSelected)
          row.addElement("[-]");
        else
          row.addElement(cdState?"[X]":"[ ]");
        row.addElement((new Integer(i)).toString());
        row.addElement(name);
        tableData.addElement(row);
        nodeSelected = true;
      } else {
        nodeSelected = false;
      }
       
      Vector cdProducts = (Vector)cdnode.getCDproducts();

      for (int k=0; k < cdProducts.size(); k++) {
        CDNode tempNode = (CDNode)cdnode.getChildAt(k);
        row = new Vector();
        boolean state = tempNode.isNodeSelected();
        String id = (String) tempNode.getUserObject();
        if (nodeSelected) {
          row.addElement(state?"[X]":"[ ]");
          row.addElement((new Integer(i)).toString() + decimalSeparator + 
                         (new Integer(k)).toString());
          row.addElement(id);
          tableData.addElement(row);
        }
      }
    }
    /*
     *  Set display
     */
    TTYDisplay.displayTable(tableData);
  } 


  /**
   * editNode opens up just the node for display to
   * choose the individual products.
   *
   * Basically the same as consoleInteraction, but only for the
   * node itself, not all CDs.
   */
  private void editNode(String selection) 
  {
    boolean valid = true;
    /*
     * Check out the selection to make sure it is valid
     */
    try {
      int selected = new Integer(selection).intValue();

      if ( (selected < 0) || (selected > (cdNodes.size()-1)) ) {
        valid = false;
      }  else {
        valid = true;
      }
    } catch(Exception exc) {
      valid = false;
    }

    if (!valid) {

      Object[] arg2 = {selection};
      String badProd = MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                     SolarisInstallResources.MSG_CLI_PRODSEL_BAD_CD_NUM, arg2);
      String title =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                     SolarisInstallResources.MSG_ERROR);
      String ok =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                     SolarisInstallResources.MSG_OK_DISK);

      displayQuery(this, title, badProd,
             new String[] {ok},
             new String[] {null});
      return;
    } else {
      String helpText =
        MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
          SolarisInstallResources.MSG_CLI_PRODSEL_ENTER_PROD_NUMBER);

      String selProducts =
        MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
          SolarisInstallResources.MSG_CLI_PRODSEL_SELECT_PRODUCTS);

      String nval =
          MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
             SolarisInstallResources.MSG_CLI_N);

       Object[] arg1 =  {nval};
       String prompt =
          MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
             SolarisInstallResources.MSG_CLI_PRODSEL_ENTER_PROD_NUMBER, arg1);

      boolean done = false;
       while (!done) {
          TTYDisplay.clear();
          TTYDisplay.showText(selProducts);
          TTYDisplay.showNewline();
          showNode(selection);
          TTYDisplay.showNewline();
          String toggle = TTYDisplay.queryValue(prompt, nval, "");

          if (toggle.equals(nval)) {
               done = true;
          } else {
               toggleBox(toggle);
          }
       }
     }
  } // editNode()



  /**
   * toggleBox toggles the checkbox (in TTY mode) of the
   * product that has been selected.
   */
  private void toggleBox(String selection) 
  {
    /*
     * Parse out the selection to get what is after the decimal
     */
    boolean validProd = true;
    boolean nodeSelected = false;
    int decimalIndex;
    CDNode cdNode = null;
    CDNode prodNode = null;
    String cdSelectionString = null;
    int cdSelection;
    String prodSelectionString = null;
    int prodSelection;
    boolean selectCD = true;
    int badvalNum = SolarisInstallResources.MSG_CLI_PRODSEL_BAD_PROD_NUM;
    String name = null;
    String mouseHelp = null;


    try {
      decimalIndex = selection.indexOf(".");
      if (decimalIndex != -1) {
        cdSelectionString = selection.substring(0, decimalIndex);
        selectCD = false;
        badvalNum = SolarisInstallResources.MSG_CLI_PRODSEL_BAD_PROD_NUM;
      } else {
        cdSelectionString = selection;
        badvalNum = SolarisInstallResources.MSG_CLI_PRODSEL_BAD_CD_NUM;
      }

      cdSelection = new Integer(cdSelectionString).intValue();
      cdNode = (CDNode)cdNodes.elementAt(cdSelection);
      name = cdNode.getName();

      /*
       * Determine if we picked a product or CD
       */
      if (decimalIndex != -1) {
        prodSelectionString = selection.substring(decimalIndex + 1);
        prodSelection = new Integer(prodSelectionString).intValue();
        prodNode = (CDNode)cdNode.getChildAt(prodSelection);
        nodeSelected = prodNode.isNodeSelected();
        name = prodNode.getName();
        Vector solprods = (Vector)cdNode.getCDproducts();
        //for (int k=0; k < solprods.size(); k++) {
        SolarisProduct prod = (SolarisProduct)solprods.elementAt(prodSelection);
        mouseHelp = getprodMouseText(prod);
      } else {
        mouseHelp = getCDMouseText(name);
        nodeSelected = cdNode.isNodeSelected();
      }
    } catch(Exception exc) {
      validProd = false;
    }


    if (!validProd) {
      Object[] arg = {selection};
      String badVal = MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
               badvalNum, arg);
      String title =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall" ,
               SolarisInstallResources.MSG_ERROR);
      String ok =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
               SolarisInstallResources.MSG_OK_DISK);

      displayQuery(this, title, badVal,
             new String[] {ok},
             new String[] {null});
      return;
    } else {


      Object[] arg2 = {name};
      String togglePrompt =
                 MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                       SolarisInstallResources.MSG_CLI_PRODSEL_TOGGLE,arg2);
      String helpText = "";

      TTYDisplay.showNewline();
      TTYDisplay.showText(mouseHelp);
      TTYDisplay.showNewline();
      String yval =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                               SolarisInstallResources.MSG_CLI_Y);

      String nval =  MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                               SolarisInstallResources.MSG_CLI_N);



      String yn = TTYDisplay.queryValue(togglePrompt, 
                             (nodeSelected?nval:yval),
                                   new String[] { yval, nval }, helpText);

      if( yn.equalsIgnoreCase(yval) ) {
        if (selectCD)
          handleTreeSelection(prodTree, cdNode);
        else
          handleTreeSelection(prodTree, prodNode);
      }
    }
  } // toggleBox




  /* ****************************************************
   * Selection logic for the tree
   * **************************************************** */
  public void handleTreeSelection(JTree tree, DefaultMutableTreeNode TreeNode) 
  {
     CDNode node = (CDNode)TreeNode;
   
    /*
     * If this is the root, skip it
     */
    if (node.isRoot())
      return;

    boolean isSelected = !(node.isSelected());
    boolean isNodeSelected = !(node.isNodeSelected());

    /*
     * Since we have our node, and we don't know if
     * this is the same node that was being worked with
     * before, we walk the tree and make sure that
     * setPartialSelected() is set to false for
     * all the others.
     *
     * This is for the 3-state - making sure that if
     * the CD is not chosen, then it should go to full
     * if we are not working in the same focus.
     */
    CDNode rootNode = (CDNode)node.getRoot();
    Enumeration childEnum = rootNode.getChildrenEnum();
    while (childEnum.hasMoreElements()) {
      CDNode cd = (CDNode)childEnum.nextElement();

      if (cd.isCDNode()) {
        /*
         * Means this a CD - go ahead and make all other CDs
         * setPartialSelected(false);
         */
        if (cd != node) {
          cd.setPartialStateSaved(false);
        }
      } else {
        /*
         * Means this is a product under a CD - get the CD
         * and make all other CDs setPartialSelected(false);
         */
        CDNode productParentNode = (CDNode)node.getParent();
        if ( !(cd.getName(cd).equals(productParentNode.getName(productParentNode))) ) {
          cd.setPartialStateSaved(false);
        }
      }
    }
    /*
     * Now handle the logic
     */
    handleNodeLogic(tree, node, isSelected, isNodeSelected);

  }

  public void handleMouseOver(JTree tree, DefaultMutableTreeNode TreeNode) 
  {
    if (! wizardManager.hasGUI()) return;

    if (tree == null) {
      if (mouseLabel != null) {
        if (helpOn == true) {
        /*
         * We intentionally never set helpOn to true. Originally, I
         * was going to clear the help area, but then how would we
         * be able to move the mouse down to scroll?!?!
         * 
         * So, none of the following is executed.
         */
          mouseLabel.setText(" ");
          mousePanel.validate();
          helpOn = false;
        } else {
        /*
         * Don't do a validate here or we could encounter a
         * problem with flowlabel overwriting text in scrollarea 
         */
        }
      }
    } else {
      CDNode node = (CDNode)TreeNode;
   
      /*
       * If this is the root, skip it
       */
      if (node.isRoot()) {
        return;
      }

      String mouseText = null;

      if (node.isCDNode()) {
        mouseText = getCDMouseText(node.getName());
      } else {
        CDNode parentNode = ((CDNode)node.getParent());
        Vector solprods = (Vector) parentNode.getCDproducts();
        for (int k=0; k < solprods.size(); k++) {
          SolarisProduct prod = (SolarisProduct) solprods.elementAt(k);
          String name = prod.getName();
          if (name.equals(node.getName())) {
            mouseText = getprodMouseText(prod);
            break;
          }
        }
      }

      mouseLabel.setText(mouseText);
      mousePanel.validate();
    }
  }


  /**
   * Does the logic behind the tree selection.
   *
   * @param TreeNode The node that is in question
   */
  public void handleNodeLogic(JTree tree, CDNode node, boolean isSelected, 
                                           boolean isNodeSelected) 
  {
    WizardTreeManager wizardManager = getTreeManager();
 
    /*
     * Do the logic here instead of in the node itself...
     */
    if (node.isCDNode()) {
      /*
       * This is a parent
       * If this is set to partial, this should be a
       * 3 way click:
       *    partial -=> full -=> empty -=> partial
       *                    (same state as started in)
       */
      if (node.isPartialSelected()) {
        /*
         * partial -=> full
         * Save the partial state
         * node.savePartialState();
         */
        node.setPartialSelected(false);
        node.setSelected(true);
        node.setNodeSelected(true);
        node.savePartialState();

        Enumeration enum = node.getChildrenEnum();
        while (enum.hasMoreElements()) {
          CDNode childNode = (CDNode)enum.nextElement();
          /*
           * Check here to see if a node was already chosen as
           * not to increase too high.
           */ 
          if (!(childNode.isNodeSelected())) {
            node.setProductChosen(true);
            childNode.setNodeSelected(true);
          }
        }
      } else if ( (!node.isPartialSelected()) && (node.isNodeSelected())) {
        /*
         *  full -=> empty
         */
        node.setPartialSelected(false);
        node.setSelected(isSelected);
        node.setNodeSelected(isNodeSelected);

        Enumeration enum = node.getChildrenEnum();
        while (enum.hasMoreElements()) {
          CDNode childNode = (CDNode)enum.nextElement();
          /*
           * Check here to see if a node was already chosen as
           * not to increase too high.
           */
          if ((childNode.isNodeSelected())) {
            node.setProductChosen(false);
            childNode.setNodeSelected(false);
          }
        }
      } else {
        /*
         * empty -=> partial state (or full)
         */
        if (node.isPartialStateSaved()) {
          node.setPartialSelected(true);
          node.setNodeSelected(true);
          node.setSelected(false);

          Vector partialVector = node.restorePartialState();
          Enumeration partEnum = partialVector.elements();
          while (partEnum.hasMoreElements()) {
            CDNode partNode = (CDNode)partEnum.nextElement();
            partNode.setNodeSelected(true);
            node.setProductChosen(true);
          }

          /*
           * Tell the tree that the parent was changed too
           */
          if (wizardManager.hasGUI()) {
                ((DefaultTreeModel)tree.getModel()).nodeChanged(node);
          }
       } else {
          node.setPartialSelected(false);
          node.setSelected(isSelected);
          node.setNodeSelected(isNodeSelected);

          Enumeration enum = node.getChildrenEnum();
          while (enum.hasMoreElements()) {
            CDNode childNode = (CDNode)enum.nextElement();
            /*
             * Check here to see if a node was already chosen as
             * not to increase too high.
             */
            if ((childNode.isNodeSelected()) != isNodeSelected) {
              node.setProductChosen(isNodeSelected);
              childNode.setNodeSelected(isNodeSelected);
            }
          }

          /*
           * Tell the tree that the parent was changed too
           */
          if (wizardManager.hasGUI()) {
                ((DefaultTreeModel)tree.getModel()).nodeChanged(node);
          }
        } 
      }
      /*
       *  reset size of CD node
       */
      int size = getCDSize(node);
      node.setSize(convertKBtoMB(size));

    } else {
      /*
       * This is a child - needs to update the parent
       * according to how many products have been chosen
       */
      node.setSelected(isSelected);
      node.setNodeSelected(isNodeSelected);
 
      /*
       * Find out what the parent is (CD)
       */
      CDNode parentNode = ((CDNode)node.getParent());
      parentNode.setProductChosen(isNodeSelected);

      int totalProducts = parentNode.getChildCount();
      int productsChosen = parentNode.getProductsChosen();

      if (productsChosen == totalProducts) {
        /*
         * Means that with this child count, the parent should be full
         * so set it that way....
         */
        parentNode.setSelected(true);
        parentNode.setPartialSelected(false);
        parentNode.setNodeSelected(true);
      } else if (productsChosen == 0) {
        parentNode.setPartialSelected(false);
        parentNode.setNodeSelected(false);
      } else if ( productsChosen < totalProducts) {
        parentNode.setPartialSelected(true);
        parentNode.setNodeSelected(true);
        parentNode.setSelected(false);
        if (wizardManager.hasGUI()) {
                ((DefaultTreeModel)tree.getModel()).nodeChanged(parentNode);
        }
      }
      /*
       *  reset size of parent (CD)
       */
     int size = getCDSize(parentNode);
     parentNode.setSize(convertKBtoMB(size));

    } 
  } 


  /*
   * Walks through the nodes and resets selections to current
   * settings based on getState from SolarisProducts
   *
   * Requires the cdNodes vector.
   */
  public void setCurrentNodeInfo() 
  {
    /*
     * Need to make sure that the productsChosen is
     * reset in the tree structure so that it does
     * not report a false number.
     */
    initializeNodeInfo();

    /*
     * Determine the settings for the buttons.
     */
    for (int i=0; i < cdNodes.size(); i++) {
      CDNode cdnode = (CDNode) cdNodes.elementAt(i);
      Vector solprods = (Vector) cdnode.getCDproducts();
      for (int k=0; k < solprods.size(); k++) {
        SolarisProduct prod = (SolarisProduct) solprods.elementAt(k);
        boolean prodselected = prod.getState();
        CDNode tempNode = (CDNode)cdnode.getChildAt(k);
        if (prodselected) {
          tempNode.setNodeSelected(prodselected);
          cdnode.setProductChosen(prodselected);
          cdnode.checkPartialState();
        }
        /*
         * size of an individual product might change if
         * products were selected/deselected
         */
        int size = SolarisFileSystem.getProductSizeinKB(prod);
        tempNode.setSize(convertKBtoMB(size));
        if (wizardManager.hasGUI()) {
          ((DefaultTreeModel)prodTree.getModel()).nodeChanged(tempNode);
        }

      }
      int size = getCDSize(cdnode);
      cdnode.setSize(convertKBtoMB(size));
      if (wizardManager.hasGUI()) {
        ((DefaultTreeModel)prodTree.getModel()).nodeChanged(cdnode);
      }
    }
  }



  /*
   * Walks through the nodes and basically 'deselects' everything
   *
   * Requires the cdNodes vector.
   */
  public void initializeNodeInfo() 
  {
    /*
     * Determine the settings for the buttons.
     */
    for (int i=0; i < cdNodes.size(); i++) {
      CDNode cdnode = (CDNode)cdNodes.elementAt(i);
      int cdProdNum = cdnode.getChildCount();
      if (cdnode.isNodeSelected())
        cdnode.setNodeSelected(false);
      if (cdnode.isPartialSelected())
        cdnode.setPartialSelected(false);

      for (int k=0; k < cdProdNum; k++) {
        CDNode tempNode = (CDNode)cdnode.getChildAt(k);
        if (tempNode.isNodeSelected()) {
          tempNode.setNodeSelected(false);
          cdnode.setProductChosen(false);
        }
      }
    }
  }

  public String getCDMouseText(String cdname) 
  {
    Route objectRoute = getRoute().getChildServerRoute("profileObject");

    String helpText =
         (String)wizardManager.callServerObjectMethod(
          objectRoute.copy(),
          "getCDMouseText",
          new String[] {"java.lang.String.class"},
          new Object[] {cdname});

    return(helpText);
  }


  public String getprodMouseText(SolarisProduct prod) 
  {
    Route objectRoute = getRoute().getChildServerRoute("profileObject");
    String filename = prod.getFilename().substring(
                                  ProfileConstants.pdFilePrefix.length());
    String helpText =
         (String)wizardManager.callServerObjectMethod(
          objectRoute.copy(),
          "getProdMouseText",
          new String[] {"java.lang.String.class"},
          new Object[] {filename});

    return(helpText);
  }


  /*
   *  If depend error is due to upgrade, deselect products
   *  that failed. 
   */
  public void handleUpgradeDependErrs(DependError errorInfo)
  {
    WizardTreeManager wizardManager = getTreeManager();
    Route objectRoute = getRoute().getChildServerRoute("profileObject");

    if (errorInfo.hasError()) {
      int depError = errorInfo.getErrorCode();
      if (depError == DependError.OS_NOT_VALID_UPGRADE) {
          /*
           * Get product names that failed dependency
           * checking
           */
        Vector failedProdNames = new Vector();
        Vector errinfo = errorInfo.getInfoVector();

        for (int i = 2; i < errinfo.size(); i++) {
          failedProdNames.addElement(errinfo.elementAt(i));
          if (Def.DEBUG) {
            System.out.println("handleUpgradeDependErrs: adding to "
                             + "failed products: " + errinfo.elementAt(i));
          }
        }

        for (int i=0; i < cdNodes.size(); i++) {
           /*
            * Deselect products for panel
            */
          CDNode cdnode = (CDNode) cdNodes.elementAt(i);
          Vector solprods = (Vector) cdnode.getCDproducts();
          for (int k=0; k < solprods.size(); k++) {
            SolarisProduct prod = (SolarisProduct) solprods.elementAt(k);
            if (failedProdNames.contains(prod.getName())) {
              /*
               * Reset the panel state of the product
               */
              prod.setState(false);
              CDNode tempNode = (CDNode)cdnode.getChildAt(k);
              if (prodTree != null) handleTreeSelection(prodTree, tempNode);
              if (Def.DEBUG) {
                System.out.println("handleUpgradeDependErrs: upgrade depend: "
                     + "deselecting on panel " + prod.getName());
              }
            }
          }
        }

        Enumeration allProdEnum = allProductList.elements();
        while (allProdEnum.hasMoreElements()) {
          SolarisProduct prod = (SolarisProduct)allProdEnum.nextElement();
          if (prod.file.startsWith(ProfileConstants.pdFilePrefix)) {
            if (failedProdNames.contains(prod.getName())) {
                /*
                 *  Set server side product state to not selected
                 */
              boolean defaultinstall = false;
              if (Def.DEBUG) {
                System.out.println("handleUpgradeDependErrs: upgrade depend: "
                             + "deselecting " + prod.getName());
              }
              wizardManager.callServerObjectMethod(
                    getRoute().getChildServerRoute("profileObject"),
                    "setProductState",
                    new String[] {"java.lang.String.class",
                                  "java.lang.String.class",
                                  "java.lang.Boolean.class" },
                    new Object[] {prod.getName(),
                                  prod.getCDname(),
                                  new Boolean(defaultinstall)});
            }
          }
        }
      }
    }

  }


  /*
   * checkForNewCDs that may have been added. Assumes that all files
   * have been moved to proper directories and SolarisProducts have
   * been instantiated (see ProfileUtil.addNewProducts)
   */
  public void checkForNewCDs()
  {
    WizardTreeManager wizardManager = getTreeManager();

    Route objectRoute = getRoute().getChildServerRoute("profileObject");

    Vector CDnamesNew =
        (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
        "getCDList",
        null, null);

    int CDnamesSize = CDnames.size();
    int CDnamesNewSize = CDnamesNew.size();

    /*
     * Size change indicates new CD(s). 
     */
    if (CDnamesSize != CDnamesNewSize) {
      if (Def.DEBUG) System.out.println("checkForNewCDs: CD size change was "
                         + CDnamesSize + " now " + CDnamesNewSize);
    
      for (int index = CDnamesSize; index < CDnamesNewSize; index ++) {
        String cdname = (String) CDnamesNew.elementAt(index);

        /*
         * get product list
         */
        if (Def.DEBUG) System.out.println("checkForNewCDs: "+ cdname);

        /*
         * Read the list of products for the new cd and
         * add new nodes. 
         */
        Vector solProducts = new Vector();
        solProducts =
            (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
            "getCDProductList",
            new String[] {"java.lang.String.class"},
            new Object[] { cdname });

        /*
         * Make sure OS dependency checking hasn't removed all
         * products from cd before adding cd to panel
         */
        if ((solProducts == null) || (solProducts.size() == 0)) {
          if (Def.DEBUG) System.out.println("ProductSelectionPanel: " +
                         "addCD: removing " +cdname);
          continue;
        }

        /*
         * add to CDnamesOrig so default install will retain added cds
         */
        CDnames.addElement(cdname);
        CDnamesOrig.addElement(cdname);

        CDNode parentNode = new CDNode(cdname, solProducts);


        Integer CDsource = 
             (Integer) wizardManager.callServerObjectMethod(
                   getRoute().getChildServerRoute("profileObject"),
                   "readCDsource",
                   new String[] {"java.lang.String.class"},
                   new Object[] {cdname});

        int source = CDsource.intValue();
        parentNode.setAddSource(source);

        if (source == CDNode.FILE_SYSTEM) {
          String addPath =
            (String) wizardManager.callServerObjectMethod(
                  getRoute().getChildServerRoute("profileObject"),
                  "readCDpath",
                  new String[] {"java.lang.String.class"},
                  new Object[] {cdname });

          if (addPath != null) parentNode.setAddPath(addPath);
        }

        /*
         * set date state (selected/deselected) flag
         */
        for (int i = 0; i < solProducts.size(); i++) {
          SolarisProduct prod = (SolarisProduct)solProducts.elementAt(i);
          String prodName = prod.getName();
          String prodDefault = null;
          if (prod.isInstalledByDefault()) {
            prodDefault = "YES";
            prod.setState(true);
          } else {
            prodDefault = "NO";
            prod.setState(false);
          }
          /*
           * add nodes corresponding to new CDs/prods
           */
          if (Def.DEBUG) System.out.println("adding childnode  "+prodName);
          CDNode childNode = new CDNode(prodName, prodDefault, parentNode);
          int size = SolarisFileSystem.getProductSizeinKB(prod);
          childNode.setSize(convertKBtoMB(size));
          parentNode.add(childNode);
        }
 
        /*
         * add CD Name/size to table
         */
        int size = getCDSize(parentNode);
        parentNode.setSize(convertKBtoMB(size));
        cdNodes.addElement(parentNode);
        rootNode.add(parentNode);

        if (Def.DEBUG) System.out.println("Adding CD cdname="+cdname);
      }

      /*
       * Reinitialize tree, add listener as in createUI
       *   so new nodes show up on panel
       */
      if (wizardManager.hasGUI()) {
        prodTree = new JTree(rootNode);
        cnr = new CheckNodeRenderer(checkedImg, uncheckedImg, partialCheckedImg,
                                     "CDNode");
        prodTree.setCellRenderer(cnr);
        prodTree.getSelectionModel().setSelectionMode(
                       TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
        prodTree.addMouseListener(new NodeSelectionListener(prodTree, this));
        prodTree.addMouseMotionListener(new MouseOverListener(prodTree, this));
        if (jsp != null) containerPanel.remove(jsp);    
        jsp = new JScrollPane(prodTree);
        containerPanel.add(jsp,"Center");

      }
      setCurrentNodeInfo();

      /*
       * update allProductList (panel version of products)
       */
      allProductList =
         (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
         "getProfileUtilProductList", null, null);
    } 

  }



  /**
   * Verify that the cluster is sufficient to install the products.
   * If not, bump up the cluster. Called from createUI, reset and 
   * isDisplayComplete.
   */
  public DependError doDependChecking()
  {
    WizardTreeManager wizardManager = getTreeManager();

    Route objectRoute = getRoute().getChildServerRoute("profileObject");

    DependError errorInfo = null;

    Vector allprodList =
         (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
         "getProfileUtilProductList", null, null);

    Vector selprodList =
         (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
         "getProfileUtilSelectedProdList", null, null);

    String currentCluster =
         (String)wizardManager.callServerObjectMethod(objectRoute.copy(),
         "getProfileCluster", null, null);

    Depend depend = 
          new Depend(selprodList, allprodList, currentCluster, isUpgrade());
    errorInfo = depend.getDependError();

    if (errorInfo.hasError()) {
      int depError = errorInfo.getErrorCode();
      switch (depError) {
        case  DependError.OS_NOT_VALID:
            /*
             * The default cluster is insufficient for the default
             * product list. Bump up the cluster per Depend.
             */
            String newCluster = (String)errorInfo.getInfoVector().elementAt(0);
            if (Def.DEBUG) System.out.println("\nProductSelectionPanel: " 
                             + " Bump cluster to " + newCluster);
            /*
             * Remove old cluster and add new one chosen by depend checking
             */
            wizardManager.callServerObjectMethod(objectRoute.copy(),
                "setNewCluster",
                new String[] {"java.lang.String.class",
                               "java.lang.String.class" },
                new Object[] {currentCluster, newCluster});
            break;
       
        case  DependError.OS_NOT_VALID_UPGRADE:
            if (Def.DEBUG) {
              System.out.println("\nProductSelectionPanel Depend "
                                + "OS_NOT_VALID_UPGRADE" );
            }
            break;

        default:
            if (Def.DEBUG) {
              System.out.println("\nProductSelectionPanel Unexpected Depend "
                                + "Error:" +depError);
            }
            break;
      }
    } else {
      if (Def.DEBUG) System.out.println("\nProductSelectionPanel Depend OK");
    }

    return (errorInfo);

  }


  /**
   * Returns true if user chose upgrade path in wizard, false if
   * initial install.
   */
  public boolean isUpgrade()
  {
    WizardTreeManager wizardManager = getTreeManager();

    Boolean choseUpgrade =
      (Boolean) wizardManager.callServerObjectMethod(
                      getRoute(),
                      "getData",
                      new String[]{"com.sun.wizards.core.Route.class",
                                   "java.lang.String.class"},
                      new Object[] {null, "UpgradeInstall"});


    if ((choseUpgrade != null) && choseUpgrade.booleanValue()) {
      if (Def.DEBUG) System.out.println("ProdSelPanel isUpgrade: true");
      return(true);
    } else {
      if (Def.DEBUG) System.out.println("ProdSelPanel isUpgrade: false");
      return(false);
    }
  }


  /**
   * Returns true if this ProductSelectionPanel is finished displaying.
   * This method can be used to verify user input.
   *
   * @return	true if the display of this ProductSelectionPanel is
   *		complete; false otherwise.
   */
  public boolean isDisplayComplete()
  {
    WizardTreeManager wizardManager = getTreeManager();

    /*
     * save the selected list 
     */
    Vector props = handleSelection();

    wizardManager.callServerObjectMethod(
                getRoute().getChildServerRoute("profileObject"),
                "saveSelection",
                new String[] {"java.util.Vector.class"},
                new Object[] {props});

    /*
     * do dependency checking (must come after handleSelection)
     */
    DependError errorInfo = null;
    errorInfo = doDependChecking();

    if (errorInfo.hasError()) {
      int depError = errorInfo.getErrorCode();
      if (depError == DependError.OS_NOT_VALID_UPGRADE) {
            /*
             * For the upgrade case, give the user a popup
             * letting them know cluster insufficient, but
             * allow them to go on to the next panel.
             */
        String dialogMessage = errorInfo.setDisplayMsg();

        String okString =
                MsgCat.getString("com.sun.solarisinstall.SolarisInstall",
                                  SolarisInstallResources.MSG_DEPEND_OK);
        wizardManager.displayQuery( this, dialogMessage,
                            new String[] { okString },
                            new String[] {"nextOK"});

        return(true);
      }
    }

    /*
     *  Save the changes
     */
    wizardManager.callServerObjectMethod(
                getRoute().getChildServerRoute("profileObject"),
                "saveProfileChanges",
                null, null);

    return(true);
  }


  public void nextOK()
  {
    return;
  }

  /**
   * Reset this component, given the specified information.
   * This method may be called when a user backtracks in
   * the client wizard tree and changes a fundamental setting.
   * The panel that enabled the modification might traverse
   * subsequent panels and call this method resynchronize
   * all panels with the user's choice.
   *
   * When this method is called, the user selected "Default" installation.
   * This panel must reset its install directory information.
   *
   * @param Object	The information required to perform a reset.
   */
  public void reset(Object info)
  {
    if (Def.DEBUG) {
       System.out.println("ProductSelection Panel reset");
    }

    WizardTreeManager wizardManager = getTreeManager();

    if (!createdUI) {
      /*
       * We need to have the cdNodes structure created - handled by
       * createUI.
       */
      createUI();

      /**
       * The createUI call in turn called this reset method,
       * the reset is complete, return now.
       */
      return;
    }

    /*
     * We want to reset all products to the default install values
     */
    CDnames = (Vector)CDnamesOrig.clone();

    /*
     * Reset nodes to default values (falls through if no panel)
     */
    initializeNodeInfo();
    for (int i=0; i < cdNodes.size(); i++) {
      CDNode cdnode = (CDNode) cdNodes.elementAt(i);
      Vector solprods = (Vector) cdnode.getCDproducts();
      for (int k=0; k < solprods.size(); k++) {
        SolarisProduct prod = (SolarisProduct) solprods.elementAt(k);
        /*
         * Reset the panel state of the product
         */
        if (prod.isInstalledByDefault()) {
          prod.setState(true);
        } else {
          prod.setState(false);
        }

        if (prod.isInstalledByDefault()) {
          CDNode tempNode = (CDNode)cdnode.getChildAt(k);
          tempNode.setNodeSelected(true);
          cdnode.setProductChosen(true);
          cdnode.checkPartialState();

          if (wizardManager.hasGUI()) {
            ((DefaultTreeModel)prodTree.getModel()).nodeChanged(cdnode);
          }
        }
      }
    }


    /*
     * Get all products
     */
    allProductList =
          (Vector)wizardManager.callServerObjectMethod(
          getRoute().getChildServerRoute("profileObject"),
          "getProfileUtilProductList", null, null);


    /*
     * Reset the state of the products on the server side 
     */
     boolean defaultinstall=false;
     Enumeration allProdEnum = allProductList.elements();

     Vector cdList =
             (Vector)wizardManager.callServerObjectMethod(
             getRoute().getChildServerRoute("profileObject"),
             "getCDList",
             null, null);

     while (allProdEnum.hasMoreElements()) {
       SolarisProduct prod = (SolarisProduct)allProdEnum.nextElement();
       if (prod.file.startsWith(ProfileConstants.pdFilePrefix)) {
         defaultinstall = prod.isInstalledByDefault();
         wizardManager.callServerObjectMethod(
              getRoute().getChildServerRoute("profileObject"),
              "setProductState",
              new String[] {"java.lang.String.class",
                            "java.lang.String.class",
                            "java.lang.Boolean.class" },
              new Object[] {prod.getName(),
                            prod.getCDname(),
                            new Boolean(defaultinstall)});
       }
     }

    /*
     * Do dependency checking 
     *
     * There shouldn't be a problem here, unless someone set
     * the pd files/default profile up incorrectly or
     * unless doing upgrade.
     */
    DependError errorInfo = null;
    errorInfo = doDependChecking();
    /*
     * deselect products that fail depend checking
     */
    handleUpgradeDependErrs(errorInfo);

    /*
     * save the default list in dispatch_file 
     */
    Vector props = setupSaveDefault();

    wizardManager.callServerObjectMethod(
                  getRoute().getChildServerRoute("profileObject"),
                  "saveSelection",
                  new String[] {"java.util.Vector.class"},
                  new Object[] {props});

    /*
     *  Save the changes
     */
    wizardManager.callServerObjectMethod(
                getRoute().getChildServerRoute("profileObject"),
                "saveProfileChanges",
                null, null);

  }



  /*
   * sets up vector for saveSelection when reset method called (default)
   */
  private Vector setupSaveDefault() 
  {
    /*
     *  sel will hold the "properties" returned by handleDefault
     */
    Vector sel = new Vector();

    Route objectRoute = getRoute().getChildServerRoute("profileObject");
    Vector productList = 
        (Vector)wizardManager.callServerObjectMethod(objectRoute.copy(),
        "getProfileUtilProductList", null, null);

    int cdIndex = getStartingCDNum();
    String cdname="";
    String cdPrefix = null;
    String currcdname="";
    int prodIndex = 0;
    int cdsize = 0;
    int cdentries = 0;
    Enumeration e = productList.elements();
    for (int index=0; index < productList.size(); index++) {
      SolarisProduct prod = (SolarisProduct)productList.elementAt(index);

      /*
       * list cds with selected products on that cd (don't list os)
       * Assumes ordered productlist
       */
      if (prod.file.startsWith(ProfileConstants.pdFilePrefix) &&
        prod.getState()) {
        cdname =  (String)prod.getCDname();
        if ( ! currcdname.equals(cdname)) {
          if (cdsize == 0 && cdentries > 0) {
            /*
             * remove entries from last cd if cd size was zero
             */
            for (int entry = 0; entry < cdentries; entry++) {
              if (Def.DEBUG) {
                System.out.println("setupSaveDefault A removing (zero size) "
                                     +sel.elementAt(sel.size()-1));
              }
              sel.removeElementAt(sel.size()-1);
            }
            cdIndex--;
          }
          /*
           * new cd, save the entry
           */
          currcdname = cdname;
          cdsize=0;
          prodIndex = 0;

          cdPrefix = "W" + Integer.toString(++cdIndex, 10);
          sel.addElement(cdPrefix + ".CDName=" + cdname);
          cdentries=1;
          int source = CDNode.CD;
          String addPath = null;
          for (int i=0; i < cdNodes.size(); i++) {
             CDNode cdnode = (CDNode) cdNodes.elementAt(i);
             if (cdnode.getName().equals(cdname)) {
               source = cdnode.getAddSource();
               addPath = cdnode.getAddPath();
               break;
             }
          }
          if (Def.DEBUG) System.out.println("Reset Setting AddSource to "
                                            + source + " for " + cdname);
          sel.addElement(cdPrefix + ".AddSource=" + source);
          cdentries++;
          if (source == CDNode.FILE_SYSTEM ) {
            sel.addElement(cdPrefix + ".AddPath=" + addPath);
            if (Def.DEBUG) System.out.println("Reset Setting AddPath to "
                                                +addPath );
            cdentries++;
          }

        }

        /*
         * now save fields for merlin install
         */
        if (prod.getState()) {
          /*
           * Don't save entries for zero size products
           */
          int prodsize = SolarisFileSystem.getProductSizeinKB(prod);
          if (prodsize == 0) continue;

          cdsize += prodsize;
          String prodPrefix = cdPrefix + ".Product" +
                          Integer.toString(++prodIndex, 10);
          sel.addElement(prodPrefix + ".Name=" + prod.getName());
          sel.addElement(prodPrefix + ".MiniRoot=" + 
                            (prod.isMiniRootInstall()?"YES":"NO"));
          sel.addElement(prodPrefix + ".PRODID=" + prod.getProdID());
          cdentries += 3;
          if (prod.getCompID() != null) {
            sel.addElement(prodPrefix + ".COMPID1=" + prod.getCompID());
            cdentries += 1;
          }
        }
      }
    }
    if (cdsize == 0 && cdentries > 0) {
      /*
       * remove entries from last cd if cd size was zero
       */
      for (int entry = 0; entry < cdentries; entry++) {
        if (Def.DEBUG) {
          System.out.println("setupSaveDefault B removing (zero size) "
                             +sel.elementAt(sel.size()-1));
        }
        sel.removeElementAt(sel.size()-1);
      }
    }
    return (sel);
  }


  private Vector handleSelection() 
  {
   /* 
    * The solProducts vector (obtained from the cdnode)
    * contains SolarisProducts for a chosen
    * CD and is used by this panel. 
    *
    * The install state of the server side products is changed 
    * by the setProductState method.
    */
    WizardTreeManager wizardManager = getTreeManager();

    /*
     *  sel will hold the "properties" returned by handleSelection
     */
    Vector sel = new Vector();
    boolean CDhasProdSelected = false;

    /*
     * first save the CD information
     */

    int cdIndex = getStartingCDNum();

    for (int i=0; i < cdNodes.size(); i++) {
      CDNode cdnode = (CDNode)cdNodes.elementAt(i);
      int prodSize = cdnode.getChildCount();
      Vector solprods = (Vector) cdnode.getCDproducts();

      boolean cdSelected = cdnode.isNodeSelected();
      String cdName = cdnode.getName();

      if (!cdSelected) {
        for (int k=0; k < prodSize; k++) {
          SolarisProduct prod = (SolarisProduct)solprods.elementAt(k);

          /*
           * CD is not selected, deselect all prods
           */
          //setCDinstall(cdName, "NO");
          prod.setState(false);
  
          wizardManager.callServerObjectMethod(
                    getRoute().getChildServerRoute("profileObject"),
                    "setProductState",
                    new String[] {"java.lang.String.class",
                                  "java.lang.String.class",
                                  "java.lang.Boolean.class" },
                    new Object[] {prod.getName(), 
                                  prod.getCDname(),
                                  Boolean.FALSE });
        }
      }  else {
        /*
         * CD is selected
         *   Use "rows" to keep track of how many entries 
         *   to delete if zero size CD
         */
        int rows = 0;
        CDhasProdSelected = false;
        //setCDinstall(cdName, "YES");
        String cdPrefix = "W" + Integer.toString(++cdIndex, 10);
        sel.addElement(cdPrefix + ".CDName=" + cdName);
        rows++;
        int source = cdnode.getAddSource();
        String addPath = cdnode.getAddPath();
        if (Def.DEBUG) System.out.println("Setting AddSource to " + source 
                                           + " for " + cdnode.getName());
        sel.addElement(cdPrefix + ".AddSource=" + source);
        rows++;
        if (source == CDNode.FILE_SYSTEM ) {
          sel.addElement(cdPrefix + ".AddPath=" + addPath);
          if (Def.DEBUG) System.out.println("Setting AddPath to "+addPath );
          rows++;
        }
 
        /*
         * save the product section (name, prodid, <compid>)
         */
        int prodIndex = 0;
        for (int k=0; k < prodSize; k++) {
          SolarisProduct prod = (SolarisProduct)solprods.elementAt(k);

          CDNode tempNode = (CDNode) cdnode.getChildAt(k);
          boolean prodSelected = tempNode.isNodeSelected();
          if (prodSelected) {
            prod.setState(true); 
          } else {
            prod.setState(false); 
          }
          /*
           * Save the state of the product in the server side 
           */
          wizardManager.callServerObjectMethod(
                  getRoute().getChildServerRoute("profileObject"),
                  "setProductState",
                  new String[] {"java.lang.String.class",
                                "java.lang.String.class",
                                "java.lang.Boolean.class" },
                  new Object[] {prod.getName(), 
                                prod.getCDname(),
                               new Boolean(prodSelected)});


          /*
           * now save fields for launcher install
           */
          if (prod.getState()) {
            /*
             * Don't save zero size products 
             */
            double ProdsizeinMB = convertKBtoMB(
                         SolarisFileSystem.getProductSizeinKB(prod));
            if (ProdsizeinMB != 0.0 ) {
              CDhasProdSelected = true;
              String prodPrefix = cdPrefix + ".Product" +
                                      Integer.toString(++prodIndex, 10);
              sel.addElement(prodPrefix + ".Name=" + prod.getName());
              sel.addElement(prodPrefix + ".MiniRoot=" + 
                           (prod.isMiniRootInstall()?"YES":"NO"));
              sel.addElement(prodPrefix + ".PRODID=" + prod.getProdID());
              rows += 3;
  	        if (prod.getCompID() != null) {
                sel.addElement(prodPrefix + ".COMPID1=" + prod.getCompID());
                rows++;
              }
            } else {
              if (Def.DEBUG)
                System.out.println("skipping zero size product"+prod.getName());
            }
          }
        }
        /*
         * CD was selected, but size is zero. Don't
         * save entries in product_table
         */
        double cdsize = getCDSize(cdnode);
        if (cdsize == 0.0) {
        if (Def.DEBUG) System.out.println("\nhandleSelection A: skipping "
                                    + "zero size " + cdName);
          for (int k=0; k < rows; k++) {
            if (Def.DEBUG) System.out.println("\nhandleSelection A: removing "
                              + "zero size " + sel.elementAt(sel.size()-1));
            sel.removeElementAt(sel.size()-1);
          }
          cdIndex--;
        } else {
          if (! CDhasProdSelected){
            /*
             * CD was selected, but no products selected. Don't
             * save entries in product_table
             */
            if (Def.DEBUG) System.out.println("\nhandleSelection B: removing "
                                            + sel.elementAt(sel.size()-1));
            sel.removeElementAt(sel.size()-1);
            cdIndex--;
          }
        }
      }
    }
    return (sel);
  }


  /*
   *   If the language cd needs to be installed, want to 
   *   start the other cd numbers at 2.
   */
  private int getStartingCDNum()
  {
    WizardTreeManager wizardManager = getTreeManager();

    Boolean haveLangTable = 
        (Boolean) wizardManager.callServerObjectMethod(
                  getRoute().getChildServerRoute("profileObject"),
                  "doesFileExist",
                  new String[] {"java.lang.String.class"},
                  new Object[] {ProfileConstants.langTable});

    if (haveLangTable.booleanValue()) return(1);
    return(0);
  }


  /*
   * Get the cumulative size (KB) of currently selected 
   * products on the CD.
   */
  private int getCDSize(CDNode parent )
  {
    int size = 0;
    Vector solProducts = parent.getCDproducts();
    for (int k = 0; k < solProducts.size(); k++) {
      SolarisProduct prod = (SolarisProduct)solProducts.elementAt(k);
      CDNode tempNode = (CDNode) parent.getChildAt(k);
      boolean prodSelected = tempNode.isNodeSelected();
      if (prodSelected) {
          size += SolarisFileSystem.getProductSizeinKB(prod);
      }
    }
    return size;
  }


  /**
   * The stopTimer method is used when the mouse has been
   * moved off of the current node.
   * 
   * This method is in this class so it can be called from
   * both inner classes.
   */
  public synchronized void stopTimer()
  {
    /*
     * Set the timerStopped flag. The timerThread will then
     * stop automatically.
     */
    timerStopped = true;

    while ( isTimerRunning() ) {
      /*
       * Wait for the timerThread to stop.
       */
      try 
      {
         Thread.sleep(50);
      }
      catch(InterruptedException ie)
      {
      }
    }
  }


  /**
   * This method is in this class so it can be called from
   * both inner classes.
   */
  public boolean isTimerRunning()
  {
    synchronized(threadLock) {
      return(timerThread != null);
    }
  }


  /*
   * Get the runtime classes required by this panel.
   */
  public void addRuntimeResources(Vector resourceVector)
  {
    super.addRuntimeResources(resourceVector);
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.ProductSelectionPanel"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.StaticSizePanel"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.ProductSelectionPanel$NodeSelectionListener"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.ProductSelectionPanel$MouseOverListener"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.CheckNodeRenderer"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.CheckNodeRenderer$TreeLabel"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.TreeHandler"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.CDNode"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.core.Depend"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.core.DependError"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.Updatable"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.SolarisFSObj"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.SolarisFileSystem"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.ProfileUtil"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.Profile"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.ProfileFile"});
    resourceVector.addElement(new String[] {null, "com.sun.solarisinstall.panels.SolarisProduct"});
    resourceVector.addElement(new String[] {null, "com.sun.wizards.awt.HeaderPanel"});
    resourceVector.addElement(new String[] {null, "com.sun.wizards.awt.FlowLabel"});
    resourceVector.addElement(new String[] {null, "com.sun.wizards.awt.ColumnLayout"});
  }



 /**
   * Inner class for the listener of the JTree to determine the path
   * we are on. Used a MouseAdapter so that we can avtually 'toggle'
   * nodes instead of merely 'selecting' nodes.
   * 
   * Also has mouseExited method which is used to note when mouse 
   * exits product area so mouse timer can be stopped.
   */
  class NodeSelectionListener extends MouseAdapter 
  {
    JTree tree;
    TreeHandler handler;
   
    NodeSelectionListener(JTree tree, TreeHandler handler)
    {
      this.tree = tree;
      this.handler = handler;
    }

    public void mouseClicked(MouseEvent e) 
    {
      /*
       * Don't handle double clicks here...
       */
      if (e.getClickCount() > 1)
        return;

      int x = e.getX();
      int y = e.getY();
      int row = tree.getRowForLocation(x, y);
      TreePath  path = tree.getPathForRow(row);

      if (path != null) {
        CDNode node = (CDNode)path.getLastPathComponent();

        /*
         * Handle the events here...
         */
        handler.handleTreeSelection(tree, node);

        /*
         * Tell the tree that we have changed the node
         */
        ((DefaultTreeModel)tree.getModel()).nodeChanged(node);
      }
    }

    public void mouseExited(MouseEvent e) 
    {
        lastnode = null;
        stopTimer();
    }

  } // End NodeSelectionListener



  class MouseOverListener extends MouseMotionAdapter implements Runnable
  {
    JTree tree;
    TreeHandler handler;
   
    MouseOverListener(JTree tree, TreeHandler handler)
    {
      this.tree = tree;
      this.handler = handler;
    }

    public void mouseMoved(MouseEvent e) 
    {
      int x = e.getX();
      int y = e.getY();
      int row = tree.getRowForLocation(x, y);
      TreePath  path = tree.getPathForRow(row);

      if (path != null) {
        CDNode node = (CDNode)path.getLastPathComponent();

        /*
         * Handle the events here...
         */
        if (node != lastnode) {
          lastnode = node;
          startTimer();
        }

      } else {
        lastnode = null;
        stopTimer();
      }  
    }

    /**
     * The run method is used to run a timer to decide when
     * it is appropriate to change mouseover help
     */
    public void run()
    {
      long startTime = System.currentTimeMillis();
      long currentTime = startTime;
  
      while (currentTime - startTime <= timeOut) {
        try {
           synchronized(threadLock) {
             timerThread.sleep(50);
           }
           currentTime = System.currentTimeMillis();
           if (timerStopped) {
             synchronized(threadLock) {
               timerThread = null;
               return;
             }
           }
        }
        catch(InterruptedException ie)
        {
        }
      }
     
      synchronized(threadLock) {
        timerThread = null;
        timerStopped = true;
        handler.handleMouseOver(tree, lastnode);
      }

    }


    /**
     * The startTimer method is used to start the mouseover help
     * timer. When the timer expires, it's time to change the
     * help message.
     */
    public synchronized void startTimer()
    {
      stopTimer();
      synchronized(threadLock) {
        timerThread = new Thread(this);
        timerStopped = false;
        timerThread.start();
      }
    }
  

  } // End MouseOverListener
 

}
