//
// CBROWTree.java
//
// The contents of this file are subject to the SNIA Public License Version 1.0
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
//      http://www.snia.org/resources/openSource.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is CBROWTree.java
//
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s): Heung-for Cheng
//
//
// This source uses the Solaris WBEM APIs version 2.2.
// Synchronization will be maintained with the Solaris WBEM APIS,
// which are being standardized through the Java Community Process.
//
// $Id: CBROWTree.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
//
//
package org.snia.wbemcmd.browser;

import java.util.Vector;
import java.util.Enumeration;
import java.awt.*;
import java.io.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.tree.*;
import org.snia.wbem.client.CIMClient;
import org.snia.wbem.cim.CIMObjectPath;
import org.snia.wbem.cim.CIMException;
import org.snia.wbem.cim.CIMClass;
import org.snia.wbem.cim.CIMInstance;
import org.snia.wbem.cim.CIMNameSpace;
import org.snia.wbem.cim.CIMValue;
import org.snia.wbem.cim.CIMQualifier;
import org.snia.wbem.cim.CIMParameter;
import org.snia.wbem.cim.CIMMethod;
import org.snia.wbem.cim.CIMProperty;
import org.snia.wbemcmd.mof.MOFParse;
import org.snia.wbemcmd.browser.query.QueryFrame;


//
// Displays main browser window
//
class CBROWTree {
    //
    // Property and method column indexes
    //
    private static final int PROP_COL_KEY = 0;
    private static final int PROP_COL_NAME = 1;
    private static final int PROP_COL_TYPE = 2;
    private static final int METHOD_COL_NAME = 0;
    private static final int METHOD_COL_RETVAL = 1;
    private static final int METHOD_COL_ARGS = 2;
    private static final int QUAL_COL_NAME = 0;
    private static final int QUAL_COL_VALUE = 1;

    CIMClient cc;
    CIMNameSpace cn;
    JFrame mainFrame;
    java.awt.FileDialog openFileDialog1;
    JTable propTable;       // lists properties
    JTable methodTable;     // lists methods
    JTable qualTable;       // lists qualifiers
    TableModel propNames;       // property names
    TableModel methodValues;    // method values
    TableModel qualValues;      // qualifier values
    CBROWNode root;
    CIMClass currentClass=null;
    CBROWInstance cbx;
    int currentQualifierIndex=-1;
    JTextArea qualifierDescriptionPane; // where qualifier text is expanded
    protected JTree tree;
    protected DefaultTreeModel treeModel;
    JMenuItem menuItemShow;     // menu item for Show (instances)
    JMenuItem menuItemDel;      // menu item for Class delete
    JComboBox instanceNameList; // Instance names
    JMenuItem MOFmenuItem;
    String classText;
    int currentMethod=-1;       // index for current method
    JMenuItem menuItemExecute;
    String selectedNameSpace=null;  // name of current namespace
    JMenuItem menuItemQuery;

    public Frame getFrame() {
        return((Frame)mainFrame);
    }


