package org.statcato.help;

import javax.swing.*;
    
import javax.swing.JTree;
import javax.swing.tree.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.HyperlinkEvent;

import java.net.URL;
import java.io.IOException;
import java.awt.event.*;
import java.awt.*;
import java.util.Vector;

/**
 * Help Panel.  Contains documentation for the Statcato application.
 * 
 * @author Margaret Yau
 * @version %I%, %G%
 * @since 1.0
 */
public class HelpPanel extends JPanel
                      implements TreeSelectionListener, HyperlinkListener,
                        ActionListener {
    private JEditorPane htmlPane;
    private JTree tree;
    private URL helpURL = null;
    Vector<URL> history = new Vector<URL>(0);
    int historyIndex = -1;
    private JButton backButton, forwardButton;
    private static boolean DEBUG = false;

    //Optionally play with line styles.  Possible values are
    //"Angled" (the default), "Horizontal", and "None".
    private static boolean playWithLineStyle = false;
    private static String lineStyle = "Horizontal";
    
    //Optionally set the look and feel.
    private static boolean useSystemLookAndFeel = true;

    // help file name constants
    public static final String DEFAULT = "StatcatoHelp.html";
    public static final String INTRO_WELCOME = "introduction/welcome.html";
    public static final String INTRO_GETTING_STARTED = "introduction/getting_started.html";
    public static final String INTRO_TOOLBAR = "introduction/toolbar.html";
    public static final String INTRO_SHORTCUTS = "introduction/shortcuts.html";    
    public static final String WIN_WINDOWS = "windows/index.html";
    public static final String WIN_SESSION_WINDOW = "windows/log.html";
    public static final String WIN_Datasheet_WINDOW = "windows/datasheet.html";
    public static final String WIN_HISTORY_WINDOW = "windows/history.html";
    public static final String FILE = "file/index.html";
    public static final String FILE_MENU = "file/files.html";
    public static final String EDIT = "edit/index.html";
    public static final String EDIT_MENU = "edit/menu.html";
    public static final String DATA = "data/index.html";
    public static final String DATA_SORT = "data/sort.html";
    public static final String DATA_RANK = "data/rank.html";
    public static final String DATA_DISPLAY_DATA = "data/display.html";
    public static final String DATA_STANDARDIZE = "data/standardize.html";
    public static final String DATA_PATTERN = "data/pattern.html";
    public static final String DATA_NUMBER_SEQUENCE = "data/number_sequence.html";
    public static final String DATA_DATA_SEQUENCE = "data/data_sequence.html";
    public static final String DATA_RANDOM = "data/random.html";
    public static final String DATA_RANDOM_BINOMIAL = "data/binomial.html";
    public static final String DATA_RANDOM_INTEGER = "data/integer.html";
    public static final String DATA_RANDOM_NORMAL = "data/normal.html";
    public static final String DATA_RANDOM_UNIFORM = "data/uniform.html";
    public static final String DATA_RANDOM_USER = "data/samples_from_column.html";
    public static final String CALC = "calc/index.html";
    public static final String CALC_CALCULATOR = "calc/calculator.html";
    public static final String CALC_PROB = "calc/probability_distribution.html";
    public static final String CALC_NORMAL = "calc/normal_dist.html";
    public static final String CALC_STUDENT = "calc/t_dist.html";
    public static final String CALC_CHISQUARE = "calc/chisquare_dist.html";
    public static final String CALC_F = "calc/f_dist.html";
    public static final String CALC_UNIFORM = "calc/uniform_dist.html";
    public static final String CALC_BINOMIAL = "calc/binomial_dist.html";
    public static final String CALC_DISCRETE = "calc/discrete_dist.html";
    public static final String CALC_GEOMETRIC = "calc/geometric_dist.html";
    public static final String CALC_INTEGER = "calc/integer_dist.html";
    public static final String CALC_POISSON = "calc/poisson_dist.html";
    public static final String CALC_PVALUE = "calc/pvalue.html";
    public static final String CALC_HYTESTCONC = "calc/hytest_conclusion.html";
    public static final String CALC_FREQTABLE = "calc/freq_table.html";
    public static final String STAT = "stat/index.html";
    public static final String STAT_BASIC = "stat/basic/index.html";
    public static final String STAT_DESCRIPTIVE = "stat/basic/descriptive_statistics.html";
    public static final String STAT_COLUMN = "stat/basic/column_statistics.html";
    public static final String STAT_ROW = "stat/basic/row_statistics.html";
    public static final String STAT_NORMALITY = "stat/basic/normality_test.html";
    public static final String STAT_SAMPLE_SIZE = "stat/sample_size/index.html";
    public static final String STAT_SAMPLE_SIZE_MEAN = "stat/sample_size/mean.html";
    public static final String STAT_SAMPLE_SIZE_PROP = "stat/sample_size/prop.html";
    public static final String STAT_CI = "stat/ci/index.html";
    public static final String STAT_CI_1MEAN = "stat/ci/one_pop_mean.html";
    public static final String STAT_CI_1PROP = "stat/ci/one_pop_prop.html";
    public static final String STAT_CI_1VAR = "stat/ci/one_pop_var.html";
    public static final String STAT_CI_2MEAN = "stat/ci/two_pop_mean.html";
    public static final String STAT_CI_2PROP = "stat/ci/two_pop_prop.html";
    public static final String STAT_CI_2VAR = "stat/ci/two_pop_var.html";
    public static final String STAT_CI_MATCHED = "stat/ci/matched_pairs.html";
    public static final String STAT_HYTEST = "stat/hytest/index.html";
    public static final String STAT_HYTEST_MEAN = "stat/hytest/mean.html";
    public static final String STAT_HYTEST_PROP = "stat/hytest/prop.html";
    public static final String STAT_HYTEST_VAR = "stat/hytest/variance.html";
    public static final String STAT_HYTEST_2MEAN = "stat/hytest/2mean.html";
    public static final String STAT_HYTEST_2PROP = "stat/hytest/2prop.html";
    public static final String STAT_HYTEST_2VAR = "stat/hytest/2var.html";
    public static final String STAT_HYTEST_MATCHED = "stat/hytest/matched_pairs.html";
    public static final String STAT_CORREG = "stat/regression/index.html";
    public static final String STAT_REG2 = "stat/regression/two_variables.html";
    public static final String STAT_REGMULT = "stat/regression/multiple_regression.html";
    public static final String STAT_REGNONLIN = "stat/regression/nonlinear_models.html";
    public static final String STAT_MULTINOMIAL = "stat/multinomial/index.html";
    public static final String STAT_GOODNESS_OF_FIT = "stat/multinomial/goodness_of_fit.html";
    public static final String STAT_CONTINGENCY_TABLE = "stat/multinomial/contingency_table.html";
    public static final String STAT_ANOVA = "stat/anova/index.html";
    public static final String STAT_ANOVA1 = "stat/anova/one_way.html";
    public static final String STAT_ANOVA2 = "stat/anova/two_way.html";
    public static final String GRAPH = "graph/index.html";
    public static final String GRAPH_BARCHART = "graph/barchart.html";
    public static final String GRAPH_BOXPLOT = "graph/boxplot.html";
    public static final String GRAPH_DOTPLOT = "graph/dotplot.html";
    public static final String GRAPH_HISTOGRAM = "graph/histogram.html";
    public static final String GRAPH_NQP = "graph/nqp.html";
    public static final String GRAPH_PIECHART = "graph/piechart.html";
    public static final String GRAPH_SCATTERPLOT = "graph/scatterplot.html";
    public static final String GRAPH_STEMPLOT = "graph/stemplot.html";
    public static final String REFERENCES = "references.html";
    public static final String LICENSE = "license.html";
    
    
    /**                                                                                                          
     * Constructor.  Creates a split pane containing the documentation
     * navigation tree and HTML viewing pane.  Creates a navigation control panel
     * at the bottom.
     */
    public HelpPanel() {
        super(new BorderLayout());

        //Create the nodes.
        DefaultMutableTreeNode top =
            new DefaultMutableTreeNode(new BookInfo("Statcato Help", DEFAULT));
        createNodes(top);

        //Create a tree that allows one selection at a time.
        tree = new JTree(top);
        tree.getSelectionModel().setSelectionMode
                (TreeSelectionModel.SINGLE_TREE_SELECTION);

        //Listen for when the selection changes.
        tree.addTreeSelectionListener(this);

        if (playWithLineStyle) {
            System.out.println("line style = " + lineStyle);
            tree.putClientProperty("JTree.lineStyle", lineStyle);
        }

        //Create the scroll pane and add the tree to it. 
        JScrollPane treeView = new JScrollPane(tree);

        //Create the HTML viewing pane.
        htmlPane = new JEditorPane();
        htmlPane.setEditable(false);
        htmlPane.addHyperlinkListener(this);
        
        JScrollPane htmlView = new JScrollPane(htmlPane);

        //Add the scroll panes to a split pane.
        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splitPane.setLeftComponent(treeView);
        splitPane.setRightComponent(htmlView);

        Dimension minimumSize = new Dimension(100, 100);
        htmlView.setMinimumSize(minimumSize);
        treeView.setMinimumSize(minimumSize);
        splitPane.setDividerLocation(300); 
        splitPane.setPreferredSize(new Dimension(800, 600));

        //Add the split pane to this panel.
        add(splitPane, BorderLayout.CENTER);
        add(createControlPanel(), BorderLayout.PAGE_END);
        initHelp();        
    }

    /**
     * Create a panel containing navigation control buttons.
     * 
     * @return JComponent (a control panel)
     */
    private JComponent createControlPanel() {
        JPanel panel = new JPanel();
        backButton = new JButton("Back");
        backButton.addActionListener(this);
        forwardButton = new JButton("Forward");
        forwardButton.addActionListener(this);
        panel.add(backButton);
        panel.add(forwardButton);
        return panel;
    }
    
    /** 
     * Updates the HTML page in the viewing pane depending on whether
     * the back or forward button is pressed.  A vector is used to keep
     * track of viewing history.
     * 
     * @param e action event
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        URL url = null;
        if (e.getSource() == backButton) {
            // display the previous URL
            if (historyIndex > 0) {
                url = (URL) history.elementAt(historyIndex - 1);    
                
                try {
                    htmlPane.setPage(url);  
                    historyIndex--;
                    forwardButton.setEnabled(true);
                    if (historyIndex == 0)
                        backButton.setEnabled(false);
                } catch (IOException ioe) {
                    System.err.println("Attempted to read a bad URL: " + url);
                }
            }
        }
        else if (e.getSource() == forwardButton) {
            // display the next URL in history
            if (historyIndex < history.size() - 1) {
                url = (URL) history.elementAt(historyIndex + 1);
                
                try {
                    htmlPane.setPage(url);
                    historyIndex++;
                    backButton.setEnabled(true);
                    if (historyIndex == history.size()-1)
                        forwardButton.setEnabled(false);
                }
                catch (IOException ioe) {
                     System.err.println("Attempted to read a bad URL: " + url);
                }
            }           
        }
    }
    
    /** Required by TreeSelectionListener interface. */
    @Override
    public void valueChanged(TreeSelectionEvent e) {
        
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)
                           tree.getLastSelectedPathComponent();

        if (node == null) return;

        Object nodeInfo = node.getUserObject();
        
        if (node.getLevel() != 0) {
            BookInfo book = (BookInfo)nodeInfo;
            displayURL(book.bookURL);
            if (DEBUG) {
                System.out.print(book.bookURL + ":  \n    ");
            }
        } else {
            //System.out.println("valueChanged displayURL");
            //displayURL(helpURL); 
        }
        if (DEBUG) {
            System.out.println(nodeInfo.toString());
        }
    }

    @Override
    public void hyperlinkUpdate(HyperlinkEvent event) {  
        if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            displayURL(event.getURL());                
        }
    }


    /**
     * Initializes this help panel.
     */
    private void initHelp() {
        displayPage(DEFAULT);
        backButton.setEnabled(false);
    }

    /**
     * Displays the HTML page at the given file name in the viewing pane.
     * 
     * @param urlString file name
     */
    public void displayPage(String urlString) {
        URL url = getClass().getResource(urlString);
        if (url == null) {
            System.err.println("Couldn't open help file: " + urlString);
        }
        displayURL(url);
        searchAndExpand(urlString);
    }
    
    /** 
     * Displays the HTML page at the given URL in the viewing pane.
     * Maintains a history of pages viewed.
     * 
     * @param url HTML page URL
     */
    public void displayURL(URL url) {
        try {
            if (url != null) {
                if (history.size() > 99) { // have a max history length of 100
                    history.removeElementAt(0);
                }
                //System.out.println("add URL: "+ url.toString());
                
                // if there is forward history
                if (historyIndex < history.size() - 1) { 
                    // remove all forward history
                    for (int i = historyIndex+1; i < history.size(); ++i) {
                        history.removeElementAt(history.size()-1);
                    }                   
                }
                history.addElement(url);
                historyIndex++;
                htmlPane.setPage(url);
                if (historyIndex > 0)
                    backButton.setEnabled(true);
                if (historyIndex == history.size() - 1)
                    forwardButton.setEnabled(false);                
            } else { //null url
		htmlPane.setText("File Not Found");
                if (DEBUG) {
                    System.out.println("Attempted to display a null URL.");
                }
            }
        } catch (IOException e) {
            System.err.println("Attempted to read a bad URL: " + url);
        }
    }

    /**
     * Creates the navigation tree structure.
     * 
     * @param top top tree node
     */
    private void createNodes(DefaultMutableTreeNode top) {
        DefaultMutableTreeNode category = null;
        DefaultMutableTreeNode book = null;
        DefaultMutableTreeNode chapter = null;

        // ---------Category: Introduction---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Introduction", INTRO_WELCOME));
        top.add(category);

        // Introduction > Welcome
        book = new DefaultMutableTreeNode(new BookInfo
            ("Welcome", INTRO_WELCOME));
        category.add(book);

        // Introduction > Getting Started
        book = new DefaultMutableTreeNode(new BookInfo
            ("Getting Started", INTRO_GETTING_STARTED));
        category.add(book);

        // Introduction > Toolbar
        book = new DefaultMutableTreeNode(new BookInfo
                ("Toolbar", INTRO_TOOLBAR));
        category.add(book);
        
        // Introduction > Keyboard Shortcuts
        book = new DefaultMutableTreeNode(new BookInfo
                ("Keyboard Shortcuts", INTRO_SHORTCUTS));
        category.add(book);
        
        // ----------Category:Windows----------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Windows", WIN_WINDOWS));
        top.add(category);

        // Log and Datasheet Windows > Log Window
        book = new DefaultMutableTreeNode(new BookInfo
            ("Log Window", WIN_SESSION_WINDOW));
        category.add(book);

        // Log and Datasheet Windows > Datasheet Window
        book = new DefaultMutableTreeNode(new BookInfo
            ("Datasheet Window", WIN_Datasheet_WINDOW));
        category.add(book);

        // Log and Datasheet Windows > Dialog History Window
        book = new DefaultMutableTreeNode(new BookInfo
            ("Dialog History Window", WIN_HISTORY_WINDOW));
        category.add(book);
        
        // ---------------------Category: Files---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Files", FILE));
        top.add(category);        
        
        book = new DefaultMutableTreeNode(new BookInfo
                ("File Menu", FILE_MENU));
        category.add(book); 
        
        // ---------------------Category: Edit---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Edit Data", EDIT));
        top.add(category);
        
        // Edit Data > Edit Menu
        book = new DefaultMutableTreeNode(new BookInfo
            ("Edit Menu", EDIT_MENU));
        category.add(book); 

        // ---------------------Category: Manipulate Data---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Manipulate Data", DATA));
        top.add(category);
        
        // Manipulate Data > Sort
        book = new DefaultMutableTreeNode(new BookInfo
            ("Sort", DATA_SORT));
        category.add(book);  
        
        // Manipulate Data > Rank
        book = new DefaultMutableTreeNode(new BookInfo
            ("Rank", DATA_RANK));
        category.add(book);
        
        // Manipulate Data > Display
        book = new DefaultMutableTreeNode(new BookInfo
            ("Display Data", DATA_DISPLAY_DATA));
        category.add(book);
        
        // Manipulate Data > Standardize
        book = new DefaultMutableTreeNode(new BookInfo
            ("Standardize", DATA_STANDARDIZE));
        category.add(book);
        
        // Manipulate Data > Generate Patterned Data
        book = new DefaultMutableTreeNode(new BookInfo
                ("Generate Patterned Data", DATA_PATTERN));
        category.add(book);
        
        // Manipulate Data > Generate Patterned Data > Simple Number Sequence
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Simple Number Sequence", DATA_NUMBER_SEQUENCE));
        book.add(chapter);
        
        // Manipulate Data > Generate Patterned Data > Arbitrary Data Sequence
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Arbitrary Data Sequence", DATA_DATA_SEQUENCE));
        book.add(chapter);
        
        // Manipulate Data > Generate Random Data
        book = new DefaultMutableTreeNode(new BookInfo
                ("Generate Random Data", DATA_RANDOM));
        category.add(book);       
        
        // Manipulate Data > Generate Random Data > Binomial
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Binomial", DATA_RANDOM_BINOMIAL));
        book.add(chapter);
        
        // Manipulate Data > Generate Random Data > Integer
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Integer", DATA_RANDOM_INTEGER));
        book.add(chapter);
        
        // Manipulate Data > Generate Random Data > Normal
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Normal", DATA_RANDOM_NORMAL));
        book.add(chapter);
        
        // Manipulate Data > Generate Random Data > Uniform
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Uniform", DATA_RANDOM_UNIFORM));
        book.add(chapter);
        
        // Manipulate Data > Generate Random Data > Samples from Column
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Samples from column", DATA_RANDOM_USER));
        book.add(chapter);
        
        // ---------------------Category: Calculate Data---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Calculate Data", CALC));
        top.add(category);                 
        
        // Calculate > Calculator
        book = new DefaultMutableTreeNode(new BookInfo
                ("Calculator", CALC_CALCULATOR));
        category.add(book);
        
        // Calculate > Probability Distributions
        book = new DefaultMutableTreeNode(new BookInfo
                ("Probability Distributions", CALC_PROB));
        category.add(book);
        
        // Calculate > Probability Distributions > Normal
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Normal", CALC_NORMAL));
        book.add(chapter);
        
        // Calculate > Probability Distributions > Student's t
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Student's t", CALC_STUDENT));
        book.add(chapter);
        
        // Calculate > Probability Distributions > Chi-square
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Chi-square", CALC_CHISQUARE));
        book.add(chapter);
        
        // Calculate > Probability Distributions > F
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("F", CALC_F));
        book.add(chapter);        

        // Calculate > Probability Distributions > Uniform
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Uniform", CALC_UNIFORM));
        book.add(chapter); 
        
        // Calculate > Probability Distributions > Binomial
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Binomial", CALC_BINOMIAL));
        book.add(chapter); 
        
        // Calculate > Probability Distributions > Discrete
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Discrete", CALC_DISCRETE));
        book.add(chapter); 
        
        // Calculate > Probability Distributions > Geometric
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Geometric", CALC_GEOMETRIC));
        book.add(chapter); 
        
        // Calculate > Probability Distributions > Integer
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Integer", CALC_INTEGER));
        book.add(chapter);
        
        // Calculate > Probability Distributions > Poisson
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Poisson", CALC_POISSON));
        book.add(chapter); 
        
        // Calculate > P-value
        book = new DefaultMutableTreeNode(new BookInfo
            ("P-value", CALC_PVALUE));
        category.add(book);   
        
        // Calculate > Frequency Table
        book = new DefaultMutableTreeNode(new BookInfo
            ("Frequency Table", CALC_FREQTABLE));
        category.add(book);  
        
        // Calculate > Frequency Table
        book = new DefaultMutableTreeNode(new BookInfo
            ("Hypothesis Test Conclusion Tool", CALC_HYTESTCONC));
        category.add(book);  
        
        // ---------------------Category: Statistics---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Statistics", STAT));
        top.add(category);
        
        // Statistics > Basic Statistics
        book = new DefaultMutableTreeNode(new BookInfo
            ("Basic Statistics", STAT_BASIC));
        category.add(book);  
        
        // Statistics > Basic Statistics > Descriptive Statistics
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Descriptive Statistics", STAT_DESCRIPTIVE));
        book.add(chapter);

        // Statistics > Basic Statistics > Column Statistics
        chapter = new DefaultMutableTreeNode(new BookInfo
            ("Column Statistics", STAT_COLUMN));
        book.add(chapter);  
        
        // Statistics > Basic Statistics > Row Statistics
        chapter = new DefaultMutableTreeNode(new BookInfo
            ("Row Statistics", STAT_ROW));
        book.add(chapter);

        // Statistics > Basic Statistics > Normality Test
        chapter = new DefaultMutableTreeNode(new BookInfo
            ("Normality Test", STAT_NORMALITY));
        book.add(chapter);
        
        // Statistics > Sample Size 
        book = new DefaultMutableTreeNode(new BookInfo
                ("Sample Size Determination", STAT_SAMPLE_SIZE));
        category.add(book);
        
        // Statistics > Sample Size > 1-Population Mean
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Mean", STAT_SAMPLE_SIZE_MEAN));
        book.add(chapter);
        
       // Statistics > Sample Size > 1-Population Proportion
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Proportion", STAT_SAMPLE_SIZE_PROP));
        book.add(chapter);
        
        // Statistics > Confidence Intervals
        book = new DefaultMutableTreeNode(new BookInfo
                ("Confidence Intervals", STAT_CI));
        category.add(book);
        
        // Statistics > Confidence Intervals > 1-Population Mean
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Mean", STAT_CI_1MEAN));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > 1-Population Proportion
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Proportion", STAT_CI_1PROP));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > 1-Population Variance
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Variance", STAT_CI_1VAR));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > 2-Population Mean
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Mean", STAT_CI_2MEAN));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > 2-Population Proportion
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Proportion", STAT_CI_2PROP));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > 2-Population Variance
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Variance", STAT_CI_2VAR));
        book.add(chapter);
        
        // Statistics > Confidence Intervals > Matched Pairs
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Matched Pairs", STAT_CI_MATCHED));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests
        book = new DefaultMutableTreeNode(new BookInfo
                ("Hypothesis Tests", STAT_HYTEST));
        category.add(book);
        
        // Statistics > Hypothesis Tests > 1-Population Mean
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Mean", STAT_HYTEST_MEAN));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > 1-Population Proportion
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Proportion", STAT_HYTEST_PROP));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > 1-Population Variance
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("1-Population Variance", STAT_HYTEST_VAR));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > 2-Population Mean
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Mean", STAT_HYTEST_2MEAN));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > 2-Population Proportion
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Proportion", STAT_HYTEST_2PROP));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > 2-Population Variance
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("2-Population Variance", STAT_HYTEST_2VAR));
        book.add(chapter);
        
        // Statistics > Hypothesis Tests > Matched Pairs
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Matched Pairs", STAT_HYTEST_MATCHED));
        book.add(chapter);
        
        // Statistics > Correlation and Regression
        book = new DefaultMutableTreeNode(new BookInfo
                ("Correlation and Regression", STAT_CORREG));
        category.add(book);
        
        // Statistics > Correlation and Regression > Linear Two Variables
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Linear Two Variables", STAT_REG2));
        book.add(chapter);    
        
        // Statistics > Correlation and Regression > Multiple Regression
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Multiple Regression", STAT_REGMULT));
        book.add(chapter);    
        
        // Statistics > Correlation and Regression > Non-Linear Models
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Non-Linear Models", STAT_REGNONLIN));
        book.add(chapter); 
        
        // Statistics > Multinomial
        book = new DefaultMutableTreeNode(new BookInfo
            ("Multinomial", STAT_MULTINOMIAL));
        category.add(book);  

        // Statistics > Multinomial > Chi-Square Goodness-of-Fit
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Chi-Square Goodness-of-Fit", STAT_GOODNESS_OF_FIT));
        book.add(chapter);
        
        // Statistics > Multinomial > Chi-Square Contingency Table
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Chi-Square Contingency Table", STAT_CONTINGENCY_TABLE));
        book.add(chapter);     
        
        // Statistics > Analysis of Variance
        book = new DefaultMutableTreeNode(new BookInfo
            ("Analysis of Variance", STAT_ANOVA));
        category.add(book);  

        // Statistics > Analysis of Variance > One-way ANOVA
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("One-way ANOVA", STAT_ANOVA1));
        book.add(chapter);

        // Statistics > Analysis of Variance > Two-way ANOVA
        chapter = new DefaultMutableTreeNode(new BookInfo
                ("Two-way ANOVA", STAT_ANOVA2));
        book.add(chapter);
        
        // ---------------------Category: Graph---------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("Graph", GRAPH));
        top.add(category);              
        
        // Graph > Bar Chart
        book = new DefaultMutableTreeNode(new BookInfo
                ("Bar Chart", GRAPH_BARCHART));
        category.add(book);
        
        // Graph > Box Plot
        book = new DefaultMutableTreeNode(new BookInfo
                ("Box Plot", GRAPH_BOXPLOT));
        category.add(book);
        
        // Graph > Dot Plot
        book = new DefaultMutableTreeNode(new BookInfo
                ("Dot Plot", GRAPH_DOTPLOT));
        category.add(book);
        
        // Graph > Historgram
        book = new DefaultMutableTreeNode(new BookInfo
                ("Histogram", GRAPH_HISTOGRAM));
        category.add(book);
        
        // Graph > Normal Quantile Plot
        book = new DefaultMutableTreeNode(new BookInfo
            ("Normal Quantile Plot", GRAPH_NQP));
        category.add(book);
        
        // Graph > Pie Chart
        book = new DefaultMutableTreeNode(new BookInfo
            ("Pie Chart", GRAPH_PIECHART));
        category.add(book);
        
        // Graph > Scatterplot
        book = new DefaultMutableTreeNode(new BookInfo
                ("Scatterplot", GRAPH_SCATTERPLOT));
        category.add(book);
        
        // Graph > Stem-and-Leaf plot
        book = new DefaultMutableTreeNode(new BookInfo
                ("Stem-and-Leaf Plot", GRAPH_STEMPLOT));
        category.add(book);
        
        // ---------------------Category: Others-------------------
        category = new DefaultMutableTreeNode(new BookInfo
                ("References", REFERENCES));
        top.add(category);
        
        category = new DefaultMutableTreeNode(new BookInfo
                ("License", LICENSE));
        top.add(category);
    }
        
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event dispatch thread.
     */
    private static void createAndShowGUI() {
        if (useSystemLookAndFeel) {
            try {
                UIManager.setLookAndFeel(
                    UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                System.err.println("Couldn't use system look and feel.");
            }
        }

        //Create and set up the window.
        JFrame frame = new JFrame("Statcato Help");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        //Add content to the window.
        frame.add(new HelpPanel());

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }
    
    /**
     * Schedules a job for the event dispatch thread:  
     * creating and showing this application's GUI.
     */
    public void showPanel() {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    /**
     * Searches for a node with the given text in the URL and expands it 
     * if found.
     * 
     * @param text URL string
     */
    public void searchAndExpand(String text) {
        TreeNode[] path = search((DefaultMutableTreeNode)tree.getModel().getRoot(), text);
        
        if (path != null) {
            TreePath treePath = new TreePath(path);
            tree.scrollPathToVisible(treePath);
            tree.setSelectionPath(treePath);
        }
    }
    
    /**
     * Returns an array of TreeNode that goes from the root to node
     * containing the given object.
     * 
     * @param node tree node to start searching
     * @param object the string to search for in the node
     * @return an array of TreeNode
     */
    private TreeNode[] search (DefaultMutableTreeNode node, String object) {
        TreeNode[] path = null;

        if (node.getUserObject() instanceof BookInfo &&
                ((BookInfo)node.getUserObject()).bookURL.toString().endsWith(object)) {
            path = ((DefaultTreeModel)tree.getModel()).getPathToRoot(node);
        } else {
            int i = 0;
            int n = tree.getModel().getChildCount(node);
            
            while ((i < n) && (path == null)) {
                path = search((DefaultMutableTreeNode) tree.getModel().getChild(node, i), object);
                i++;
            }
        }
        return path;
    }    
    
    /**
     * A book information unit, containing the book name and URL.  
     */
    private class BookInfo {
        public String bookName;
        public URL bookURL;

        public BookInfo(String book, String filename) {
            bookName = book;
            bookURL = getClass().getResource(filename);
            if (bookURL == null) {
                System.err.println("Couldn't find file: "
                                   + filename);
            }
        }

        @Override
        public String toString() {
            return bookName;
        }
        /*
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof BookInfo && bookURL.equals(((BookInfo)obj).bookURL))
                return true;
            if (obj instanceof String && bookURL.equals((String)obj))
                return true;
            if (obj instanceof String && bookName.equals((String)obj))
                return true;
            return false;
        }
         * */
    }    
}