/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * <BR><BR>
 *
 * Principal Author: John Thompson, NZDL Project, University of Waikato
 *
 * <BR><BR>
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * <BR><BR>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * <BR><BR>
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * <BR><BR>
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.gui;

import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.util.JarTools;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.XMLTools;
import org.w3c.dom.*;


/**
 * This class provides a nice help facility. It is a separate frame that can be positioned
 * as the user wishes, and provides a contents page and the help information.
 * @author Michael Dewsnip, NZDL Project
 */
public class HelpFrame 
    extends JFrame
{
    /** The size of the frame */
    static private final Dimension SIZE = new Dimension(800, 560);

    static private HelpFrame self = null;

    /** The help view at the bottom of the frame. */
    static private JEditorPane help_pane = null;
    /** The help contents tree at the top of the frame. */
    static private JTree help_contents_tree = null;
    /** The help contents tree model. */
    static private HelpContentsTreeModel help_contents_tree_model = null;
    /** The split between the contents tree and the page view. */
    static private JSplitPane split_pane = null;

    public HelpFrame()
    {
	setDefaultCloseOperation(HIDE_ON_CLOSE);
	setSize(SIZE);
	setTitle(Dictionary.get("Help.Title"));

	help_pane = new JEditorPane();
	help_pane.setEditable(false);
	help_pane.addHyperlinkListener(new HelpPaneHyperlinkListener());

	HelpContentsTreeNode help_tree_root_node = new HelpContentsTreeNode(null, Dictionary.get("Help.Contents"));
	help_contents_tree_model = new HelpContentsTreeModel(help_tree_root_node);
	help_contents_tree = new JTree((DefaultTreeModel) help_contents_tree_model);
	help_contents_tree.addTreeSelectionListener(new HelpContentsTreeSelectionListener());
	help_contents_tree.setExpandsSelectedPaths(true);

	// Creation
	JPanel content_pane = (JPanel) this.getContentPane();
	split_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
	JScrollPane help_contents_tree_scroll = new JScrollPane(help_contents_tree);
	JScrollPane help_pane_scroll = new JScrollPane(help_pane);

	// Layout
	split_pane.add(help_contents_tree_scroll, JSplitPane.LEFT);
	split_pane.add(help_pane_scroll, JSplitPane.RIGHT);
	content_pane.setLayout(new BorderLayout());
	content_pane.add(split_pane, BorderLayout.CENTER);

	// Center
	Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
	setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);

	// Pretty corner icon
	this.setIconImage(JarTools.getImage("gatherer_small.gif").getImage());

	self = this;
    }

    public void destroy()
    {
	help_contents_tree = null;
	help_pane = null;
    }


    /** Retrieve the relative file path to the language-specific help index xml file. */
    private String getHelpFolder()
    {
	String help_folder = "help/" + Configuration.getLanguage() + "/";

	// Applet case: get help files out of JAR file
	if (Gatherer.isApplet && JarTools.getResource("/" + help_folder) != null) {
	    return help_folder;
	}

	// Normal case: get help files out of GLI "help" folder
	if (new File(help_folder).exists()) {
	    return help_folder;
	}

	// Resort to English
	return "help/" + StaticStrings.ENGLISH_LANGUAGE_STR  + "/";
    }


    private URL getURLForSection(String section_name)
    {
	// Form the path to the help file from the section name
	if (section_name != null) {
	    String help_file_path = getHelpFolder() + section_name + StaticStrings.HTM_FILE_EXTENSION;

	    try {
		// Applet case: get help file out of JAR file
		if (Gatherer.isApplet) {
		    return JarTools.getResource("/" + help_file_path);
		}

		// Normal case: get help file out of GLI "help" folder
		return (new File(help_file_path)).toURL();
	    }
	    catch (Exception exception) {
		DebugStream.printStackTrace(exception);
	    }
	}

	return null;
    }


    static private void selectHelpContentsTreeNode(String section_name)
    {
	// Find the tree node with this section name, then select it
	selectHelpContentsTreeNode(help_contents_tree_model.getHelpContentsTreeNodeNamed(section_name));
    }


    static private void selectHelpContentsTreeNode(HelpContentsTreeNode help_tree_node)
    {
	// Ensure the node in the help contents tree is selected and visible
	TreePath help_tree_node_path = new TreePath(help_tree_node.getPath());
	help_contents_tree.setSelectionPath(help_tree_node_path);
	help_contents_tree.scrollPathToVisible(help_tree_node_path);
    }


    static public void setView(String section_name)
    {
	// Select the appropriate node in the help contents tree
	selectHelpContentsTreeNode(section_name);

	// Show the help page with the specified section name
	self.showHelpPage(section_name);
    }


    private void showHelpPage(String section_name)
    {
	// Find the tree node with this section name, then show the page for the node
	showHelpPage(getURLForSection(section_name));
    }


    private void showHelpPage(URL help_page_url)
    {
	// Display the selected page
	if (help_page_url != null) {
	    try {
		help_pane.setPage(help_page_url);
	    }
	    catch (Exception exception) {
		DebugStream.printStackTrace(exception);
	    }
	}

	// Make the help frame visible
	setVisible(true);
	split_pane.setDividerLocation(0.4);
    }


    private class HelpContentsTreeModel
	extends DefaultTreeModel
    {
	public HelpContentsTreeModel(MutableTreeNode help_tree_root_node)
	{
	    super(help_tree_root_node);

	    // Load the XML help index file and build the contents structure from it
	    try {
		String help_index_file_path = getHelpFolder() + "help_index.xml";
		Document document = XMLTools.parseXMLFile(help_index_file_path, true);

		// Traverse the help hierarchy, building a tree representing its structure
		Node document_node = document.getFirstChild();

		// Add a help contents tree node for each major section
		int section_number = 0;
		NodeList children = document_node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
		    Node child = children.item(i);
		    if (child.getNodeName().equals(StaticStrings.SECTION_ELEMENT)) {
			section_number++;
			buildHelpContentsTree(help_tree_root_node, section_number + StaticStrings.EMPTY_STR, child);
		    }
		}
	    }
	    catch (Exception exception) {
		DebugStream.printStackTrace(exception);
	    }
	}


	private void buildHelpContentsTree(MutableTreeNode parent, String pos, Node node)
	{
	    // Determine the section name
	    String section_name = ((Element) node).getAttribute(StaticStrings.NAME_ATTRIBUTE);

	    // Determine the section title
	    String section_title = "";
	    NodeList children = node.getChildNodes();
	    for (int i = 0; i < children.getLength(); i++) {
		Node child = children.item(i);
		if (child.getNodeName().equals(StaticStrings.TITLE_ELEMENT)) {
		    section_title = pos + ": ";
		    if (child.getFirstChild() != null) {
			section_title += child.getFirstChild().getNodeValue();
		    }
		}
	    }

	    // Add the node into the tree
	    HelpContentsTreeNode help_tree_node = new HelpContentsTreeNode(section_name, section_title);
	    insertNodeInto(help_tree_node, parent, parent.getChildCount());

	    // Apply recursively to the children of this node
	    int section_number = 0;
	    for (int i = 0; i < children.getLength(); i++) {
		Node child = children.item(i);
		if (child.getNodeName().equals(StaticStrings.SECTION_ELEMENT)) {
		    section_number++;
		    buildHelpContentsTree(help_tree_node, pos + StaticStrings.STOP_CHARACTER + section_number, child);
		}
	    }
	}


	private HelpContentsTreeNode getHelpContentsTreeNodeNamed(String section_name)
	{
	    // Find the node with name matching section name, somewhere in the tree
	    if (section_name != null) {
		Enumeration descendants = ((DefaultMutableTreeNode) root).preorderEnumeration();
		while (descendants.hasMoreElements()) {
		    HelpContentsTreeNode help_tree_node = (HelpContentsTreeNode) descendants.nextElement();
		    if (section_name.equals(help_tree_node.section_name)) {
			return help_tree_node;
		    }
		}
	    }

	    // Couldn't be found, so just return the root (Contents) node
	    return (HelpContentsTreeNode) root;
	}
    }


    /** This listener is used to listen for selection changes in the contents tree, and
     * to show the appropriate page as required.
     */
    private class HelpContentsTreeSelectionListener
	implements TreeSelectionListener
    {
	/** Any implementation of <i>TreeSelectionListener</i> must include this method so we can be informed when the tree selection changes.
	 * @param event A <strong>TreeSelectionEvent</strong> containing al the relevant information about the event itself.
	 */
	public void valueChanged(TreeSelectionEvent event)
	{
	    HelpContentsTreeNode help_tree_node = (HelpContentsTreeNode) event.getPath().getLastPathComponent();
	    selectHelpContentsTreeNode(help_tree_node);
	    showHelpPage(help_tree_node.section_name);
	}
    }


    /** This class provides a wrapper around a <strong>DefaultMutableTreeNode</strong> which
     * provides the ability to set an html page to be loaded when this node is selected.
     */
    private class HelpContentsTreeNode 
	extends DefaultMutableTreeNode
    {
	/** The unique name of the section (matches the HTML filename) */
	public String section_name = null;
	/** The title to be displayed for this tree node. */
	public String section_title = null;

	public HelpContentsTreeNode(String section_name, String section_title)
	{
	    this.section_name = section_name;
	    this.section_title = section_title;
	}

	public String toString()
	{
	    return section_title;
	}
    }


    private class HelpPaneHyperlinkListener
	implements HyperlinkListener
    {
	public void hyperlinkUpdate(HyperlinkEvent e)
	{
	    if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
		// Determine which node in the help contents tree to select, then select it
		String section_link = e.getURL().getFile();
		if (section_link.endsWith(".htm")) {
		    section_link = section_link.substring(section_link.lastIndexOf("/") + 1);
		    section_link = section_link.substring(0, section_link.length() - ".htm".length());
		    selectHelpContentsTreeNode(section_link);
		}

		// Change to the page specified by the link
		showHelpPage(e.getURL());
	    }
	}
    }
}
