
package org.openspml.browser;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import javax.swing.table.*;
// avoid awt conflict
import java.util.List;

import org.openspml.util.*;
import org.openspml.message.*;

public class SchemaTree extends JPanel implements TreeSelectionListener {

    JSplitPane _split;
    JTree _tree;
    JTable _table;
    Object _tableSource;

    public SchemaTree() {       

        setLayout(new BorderLayout());
        _split = new JSplitPane();
        add(_split, BorderLayout.CENTER);

        _tree = new JTree();
        _tree.setModel(null);
	_tree.putClientProperty("JTree.lineStyle", "Angled");
	_tree.setRootVisible(false);
	_tree.setShowsRootHandles(true);
        TreeSelectionModel tsm = _tree.getSelectionModel();
        tsm.addTreeSelectionListener(this);

	CellRenderer tcr = new CellRenderer();
	tcr.setLeafIcon(null);
	tcr.setOpenIcon(null);
	tcr.setClosedIcon(null);
	// _tree.setCellRenderer(tcr);

	JScrollPane scroll = new JScrollPane();
        SwingUtil.add(scroll, _tree);
        // keep it from being squashed
	scroll.setPreferredSize(new Dimension(200, 0));
        _split.add(scroll, JSplitPane.LEFT);

        _table = new JTable();

	// If you want the table to track the size of the JScrollPane's
	// viewport, you have to set autoResizeMode to something
	// other than AUTO_RESISE_OFF.  If auto resize is off, the
	// table will be sized according to its preferred size
	// which may result in horizontal scrolling.  
	// Usually you want this set, the JScrollPane will expand
	// to fill the width of a dialog and we want the table
	// to go along with it.
	_table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);

