
package uk.co.wingpath.gui;

import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.event.Event;

public class TreePanel
{
    private final JPanel mainPanel;
    private final JPanel rightPanel;
    private final DefaultMutableTreeNode root;
    private final DefaultMutableTreeNode lastGroup;
    private final JTree tree;
    private final DefaultTreeModel treeModel;
    private final Pane rootPane;
    private final Map<String,Pane> panes;
    private final ValueEventSource listeners;
    private Pane selectedPane;
    private final ValueListener statusListener;

    public TreePanel (Action helpAction, boolean scrollable)
    {
        listeners = new ValueEventSource ();
        panes = new HashMap<String,Pane> ();

        mainPanel = new JPanel ();
        mainPanel.setLayout (new BorderLayout ());

        TextCard rootCard = new TextCard ("", "", helpAction, "Root pane");
        rootPane = new Pane (null, rootCard);
        selectedPane = rootPane;
        root = rootPane.node;
        treeModel = new DefaultTreeModel (root);
        tree = new JTree (treeModel);
        tree.getSelectionModel ().setSelectionMode (
            TreeSelectionModel.SINGLE_TREE_SELECTION);
        ToolTipManager.sharedInstance ().registerComponent (tree);
        tree.setCellRenderer (new Renderer (tree.getCellRenderer ()));
        tree.setVerifyInputWhenFocusTarget (false);
        lastGroup = root;

        if (scrollable)
        {
            JScrollPane scrollPane = new JScrollPane (tree);
            mainPanel.add (scrollPane, BorderLayout.WEST);
        }
        else
        {
            tree.setBorder (BorderFactory.createEtchedBorder ());
            mainPanel.add (tree, BorderLayout.WEST);
        }

        statusListener = new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    TreeCard card = (TreeCard) e.getSource ();
                    Pane pane = panes.get (card.getTag ());
                    treeModel.nodeChanged (pane.node);
                }
            };

        rightPanel = new JPanel ();
        rightPanel.setLayout (new CardLayout ());
        mainPanel.add (rightPanel, BorderLayout.CENTER);

        tree.addTreeSelectionListener (new TreeSelectionListener ()
            {
                public void valueChanged (TreeSelectionEvent e)
                {
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode)
                       tree.getLastSelectedPathComponent ();
                    if (node == null)
                        return;
                    selectedPane = (Pane) node.getUserObject ();
                    CardLayout layout = (CardLayout) rightPanel.getLayout ();
                    layout.show (rightPanel, selectedPane.getTag ());
                    selectedPane.selected ();
                    listeners.fireValueChanged (this);
                }
            });
    }

    public JPanel getPanel ()
    {
        return mainPanel;
    }

    public Action getHelpAction ()
    {
        for (Pane pane = selectedPane ; pane != null ; pane = pane.parent)
        {
            Action ha = pane.getHelpAction ();
            if (ha != null)
                return ha;
        }

        return null;
    }

    public JButton getDefaultButton ()
    {
        return selectedPane.getDefaultButton ();
    }

    public void setVisibleRowCount (int rows)
    {
        if (rows == 0)
            rows = panes.size () + 1;
        tree.setVisibleRowCount (rows);
    }

    public void setRootVisible (boolean visible)
    {
        tree.setRootVisible (visible);
    }

    public void setShowsRootHandles (boolean shows)
    {
        tree.setShowsRootHandles (shows);
    }

    public class Pane
    {
        private DefaultMutableTreeNode node;
        private Pane parent;
        private JComponent component;
        private TreeCard card;

        private Pane (Pane parent, TreeCard card)
        {
            this.parent = parent;
            this.card = card;
            component = card.getPanel ();
            component.setBorder (BorderFactory.createRaisedBevelBorder ());
            panes.put (card.getTag (), this);
            node = new DefaultMutableTreeNode (this);
        }

        @Override
        public String toString ()
        {
            return card.getName ();
        }

        private String getTag ()
        {
            return card.getTag ();
        }

        private JComponent getComponent ()
        {
            return component;
        }

        private String getToolTipText ()
        {
            return card.getToolTipText ();
        }

        private Action getHelpAction ()
        {
            return card.getHelpAction ();
        }

        private JButton getDefaultButton ()
        {
            return card.getDefaultButton ();
        }

        public Pane addPane (TreeCard card)
        {
            Pane pane = new Pane (this, card);
            node.add (pane.node);
            rightPanel.add (pane.getComponent (), card.getTag ());
            treeModel.nodeStructureChanged (root);

            card.addStatusListener (statusListener);

            return pane;
        }

        public void setSelected ()
        {
            TreePath path = new TreePath (node.getPath ());
            tree.setSelectionPath (path);
        }

        private void selected ()
        {
            card.selected ();
        }
    }

    public void setSelected (String tag)
    {
        Pane pane = panes.get (tag);
        if (pane != null)
            pane.setSelected ();
    }

    public void setSelected (int row)
    {
        tree.setSelectionRow (row);
    }

    public String getSelectedTag ()
    {
        return selectedPane.getTag ();
    }

    public int getSelectedRow ()
    {
        return tree.getMinSelectionRow ();
    }

    private class Renderer
        implements TreeCellRenderer
    {
        private TreeCellRenderer renderer;

        Renderer (TreeCellRenderer renderer)
        {
            this.renderer = renderer;
        }

        public Component getTreeCellRendererComponent (JTree tree, Object value,
           boolean selected, boolean expanded, boolean leaf, int row,
           boolean hasFocus)
        {
            JComponent component =
                (JComponent) renderer.getTreeCellRendererComponent (
                    tree, value, selected, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            Pane pane = (Pane) node.getUserObject ();
            component.setToolTipText (pane.getToolTipText ());
            if (component instanceof DefaultTreeCellRenderer)
            {
                DefaultTreeCellRenderer dtcr =
                    (DefaultTreeCellRenderer) component;
                dtcr.setBackgroundNonSelectionColor (
                    pane.card.hasError () ? Gui.COLOUR_BACKGROUND_ERROR :
                    pane.card.hasUnappliedChanges () ?
                        Gui.COLOUR_BACKGROUND_UNAPPLIED :
                    Gui.COLOUR_BACKGROUND_NORMAL);
            }
            return component;
        }
    }

    public void printSizes ()
    {
        String tallestMin = "";
        String widestMin = "";
        int maxMinWidth = 0;
        int maxMinHeight = 0;
        String tallestPref = "";
        String widestPref = "";
        int maxPrefWidth = 0;
        int maxPrefHeight = 0;

        System.out.println ();
        System.out.println ("TreePanel.rightPanel width " +
            rightPanel.getWidth () + " height " +
            rightPanel.getHeight () + ":");

        for (String tag : panes.keySet ())
        {
            Pane pane = panes.get (tag);
            JComponent jcomp = pane.getComponent ();
            int mw = (int) jcomp.getMinimumSize ().getWidth ();
            int mh = (int) jcomp.getMinimumSize ().getHeight ();
            int pw = (int) jcomp.getPreferredSize ().getWidth ();
            int ph = (int) jcomp.getPreferredSize ().getHeight ();
            System.out.println (pane.toString () + " " +
                mw + " " + mh + ", " + pw + " " + ph);
            if (mw > maxMinWidth)
            {
                maxMinWidth = mw;
                widestMin = pane.toString ();
            }
            if (mh > maxMinHeight)
            {
                maxMinHeight = mh;
                tallestMin = pane.toString ();
            }
            if (pw > maxPrefWidth)
            {
                maxPrefWidth = pw;
                widestPref = pane.toString ();
            }
            if (ph > maxPrefHeight)
            {
                maxPrefHeight = ph;
                tallestPref = pane.toString ();
            }
        }

        System.out.println ("Widest min: " + widestMin + " " + maxMinWidth);
        System.out.println ("Tallest min: " + tallestMin + " " + maxMinHeight);
        System.out.println ("Widest pref: " + widestPref + " " + maxPrefWidth);
        System.out.println ("Tallest pref: " + tallestPref + " " +
            maxPrefHeight);
    }

    public Pane addPane (TreeCard card)
    {
        return rootPane.addPane (card);
    }

    public void expandAll ()
    {
        for (int i = 0 ; i < tree.getRowCount () ; i++)
            tree.expandRow (i);
    }

    public void addValueListener (ValueListener l)
    {
        listeners.addListener (l);
    }

    public void removeValueListener (ValueListener l)
    {
        listeners.removeListener (l);
    }
}

