package ca.janeg.cb;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ListIterator;
import java.util.StringTokenizer;
import java.util.TreeMap;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 *  Builds and contains the JTree used to display the class heirarchy.
 *
 *  @author     Jane Griscti jane@janeg.ca
 *  @created    January 26, 2002
 */
class CBTreePanel extends JPanel {

    private ClassBrowser parent;
    private JTree tree                        = new JTree();
    private DefaultMutableTreeNode classTree;
    private DefaultMutableTreeNode pkgTree;
    private CBClassGroup classGroup;
    private Collection sortedClasses          = new ArrayList();


    /**
     *  Constructs a CBTreePanel object.
     *
     *  @param  frame   the ClassBrowser object to contain the panel
     *  @param  cbcg    the CBClassGroup to be displayed
     */
    CBTreePanel( final ClassBrowser frame, final CBClassGroup cbcg ) {
        super();
        parent = frame;
        classGroup = cbcg;

        buildPkgTree();
        buildClassTree();
        switchToPkgTree();

        tree.putClientProperty( "JTree.lineStyle", "Angled" );
        tree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
        tree.addTreeSelectionListener( new CBTreeListener() );

        JScrollPane jsp  = new JScrollPane( tree );
        jsp.setPreferredSize( new Dimension( 300, 500 ) );
        jsp.setMinimumSize( jsp.getPreferredSize() );
        add( jsp );
    }


    /**  Builds a tree model based on the class package names. */
    private void buildPkgTree() {
        DefaultMutableTreeNode top       = new DefaultMutableTreeNode( classGroup.getGroupName() );
        DefaultMutableTreeNode prevNode;
        DefaultMutableTreeNode node;

        String element;
        String key                       = "";
        StringBuffer keyBuf              = new StringBuffer( "" );
        String keyBufStr;
        TreeMap map                      = new TreeMap();
        prevNode = top;
        String[] pkgs                    = classGroup.getByPackageName();

        // build tree nodes
        for( int i = 0; i < pkgs.length; i++ ) {
            element = pkgs[i];
            keyBuf = new StringBuffer( element.length() );
            keyBufStr = "";

            StringTokenizer stok  = new StringTokenizer( element, "/" );
            ClassInfo data        = null;
            int tokenCount        = 0;

            while( stok.hasMoreTokens() ) {
                key = stok.nextToken();
                tokenCount++;

                keyBuf.append( key + '.' );
                keyBufStr = keyBuf.toString();

                if( map.containsKey( keyBufStr ) ) {
                    prevNode = (DefaultMutableTreeNode)map.get( keyBufStr );
                } else {
                    data = new ClassInfo( keyBufStr, key );
                    node = new DefaultMutableTreeNode( data );

                    // check for top level package names
                    if( tokenCount == 1 ) {
                        top.add( node );
                    } else {
                        prevNode.add( node );
                    }

                    prevNode = node;
                    map.put( keyBufStr, node );
                    sortedClasses.add( data );
                }
            }
        }

        pkgTree = top;
    }


    /*
     *  Builds a tree model based on the class names.<p>
     *
     *  Note: This is not built by using the CBClassGroup sorted classes. It
     *  uses the same ClassInfo objects created for the package tree.
     */
    private void buildClassTree() {

        Collections.sort( (ArrayList)sortedClasses,
            CBNameComparator.getInstance() );
        ListIterator liter               = ( (ArrayList)sortedClasses ).listIterator();
        DefaultMutableTreeNode classTop  = new DefaultMutableTreeNode( classGroup.getGroupName() );
        DefaultMutableTreeNode node;
        ClassInfo element;

        while( liter.hasNext() ) {
            element = (ClassInfo)liter.next();
            node = new DefaultMutableTreeNode( element );
            classTop.add( node );
        }

        sortedClasses = null;        // finished with sorted classes
        classTree = classTop;
    }


    /**
     *  Switches the JTree model to the sorted class tree model.
     *  The display is automatically updated.
     */
    void switchToClassTree() {
        DefaultTreeModel model  = (DefaultTreeModel)tree.getModel();
        model.setRoot( classTree );
        model.reload();
    }


    /**
     *  Switches the JTree model to the package name tree model.
     *  The display is  automatically updated.
     */
    void switchToPkgTree() {
        DefaultTreeModel model  = (DefaultTreeModel)tree.getModel();
        model.setRoot( pkgTree );
        model.reload();
    }


    /**
     *  The listener for the JTree contained in CBTreePanel.
     *
     *  @author     Jane Griscti jane@janeg.ca
     *  @created    January 26, 2002
     */
    private class CBTreeListener implements TreeSelectionListener {

        public void valueChanged( TreeSelectionEvent e ) {
            DefaultMutableTreeNode node  = (DefaultMutableTreeNode)
                tree.getLastSelectedPathComponent();

            if( node == null ) {
                return;
            }

            if( node.isLeaf() ) {
                ClassInfo classInfo  = (ClassInfo)node.getUserObject();
                parent.displayClassInfo( classInfo.qualifiedName );
            }
        }
    }


    /**
     *  Separates the class name from the package name and stores them
     *  separately. A ClassInfo object acts as a leaf node in the JTree.
     *
     *  @author     Jane Griscti jane@janeg.ca
     *  @created    January 5, 2002
     */
    class ClassInfo {

        String qualifiedName;
        String className;


        /**
         *  Constructs a new ClassInfo object
         *
         *  @param  fullpath  the fully qualifed class name
         *  @param  name      the simple class name
         */
        ClassInfo( String fullpath, String name ) {
            fullpath = fullpath.substring( 0, fullpath.length() - 1 );
            qualifiedName = fullpath;
            className = name;
        }

        public String getQualifiedName() {
            return qualifiedName;
        }


        /**
         *  Overrides Object.toString() to provide each node with a display
         *  name; that of the class it represents.
         *
         *@return    Description of the Returned Value
         */
        public String toString() {
            return className;
        }
    }

}