	scroll = new JScrollPane(_table);
        _split.add(scroll, JSplitPane.RIGHT);
    }

    public void setSchemas(List schemas) {

	Node root = new Node("Schemas", schemas);
	DefaultTreeModel model = new DefaultTreeModel(root);
	_tree.setModel(model);
        _tree.repaint();
    }

    public void valueChanged(TreeSelectionEvent e) {

        if (e.isAddedPath()) {
            TreePath tp = e.getPath();
            Node node = (Node)tp.getLastPathComponent();
            Object obj = node.getUserObject();
            if (obj != _tableSource) {
                TableModel tm = new SchemaTableModel(obj);
                _table.setModel(tm);
                _tableSource = obj;

		// for some odd reason, you need to call this in order to 
		// get the columns resized after changing the model, otherwise
		// you get a table with equal sized columns, looks like
		// a default of about 10 characters

		_table.sizeColumnsToFit(-1);
		_table.getParent().invalidate();
		_table.getParent().repaint();

            }
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Node
    //
    //////////////////////////////////////////////////////////////////////

    public class Node extends DefaultMutableTreeNode {

        String _name;
        ImageIcon _icon;
        boolean _leaf;

        public Node(String name, Object obj) {

            _name = name;
            setUserObject(obj);

            if (obj == null) {
                _leaf = true;
            }
            else if (obj instanceof String) {
                _name = (String)obj;
                _leaf = true;
            }
            else if (obj instanceof List) {
                add(this, (List)obj);
            }
            else if (obj instanceof Schema) {
                Schema schema = (Schema)obj;
                _name = schema.getSchemaIdentifierString();

                // todo: major version, minor version, 
                // provider identifier, schema identifier

                add(new Node("Classes", schema.getObjectClassDefinitions()));
                add(new Node("Attributes", schema.getAttributeDefinitions()));
                add(new Node("Requests", schema.getExtendedRequestDefinitions()));
            }
            else if (obj instanceof ObjectClassDefinition) {
                ObjectClassDefinition def = (ObjectClassDefinition)obj;
                _name = def.getName();

                // todo: description, superiorClasses

                add(this, def.getMemberAttributes());

                Map props = def.getProperties();
                if (props != null)
                    add(new Node("Properties", props));
            }
            else if (obj instanceof ObjectClassDefinitionReference) {
                ObjectClassDefinitionReference ref = 
                    (ObjectClassDefinitionReference)obj;
                _name = ref.getName();
                _leaf = true;
                // todo: provider identifier, schema identifier
            }
            else if (obj instanceof AttributeDefinition) {
                AttributeDefinition def = (AttributeDefinition)obj;
                _name = def.getName();
                _leaf = true;
                // todo: description, type, multi

                Map props = def.getProperties();
                if (props != null)
                    add(new Node("Properties", props));
            }
            else if (obj instanceof AttributeDefinitionReference) {
                AttributeDefinitionReference ref = 
                    (AttributeDefinitionReference)obj;
                _name = ref.getName();
                _leaf = true;
                // todo: required, provider id, schema id
            }
            else if (obj instanceof ExtendedRequestDefinition) {
                ExtendedRequestDefinition def = (ExtendedRequestDefinition)obj;
                _name = def.getOperationIdentifierString();
                // todo: description, properties
                add(this, def.getParameters());
                List refs = def.getReturnValues();
                if (refs != null)
                    add(new Node("Returns", refs));

                Map props = def.getProperties();
                if (props != null)
                    add(new Node("Properties", props));
            }
            else if (obj instanceof Map) {
                // must be a properties map
                StringBuffer b = new StringBuffer();
                Iterator it = ((Map)obj).entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry)it.next();
                    b.setLength(0);
                    b.append(entry.getKey());
                    b.append("=");
                    b.append(entry.getValue());
                    add(new Node(b.toString(), null));
                }
            }
        }

        private void add(Node parent, List children) {
            if (children != null) {
                for (int i = 0 ; i < children.size() ; i++)
                    parent.add(new Node(null, children.get(i)));
            }
        }

        public void setIcon(ImageIcon icon) {
            _icon = icon;
        }

        public ImageIcon getIcon() {
            return _icon;
        }

        /**
         * TreeNode implementation that says if we allow children.
         */
        public boolean getAllowsChildren() {

            return !_leaf;
        }

        /**
         * TreeNode implementation that says if we're a leaf.
         */
        public boolean isLeaf() {
            return _leaf;
        }

        /**
         * Return the trimmed name of this file/directory for display
         * in the tree view.
         */
        public String toString() {
            return _name;
        }
        
    }

    //////////////////////////////////////////////////////////////////////
    //
    // TreeCellRenderer
    //
    //////////////////////////////////////////////////////////////////////

    private class CellRenderer extends DefaultTreeCellRenderer {

        public CellRenderer() {
        }

        public Component getTreeCellRendererComponent(JTree tree,
						      Object value,
						      boolean sel,
						      boolean expanded,
						      boolean leaf,
						      int row,
						      boolean hasFocus) {

            super.getTreeCellRendererComponent(tree, value, sel,
					       expanded, leaf, row,
					       hasFocus);
	    if (value instanceof Node) {
		Node node = (Node)value;
		ImageIcon icon = node.getIcon();
		if (icon != null)
		    setIcon(icon);
	    }

            return this;
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // TableModel
    //
    //////////////////////////////////////////////////////////////////////

    public class SchemaTableModel extends DefaultTableModel {

        public SchemaTableModel(Object obj) {

	    addColumn("attribute");
	    addColumn("value");
            
            if (obj instanceof Schema) {
                Schema schema = (Schema)obj;

                add("Major Version", schema.getMajorVersion());
                add("Minor Version", schema.getMinorVersion());
                add("Provider Identifier", 
                    schema.getProviderIdentifierString());
                add("Schema Identifier",
                    schema.getSchemaIdentifierString());
            }
            else if (obj instanceof ObjectClassDefinition) {
                ObjectClassDefinition def = (ObjectClassDefinition)obj;

		add("name", def.getName());
		add("description", def.getDescription());
		// could display superclass list, but in practice won't
		// have any
            }
            else if (obj instanceof ObjectClassDefinitionReference) {
                ObjectClassDefinitionReference ref = 
                    (ObjectClassDefinitionReference)obj;

                add("Provider Identifier", 
                    ref.getProviderIdentifierString());
                add("Schema Identifier",
                    ref.getSchemaIdentifierString());
            }
            else if (obj instanceof AttributeDefinition) {
                AttributeDefinition def = (AttributeDefinition)obj;

		add("name", def.getName());
		add("type", def.getType());
		add("multi", (def.isMulti()) ? "true" : "");
		add("description", def.getDescription());
            }
            else if (obj instanceof AttributeDefinitionReference) {
                AttributeDefinitionReference ref = 
                    (AttributeDefinitionReference)obj;

                add("Provider Identifier", 
                    ref.getProviderIdentifierString());
                add("Schema Identifier",
                    ref.getSchemaIdentifierString());
		add("name", ref.getName());
                add("Required", (ref.isRequired()) ? "true" : "false");

		AttributeDefinition def = ref.getDefinition();
		if (def != null) {
		    add("type", def.getType());
		    add("multi", (def.isMulti()) ? "true" : "");
		    add("description", def.getDescription());
		}
            }
            else if (obj instanceof ExtendedRequestDefinition) {
                ExtendedRequestDefinition def = (ExtendedRequestDefinition)obj;

		add("name", def.getOperationIdentifierString());
		add("description", def.getDescription());
            }
        }

        private void add(String name, String value) {
            Vector v = new Vector();
            v.add(name);
            v.add(value);
            addRow(v);
        }

    }


}
