/*
** FILE:   xc_NodeCustomizer.java
**
** (c) 1998 Steve Withall.
**
** HISTORY:
**    21Apr98  stevew  Created.
*/
package xc;

import xg.xg_Node;

import xa.xa_NodeTypeChoiceList;

import eh.eh_Debug;

import com.sun.java.swing.table.AbstractTableModel;
import com.sun.java.swing.table.TableModel;

import com.sun.java.swing.text.DefaultStyledDocument;

import com.sun.java.swing.JPanel;
import com.sun.java.swing.JScrollPane;
import com.sun.java.swing.JTabbedPane;
import com.sun.java.swing.JTable;
import com.sun.java.swing.JTextArea;
import com.sun.java.swing.JViewport;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;

import java.io.StringWriter;

import java.lang.Integer;

//*****************************************************************************
/**
 * xc_NodeCustomizer is a JPanel for editing an xg_Node.
 */
public class xc_NodeCustomizer extends JPanel
{
	/** The document currently being worked on. */
	protected xg_Node      CurrentNode              = null;

    /** Tabbed pane for the display of the various aspects of a node (and any
     *  other which derived classed may add). */
    protected JTabbedPane  EditTabbedPane           = null;

    // Attributes relating to the display of the node's attributes.
    /** Panel for the display of the element's attributes. */
    JPanel                 InternalsTab             = null;

    /** Table displaying the node's internal (low-level) details. */
    JTable                 InternalsTable           = null;

    /** Model of the internal data for display in the InternalsTable. */
    InternalsTableModel    TheInternalsTableModel   = null;

    /** Scroller for the table of internal details. */
    JScrollPane            InternalsTableScrollPane = null;

    // Attributes relating to the display of the node's source.
    /** Panel for the display of the node's source. */
    JPanel                 SourceTab                = null;

    /** The XML source text area itself. */
    JTextArea              SourceTextArea           = new JTextArea();

    /** List from which to obtain node type names. */
    xa_NodeTypeChoiceList  TheNodeTypeChoiceList    = new xa_NodeTypeChoiceList();

    /** Document for the source of the node currently being customized. */
    DefaultStyledDocument  NodeSourceDocument       = new DefaultStyledDocument();

    //*****************************************************************************
    /**
     * Default constructor.
     */
    public xc_NodeCustomizer()
    {
//	    eh_Debug.add(7, "xc_NodeCustomizer: Constructor"); //TEMP
        EditTabbedPane = new JTabbedPane();
        EditTabbedPane.setBackground(Color.lightGray);

        addTabs();  // Added all required tabs

    	setLayout(new BorderLayout());
    	add("Center", EditTabbedPane);
    }

    //*****************************************************************************
    /**
     * Add all required tabs - which is expected to be overridden in derived
     * classes (which is why its contents are not merely called directly from the
     * constructor).
     */
    public void addTabs()
    {
        addInternalsTab();
        addSourceTab();
    }

    //*****************************************************************************
    /**
     * Add 'Internals' tab to display mainly derivative information about the node
     * (ie. largely calculated values - not its prime content).
     */
    public void addInternalsTab()
    {
//	    eh_Debug.add(7, "xc_NodeCustomizer.addInternalsTab:"); //TEMP
        InternalsTab = new JPanel();
	    InternalsTab.setLayout(new BorderLayout());
        EditTabbedPane.addTab("Internals", InternalsTab);

        // Set up table for the internals.
        TheInternalsTableModel   = new InternalsTableModel();
        InternalsTable           = new JTable(TheInternalsTableModel);
        InternalsTableScrollPane = new JScrollPane(InternalsTable);
        InternalsTable.setBackground(Color.lightGray);
        InternalsTab.add(InternalsTableScrollPane, BorderLayout.CENTER);
    }