    public CBROWTree(CIMClient icc,CIMNameSpace icn,DialogStatus ds, String iclassText)
        throws CIMException {
        JMenuBar menuBar = constructMenuBar();

        classText=iclassText;
        cc=icc;
        cn=icn;
        loadTree(ds);
        //
        // verticalPane is the main pane
        //
        JPanel verticalPane=new JPanel();
        verticalPane.setLayout(new BoxLayout(verticalPane,BoxLayout.Y_AXIS));
        verticalPane.setAlignmentX(Component.LEFT_ALIGNMENT);

        //
        // Create the tree
        //
        treeModel = new DefaultTreeModel(root);
        tree = new JTree(treeModel);
        tree.addTreeSelectionListener(new TreeSelectionChangeListener());
        tree.setCellRenderer(new CBROWTreeCellRenderer());
        tree.setRowHeight(-1);// Make tree ask for the height

        //
        // Create namespace combo
        //
        String[] ipropNames=getNameSpaces();


        instanceNameList=new JComboBox(ipropNames);
        instanceNameList.setMaximumSize(new Dimension(150, 20));
        instanceNameList.setAlignmentX(Component.LEFT_ALIGNMENT);
        verticalPane.add(instanceNameList);
        //
        // Add listener to drop down list
        //
        instanceNameList.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JComboBox jc=(JComboBox)(e.getSource());
                selectNameSpace((String)jc.getSelectedItem());
            }
        });

        JScrollPane sp = new JScrollPane();
        sp.getViewport().add(tree);
        sp.setPreferredSize(new Dimension(200,200));


         JTabbedPane tabbedPane = new JTabbedPane();

        //
        // Create properties tab
        //
        propNames=new TableModel(new String[] {"Key","Name","Type"});
        propTable=new JTable(propNames);
        JScrollPane scrollpane1 = new JScrollPane(propTable);
        tabbedPane.addTab("Properties", null, scrollpane1);

        //
        // Create methods tab
        //
        methodValues=new TableModel(
            new String[] {"Name","Return Value","Parameters"});
        methodTable=new JTable(methodValues);
        JScrollPane scrollpane2 = new JScrollPane(methodTable);
        tabbedPane.addTab("Methods", null, scrollpane2);
        //
        // Handles method selections
        final ListSelectionModel methodsm=
                    methodTable.getSelectionModel();
        methodsm.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        methodsm.addListSelectionListener(new ListSelectionListener() {
           public void valueChanged(ListSelectionEvent e) {
            int newindex=methodsm.getMaxSelectionIndex();
            if (currentMethod==newindex || newindex==-1)
                return;
            currentMethod=newindex;
            menuItemExecute.setEnabled(true);
        }
        });



        //
        // Create Qualifiers tab
        qualValues=new TableModel(new String[] {"Name","Values"});
        qualTable=new JTable(qualValues);
        JScrollPane scrollpane3 = new JScrollPane(qualTable);


        qualifierDescriptionPane=new JTextArea();
        JScrollPane textScrollPane=
                new JScrollPane(qualifierDescriptionPane);
        textScrollPane.setVerticalScrollBarPolicy(
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        qualifierDescriptionPane.setEditable(false);
        qualifierDescriptionPane.setLineWrap(true);
        qualifierDescriptionPane.setWrapStyleWord(true);
        //
        // Handles instance selections for qualifiers
        //
        final ListSelectionModel lsm=qualTable.getSelectionModel();
        lsm.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        lsm.addListSelectionListener(new ListSelectionListener() {
          public void valueChanged(ListSelectionEvent e) {
            boolean forceinstanceupdate=false;
            int newindex=lsm.getMaxSelectionIndex();

            if (currentQualifierIndex==newindex || newindex==-1)
                return;

            currentQualifierIndex=newindex;
            qualifierDescriptionPane.setText(((String)
                (qualValues.getValueAt(
                currentQualifierIndex,QUAL_COL_VALUE))));
            }
        });


        JSplitPane splitPanequal = new JSplitPane(
                JSplitPane.VERTICAL_SPLIT,
                scrollpane3,textScrollPane);

        tabbedPane.addTab("Qualifiers", null, splitPanequal);

        JSplitPane splitPane = new JSplitPane(
                JSplitPane.HORIZONTAL_SPLIT,
                sp,tabbedPane);

        verticalPane.add(splitPane);

        mainFrame = new JFrame("openCIMOM Browser");
        mainFrame.getContentPane().add("Center", verticalPane);
        mainFrame.setJMenuBar(menuBar);
        mainFrame.setBackground(Color.lightGray);
        mainFrame.setSize(500, 500);

        Toolkit tk=Toolkit.getDefaultToolkit();
        Dimension screenDim=tk.getScreenSize();
        Dimension windowDim=mainFrame.getSize();
        mainFrame.setLocation((screenDim.width-windowDim.width)/2,
                    (screenDim.height-windowDim.height)/2);


        mainFrame.addWindowListener( new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
        });


        mainFrame.pack();
        mainFrame.show();

        //
        // Set up file dialog
        //
        openFileDialog1=new FileDialog(mainFrame);
        openFileDialog1.setMode(FileDialog.LOAD);
        openFileDialog1.setTitle("Open MOF");
    }


    private void loadTree(DialogStatus ds) throws CIMException {
        root = new CBROWNode(cn.getNameSpace());
        Enumeration classi=cc.enumClass(
                new CIMObjectPath(classText,cn.getNameSpace()),
                true,true);
        if (classi!=null) {
            Vector v=new Vector();

            while (classi.hasMoreElements()) {
                v.addElement(classi.nextElement());
            }

            while(v.size()>0) {
                CIMClass cc=(CIMClass)(v.elementAt(0));
                v.removeElementAt(0);
                root.addCIMClass(cc,v);
            }
        }
    }

    private String[] getNameSpaces(){
            String[] ipropNames;
        try {
            Enumeration elist=cc.enumNameSpace(
                    new CIMObjectPath("",(Vector)null),
                    CIMClient.DEEP);
            Vector v=new Vector();
            if (elist!=null) {
                while (elist.hasMoreElements())
                    v.addElement((String)(elist.nextElement()));
                    ipropNames=new String[v.size()];
                    for(int j=0;j<v.size();j++) {
                        ipropNames[j]=(String)(v.elementAt(j));
                    }
            } else {
                ipropNames=new String[1];
                ipropNames[0]=cn.getNameSpace();
            }
        } catch (CIMException ce) {
            ipropNames=new String[1];
            ipropNames[0]=cn.getNameSpace();
        }
        return(ipropNames);
    }





    //
    // Builds the menu
    //
    private JMenuBar constructMenuBar() {
        JMenu            menu;
        JMenuBar         menuBar = new JMenuBar();
        JMenuItem        menuItem;

        //
        // File menu
        //
        menu = new JMenu("File");
        menuBar.add(menu);
            menuItem = menu.add(new JMenuItem("Exit"));
        menuItem.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }});
        MOFmenuItem = menu.add(new JMenuItem("Open MOF"));
        MOFmenuItem.addActionListener(new loadMOF());
        MOFmenuItem = menu.add(new JMenuItem("Reload MOF"));
        MOFmenuItem.addActionListener(new loadMOF(true,false));

        MOFmenuItem = menu.add(new JMenuItem("Open MOF extended"));
        MOFmenuItem.addActionListener(new loadMOF(false,true));
        MOFmenuItem = menu.add(new JMenuItem("Reload MOF extended"));
        MOFmenuItem.addActionListener(new loadMOF(true,true));

        //
        // Instances menu item can't be selected until a user
        // selects a class
        //
        menu = new JMenu("Instances");
        menuBar.add(menu);
        menuItemShow = menu.add(new JMenuItem("Show"));
        menuItemShow.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            //
            // Now determine the position of the window
            //
            Point windowPos=mainFrame.getLocationOnScreen();
            windowPos.x += 30;
            windowPos.y+=30;
            if (currentClass!=null)
                    cbx=new CBROWInstance(cc,cn,
                        currentClass,windowPos);
        }});

        menuItemExecute = menu.add(new JMenuItem("Execute method"));
        menuItemExecute.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            executeMethod();
        }});
        menuItemExecute.setEnabled(false);

        menuItemQuery = menu.add(new JMenuItem("Execute query"));
        menuItemQuery.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    Point windowPos=mainFrame.getLocationOnScreen();
               //     QueryFrame frameQuery=new QueryFrame(cc, windowPos);
            QueryFrame frameQuery;
            if (currentClass!=null) {
                    frameQuery=new QueryFrame(cc,cn,
                        currentClass,windowPos);
                    frameQuery.pack();
                    frameQuery.show();
            }
        }});



        // Class menu item can't be selected until a user
        // selects a class
        //
        menu = new JMenu("Class");
        menuBar.add(menu);
        menuItemDel = menu.add(new JMenuItem("Delete"));
        menuItemDel.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (currentClass!=null) {
                        String cl = "";
                        try {
                            cl = currentClass.getName();

                            // Ask, in case the currentClass isn't set right
                            int rc =
                                JOptionPane.
                                showConfirmDialog(mainFrame,
                                                  "Class: " + cl + "\n",
                                                  "Delete Class?",
                                                  JOptionPane.OK_CANCEL_OPTION,
                                                  JOptionPane.QUESTION_MESSAGE);
                            if (rc == JOptionPane.YES_OPTION) {
                                // Delete the class and remake the class tree
                                CIMObjectPath op = new CIMObjectPath(cl);
                                cc.deleteClass(op);
                                try {
                                    loadTree(null);
                                }
                                catch (CIMException ce) {
                                    CBROW.cimErrorMsg(mainFrame,
                                                      "Failed to fetch enumeration of classes",
                                                      ce);
                                }
                                treeModel.setRoot(root);
                                mainFrame.validate();
                                mainFrame.repaint();
                                JOptionPane.
                                    showMessageDialog(mainFrame,
                                                      "deleted Class: " +
                                                      cl + "\n",
                                                      "Success",
                                                      JOptionPane.INFORMATION_MESSAGE);
                            }
                        }
                        catch(CIMException ce) {
                            JOptionPane.
                                showMessageDialog(mainFrame,
                                                  "Failed to delete the Class: "
                                                  + cl + "\n",
                                                  "Error",
                                                  JOptionPane.ERROR_MESSAGE);

                        }
                    }
                    else {
                        JOptionPane.
                            showMessageDialog(mainFrame,
                                              "No class was selected \n",
                                              "Error",
                                              JOptionPane.ERROR_MESSAGE);
                    }
                }});

        //
        // Name spaces menu
        //
        menu = new JMenu("Namespaces");
            menuBar.add(menu);
        menuItem = menu.add(new JMenuItem("Edit"));
        menuItem.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            //
            // Now determine the position of the window
            //
            Point windowPos=mainFrame.getLocationOnScreen();
            windowPos.x += 30;
            windowPos.y+=30;
            CBROWNamespaces nsDialog=
                new CBROWNamespaces(mainFrame,cc,cn,windowPos);
            String selectNameSpace=nsDialog.getNamespace();
            //
            // Now update the combo box that shows available
            // namespaces
            //
            String[] nameSpaces=getNameSpaces();
            instanceNameList.removeAllItems();
            for(int i=0;i<nameSpaces.length;i++)
                instanceNameList.addItem(nameSpaces[i]);
            selectNameSpace(selectNameSpace);
        }});


        //
        // Help menu
        //
        menu = new JMenu("Help");
            menuBar.add(menu);
        menuItem = menu.add(new JMenuItem("About"));
        menuItem.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(mainFrame,
             "openCIMOM Browser (c)1999-2001\n"+
             "version 0.7\n\n"+
             "Authors and contributors: \n"+
             "David Simons, Jef Poskanzer, Andrea Westerinen, \n"+
             "Kim Ranyard, Phil Sturdy, Jim Davis, Julie Schott,\n"+
             "Bapu Patil, Peter Rivera, Frank Piorko, Dan Dahl,\n"+
             "Hewlett-Packard, Viktor Mihajlovski, Michael Leizin,\n"+
             "Andrei Viktorov, Adrian Schuur, Mike LaRocca,\n"+
             "Heung-For Cheng, Mahendra Ramachandran, Angela Lee,\n"+
             "Bhaskar Vedamurthy, Scott Baker Joy Underhill,\n"+
             "Sharad Tripathi, Naveen Sharma, Jim Stawn,\n"+
             "Pam Wright,  Daniel Abramovich, Tim Woodall,\n"+
             "Jean Lamontagne, Robert St Jacques, Christine Wang\n"+
             "Quan Wang, Klaus Olszowi "+
             "and many others..\n"+
             "\n\n"+
             "This source uses the Solaris WBEM APIs dated June 1999.\n"+
             "Synchronization will be maintained with the Solaris\n"+
             "WBEM APIS, which are being standardized through the\n"+
             "Java Community Process.\n\n",
             "openCIMOM",
             JOptionPane.INFORMATION_MESSAGE);
        }});

        return menuBar;
    }

    private void selectNameSpace(String selectNameSpace) {
        if (selectNameSpace!=null && selectedNameSpace!=null
            && selectedNameSpace.equalsIgnoreCase(selectNameSpace))
            return;

        if (selectNameSpace!=null) {
            selectedNameSpace=selectNameSpace;
            cn=new CIMNameSpace(cn.getHost(),selectNameSpace);
            instanceNameList.setSelectedItem(selectNameSpace);
            try {
                loadTree(null);
            } catch (CIMException ce) {
                CBROW.cimErrorMsg(mainFrame,
                   "Failed to fetch enumeration of classes",ce);
                }
            treeModel.setRoot(root);
            mainFrame.validate();
            mainFrame.repaint();
        }
    }


    protected DefaultMutableTreeNode getSelectedNode() {
        TreePath   selPath = tree.getSelectionPath();
            if(selPath != null)
            return(DefaultMutableTreeNode)selPath.getLastPathComponent();
        return null;
    }

    class TreeSelectionChangeListener extends Object implements TreeSelectionListener    {
        public void valueChanged(TreeSelectionEvent e) {
            CIMClass cimClass;
            CIMObjectPath cop;
            Vector properties;      // CIMProperties
            Vector instanceMethods;     // CIMMethod

            propNames.clearColumn(PROP_COL_KEY);
            propNames.clearColumn(PROP_COL_NAME);
            propNames.clearColumn(PROP_COL_TYPE);
            methodValues.clearColumn(METHOD_COL_NAME);
            methodValues.clearColumn(METHOD_COL_RETVAL);
            methodValues.clearColumn(METHOD_COL_ARGS);
            qualValues.clearColumn(QUAL_COL_NAME);
            qualValues.clearColumn(QUAL_COL_VALUE);

            DefaultMutableTreeNode dft=getSelectedNode();
            //
            // Ensure an exception does not get thrown if the object is
            // a namespace (in which case there is no parent
            //
            if (dft!=null) {
                //
                // If the node has no parent it must be a namespace
                // rather than a class
                //
                if (dft.getParent() == null)
                    dft=null;
            }

            if (dft!=null) {
                cop=new CIMObjectPath(dft.toString(),
                        cn.getNameSpace());
                try {
                    cimClass=cc.getClass(cop,false);
                } catch (CIMException ce) {
                    CBROW.cimErrorMsg(mainFrame,"Failed to download class",ce);
                    cimClass=null;
                } catch (Exception exe) {
                    cimClass=null;
                }
            } else
                cimClass=null;

            if (cimClass==null) {
                propTable.revalidate();
                propTable.repaint();
                methodTable.revalidate();
                methodTable.repaint();
                qualTable.revalidate();
                qualTable.repaint();
                currentClass=null;
                return;
            }
            //
            // No methods can be executed until a method is first
            // selected on this new class
            //
            menuItemExecute.setEnabled(false);
            properties=cimClass.getAllProperties();

            for(int i=0;i<properties.size();i++) {
                CIMProperty cp=(CIMProperty)
                        (properties.elementAt(i));
                if (cp.isKey()) {
                    //
                    // Property is used as a key
                    //
                    propNames.addValue(PROP_COL_KEY,"KEY");
                } else
                    propNames.addValue(PROP_COL_KEY,"");
                propNames.addValue(PROP_COL_NAME,cp.getName());
                propNames.addValue(PROP_COL_TYPE,
                        cp.getType().toString());
            }
            propTable.revalidate();
            propTable.repaint();
            currentClass=cimClass;

            instanceMethods=currentClass.getAllMethods();

            for(int i=0;i<instanceMethods.size();i++) {
                Vector params;
                String args;
                CIMMethod cp=(CIMMethod)
                    (instanceMethods.elementAt(i));
                methodValues.addValue(METHOD_COL_NAME,cp.getName());
                methodValues.addValue(METHOD_COL_RETVAL,
                        cp.getType().toString());
                params=cp.getParameters();
                args=new String();
                for(int j=0;j<params.size();j++) {
                    CIMParameter nq=((CIMParameter)
                            (params.elementAt(j)));
                        if (j>0)
                        args=args.concat(",");
                    args=args.concat(nq.getType().toMOF()+
                            " "+nq.getName());

                    // args=args.concat(nq.toMOF());  /* if you need qualifiers... */
                }
                methodValues.addValue(METHOD_COL_ARGS,args);
            }
            methodTable.revalidate();
            methodTable.repaint();
            //
            // Renders qualifiers
            //
            Vector qualifiers=currentClass.getQualifiers();
            for(int i=0;i<qualifiers.size();i++) {
                Vector params;
                String args;
                CIMQualifier cp=(CIMQualifier)(qualifiers.elementAt(i));
                qualValues.addValue(QUAL_COL_NAME,cp.getName());
                CIMValue cv=cp.getValue();
                if (cv!=null)
                    qualValues.addValue(QUAL_COL_VALUE,
                                cv.toString());
                else
                    qualValues.addValue(QUAL_COL_VALUE,"");
            }

            qualTable.revalidate();
            qualTable.repaint();
            //
            // Update expanded text for selected qualifier
            //
            if (currentQualifierIndex>=0
               && currentQualifierIndex<qualifiers.size()) {
                String value=((String)(qualValues.getValueAt(
                    currentQualifierIndex,QUAL_COL_VALUE)));
                if (value!=null)
                    qualifierDescriptionPane.setText(value);
                else
                    qualifierDescriptionPane.setText("");
            } else
                qualifierDescriptionPane.setText("");

            return;
        }
    }

    //
    // Loads MOF from a file
    //
    class loadMOF extends Object implements ActionListener {
        boolean forceUpdate=false;
        boolean extendedMode=false;

        public loadMOF() {
        }

        public loadMOF(boolean update, boolean extended) {
            forceUpdate=update;
            extendedMode=extended;
        }

            public void actionPerformed(ActionEvent e) {
            final String name;
            final String dir;
            String pathname;

            Point windowPos=mainFrame.getLocationOnScreen();

            openFileDialog1.setLocation(windowPos.x+30,windowPos.y+30);
            openFileDialog1.show();
            name=openFileDialog1.getFile();
            if (name==null || name.equals(""))
                return;
            dir=openFileDialog1.getDirectory();

            pathname=new String(dir+name);

            Runnable doit=new Runnable() {
                public void run() {
                    CBROWMOFStatus cbs=new CBROWMOFStatus(mainFrame);
                    (new MOFParse()).mofParse(cc,cn,dir,name,cbs,
                       forceUpdate,extendedMode);
                    cbs.setMessage("Loading class enumeration");
                    cbs.removePercentage();
                    try {
                        loadTree(null);
                    } catch (CIMException ce) {
                        CBROW.cimErrorMsg(mainFrame,"Failed to fetch enumeration of classes",ce);
                    }
                    treeModel.setRoot(root);
                    mainFrame.validate();
                    mainFrame.repaint();
                    cbs.dispose();
                }
            };
            Thread t=new Thread(doit);
            t.start();
        }
    }

    //
    // Method execution
    //
    private void executeMethod() {
        String name=methodValues.getValueAt(currentMethod,0).toString();
        new CBROWMethod(mainFrame,currentClass,cc,
                        new CIMObjectPath(currentClass.getName(),cn.getNameSpace()),
                        name);
    }

}