    //*****************************************************************************
    /**
     * Add 'Source' tab to display the XML source of the node.
     */
    public void addSourceTab()
    {
//	    eh_Debug.add(7, "xc_NodeCustomizer.addSourceTab:"); //TEMP
        SourceTab = new JPanel();
	    SourceTab.setLayout(new BorderLayout());
        EditTabbedPane.addTab("Source", SourceTab);

	    // Initialise the source edit JTextArea.
	    SourceTextArea.setFont(new Font("Courier", Font.PLAIN, 12));
	    SourceTextArea.setTabSize(4);
	    SourceTextArea.setDocument(NodeSourceDocument);
	    SourceTextArea.setEditable(false);
	    SourceTextArea.setBorder(null);
        SourceTextArea.setBackground(Color.lightGray);

        // Scroller.
    	JScrollPane  SourceScrollPane = new JScrollPane();
    	JViewport    SourceViewport   = SourceScrollPane.getViewport();
    	SourceViewport.add(SourceTextArea);
   	    SourceViewport.setBackingStoreEnabled(false);

    	JPanel       SourcePanel = new JPanel();
    	SourcePanel.setLayout(new BorderLayout());
    	SourcePanel.add("Center", SourceScrollPane);
        SourceTab.add(SourcePanel, BorderLayout.CENTER);
    }

    //*****************************************************************************
    /**
     * Generate the source of the current node, for display in the source tab.
     */
    public void generateSource()
    {
//	    eh_Debug.add(8, "xc_NodeCustomizer.generateSource: for node " + CurrentNode); //TEMP

        try
        {
            // Clear the node source document.
            NodeSourceDocument.remove(NodeSourceDocument.getStartPosition().getOffset(),
                                      NodeSourceDocument.getLength() );

            // Save the node as a string, then move it to the source document.
            //TBD It is suggested to limit the size of sources displayable here (for
            //TBD performance reasons). The proposed way to do it is to sub-class
            //TBD StringWriter with a version that throws an exception after a given
            //TBD number of characters have been written.
            StringWriter  SourceWriter = new StringWriter();
            CurrentNode.save(SourceWriter);
            NodeSourceDocument.insertString(0, SourceWriter.toString(), null);
        }
        catch (Exception  InputException)
        {
            eh_Debug.add(3, "Cannot produce node document source: " + InputException);
        }
    }

    //*****************************************************************************
    /**
     * Set the node to edit.
     *
     * @param  InputNode  The node to edit
     */
    public void setNode(xg_Node  InputNode)
    {
        CurrentNode = InputNode;
        generateSource();
//        UpdateUI();
    }

    //*****************************************************************************
    /**
     * Set the index of the currently-selected tab.
     *
     * @param  The index of the tab to select
     */
    public void setSelectedIndex(int  InputTabIndex)
    {
        EditTabbedPane.setSelectedIndex(InputTabIndex);
    }

    //*****************************************************************************
    /**
     * Get the index of the currently-selected tab. The purpose of this is to
     * allow us to attempt to display the same tab for the next node. (It is
     * not ideal, because the index may mean something different for a different
     * type of node. The answer is to create a Vector of the tab names, get the
     * tab name instead of the index here, and set according to the tab name for
     * the next node.)
     *
     * @return  The index of the currently-selected tab
     */
    public int getSelectedIndex()
    {
        return EditTabbedPane.getSelectedIndex();
    }

    //*****************************************************************************
    /**
     * Inner class to provide a model for the internals for use in the table display.
     */
    class InternalsTableModel extends AbstractTableModel
    {
        //*****************************************************************************
        public int getColumnCount()
        {
            return 2;
        }

        //*****************************************************************************
        public int getRowCount()
        {
            return 7;
        }

        //*****************************************************************************
        public Object getValueAt(int InputRowNum, int InputColumnNum)
        {
//	        eh_Debug.add(7, "xc_NodeCustomizer.getValueAt: " + InputRowNum + ", "
//	                                                         + InputColumnNum); //TEMP
            Object  ValueObject = null;

            switch (InputColumnNum)
            {
                case 0:
                    ValueObject = getLabelValueAt(InputRowNum);
                    break;

                case 1:
                    ValueObject = getValueValueAt(InputRowNum);
                    break;

                default:
	                eh_Debug.add(5, "xc_NodeCustomizer.getValueAt: Invalid column number ("
	                                     + InputColumnNum + ") requested");
            }

            return ValueObject;
        }

        //*****************************************************************************
        /**
         * Get the value of the label for the given row (implicitly column 0).
         *
         * @param  InputRowNum  the number of the row whose label is required
         */
        public Object getLabelValueAt(int InputRowNum)
        {
//	        eh_Debug.add(7, "xc_NodeCustomizer.getLabelValueAt: " + InputRowNum); //TEMP
            Object  ValueObject = null;

            switch (InputRowNum)
            {
            case 0:
                ValueObject = new String("Node name");
                break;

            case 1:
                ValueObject = new String("Start position (in source)");
                break;

            case 2:
                ValueObject = new String("End position (in source)");
                break;

            case 3:
                ValueObject = new String("Number of children");
                break;

            case 4:
                ValueObject = new String("Node class");
                break;

            case 5:
                ValueObject = new String("Node type");
                break;

            case 6:
                ValueObject = new String("DOM node type");
                break;

            default:
	            eh_Debug.add(2, "xc_NodeCustomizer.getLabelValueAt: Invalid row number ("
	                                 + InputRowNum + ") requested");
                ValueObject = new String("*Unknown*");
            }

            return ValueObject;
        }

        //*****************************************************************************
        /**
         * Get the value for the given row (implicitly column 1).
         *
         * @param  InputRowNum  the number of the row whose value is required
         */
        public Object getValueValueAt(int InputRowNum)
        {
//	        eh_Debug.add(7, "xc_NodeCustomizer.getValueValueAt: " + InputRowNum); //TEMP
            Object  ValueObject = null;

            if (CurrentNode == null)
                ValueObject = new String("***");
            else
            {
                switch (InputRowNum)
                {
                case 0:
                    ValueObject = CurrentNode.getName();
                    break;

                case 1:
                    ValueObject = new Integer(CurrentNode.getStartOffset());
                    break;

                case 2:
                    ValueObject = new Integer(CurrentNode.getEndOffset());
                    break;

                case 3:
                    ValueObject = new Integer(CurrentNode.getElementCount() );
                    break;

                case 4:
                    Class  NodeClass = CurrentNode.getClass();
                    ValueObject = NodeClass.getName();
                    break;

                case 5:
                    int     NodeType     = CurrentNode.getType();
                    String  NodeTypeName = TheNodeTypeChoiceList.getChoice(NodeType);
                    if (NodeTypeName == null)
                        NodeTypeName = new String("*Unknown*");
                    ValueObject = new String(NodeType + " (" + NodeTypeName + ")");
                    break;

                case 6:
                    int     DomNodeType     = CurrentNode.getNodeType();
                    String  DomNodeTypeName = TheNodeTypeChoiceList.getChoice(DomNodeType);
                    if (DomNodeTypeName == null)
                        DomNodeTypeName = new String("*Unknown*");
                    ValueObject = new String(DomNodeType + " (" + DomNodeTypeName + ")");
                    break;

                default:
	                eh_Debug.add(2, "xc_NodeCustomizer.getValueValueAt: Invalid row number ("
	                                              + InputRowNum + ") requested");
                    ValueObject = new String("*Unknown*");
                }
            }

            return ValueObject;
        }

        //*****************************************************************************
        /**
         * Get the name for the given column.
         *
         * @param  InputColumnNum  the number of the column whose name is required
         */
        public String getColumnName(int InputColumnNum)
        {
            String  ColumnName = null;

            switch (InputColumnNum)
            {
                case 0:
                    ColumnName = new String("Name");
                    break;

                case 1:
                    ColumnName = new String("Value");
                    break;

                default:
                    ColumnName = new String("*Unknown*");
	                eh_Debug.add(5, "xc_NodeCustomizer.getColumnName: Invalid column number ("
	                                     + InputColumnNum + ") requested");
            }

            return ColumnName;
        }
    }
}

//*****************************************************************************
