/**
 *#########################################################################
 *
 * 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.
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * 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.
 *
 * 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.
 *
 * 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.cdm;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.undo.*;
import javax.xml.parsers.DocumentBuilderFactory;

import org.fife.ui.rsyntaxtextarea.*;

import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.gui.DesignPaneHeader;
import org.greenstone.gatherer.gui.GLIButton;
import org.greenstone.gatherer.gui.FormatPane;
import org.greenstone.gatherer.gui.NumberedJTextArea;
import org.greenstone.gatherer.gui.TestingPreparation;
import org.greenstone.gatherer.metadata.MetadataElement;
import org.greenstone.gatherer.metadata.MetadataSetManager;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;
import org.greenstone.gatherer.util.XMLTools;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.io.ByteArrayInputStream;

/**
 * This class maintains a list of format statements, and allows the addition and
 * removal of these statements. This is the greenstone 3 equivalent class of
 * FormatManager.java which is used by greenstone 2
 */
public class Format4gs3Manager implements SharedByTwoFormatManager
{
	// The default global format
	static final private String GLOBAL_FORMAT;
	static
	{		
		// @formatter:off
		String GLOBAL_FORMAT_TEMP = "" +
		"<gsf:template name=\"choose-title\">" +
			"<gsf:choose-metadata>" +
				"<gsf:metadata name=\"dc.Title\"/>" +
				"<gsf:metadata name=\"exp.Title\"/>" +
				"<gsf:metadata name=\"ex.dc.Title\"/>" +
				"<gsf:metadata name=\"Title\"/>" +
				"<gsf:default>Untitled</gsf:default>" +
			"</gsf:choose-metadata>" +
		"</gsf:template>";
		GLOBAL_FORMAT = GLOBAL_FORMAT_TEMP;
		// @formatter:on
	}
	static final private String GLOBAL = "global";
	//The default search format
	static final private String SEARCH_FORMAT;
	static
	{
		// @formatter:off
		String SEARCH_FORMAT_TEMP = "" +
		"<gsf:template match=\"documentNode\">" +
			"<td valign=\"top\">" +
				"<gsf:link type=\"document\">" +
					"<gsf:icon type=\"document\"/>" +
				"</gsf:link>" +
			"</td>" +
			"<td>" +
				"<gsf:link type=\"document\">" +
					"<xsl:call-template name=\"choose-title\"/>" +
				"</gsf:link>" +
			"</td>" +
		"</gsf:template>";
		SEARCH_FORMAT = SEARCH_FORMAT_TEMP;
		// @formatter:on
	}
	static final private String SEARCH = "search";
	static final private String DISPLAY_DEFAULT_FORMAT;
	static
	{
		// @formatter:off
		String DISPLAY_DEFAULT_FORMAT_TEMP = "" +
		"<gsf:option name=\"TOC\" value=\"true\"/>" +
		"<gsf:option name=\"AllowUserComments\" value=\"false\"/>" +
		"<!--" +
			"Overwriting this template allows you to change the heading of the document." +
		"-->" +
		"<!--" +
		"<gsf:template name=\"documentHeading\">" +
			"<span style=\"font-weight:bold; font-size: 120%;\">" +
				"<xsl:call-template name=\"choose-title\"/>" +
			"</span>" +
		"</gsf:template>" +
		"-->" +
		"<!--" +
			"Overwriting this template can be used to redefine the content of the whole document." + 
			"This is useful for simple documents, but not recommended for more complex documents" +
			"(e.g. hierachical and paged documents) as it can prevent any sub-sections from showing." +
		"-->" +
		"<!--" +
		"<gsf:template name=\"documentContent\">" +
		  "<div id=\"gs-document\">" +
		  "<xsl:call-template name=\"documentPre\"/>" +
		  "<xsl:call-template name=\"wrappedSectionImage\"/>" +
		  "<div id=\"gs-document-text\">" +
		  "<xsl:call-template name=\"documentNodeText\"/>" +
		  "</div>" +
		  "</div>" +
		"</gsf:template>" +
		"-->" +
		"<!--" +
			"Overwriting this template can be used to change the content of section headings." +
		"-->" +
		"<!--" +
		"<gsf:template name=\"sectionHeading\">" +
			"<xsl:call-template name=\"choose-title\"/>" +
		"</gsf:template>" +
		"-->" +
		"<!--" +
			"Overwriting this template can be used to change the content of the top-level section." +
		"-->" +
		"<!--" +
		"<gsf:template name=\"topLevelSectionContent\">" +
			"<xsl:call-template name=\"wrappedSectionImage\"/>" +
			"<xsl:call-template name=\"wrappedSectionText\"/>" +
		"</gsf:template>" +
		"-->" +
		"<!--" +
			"Overwriting this template can be used to change the content of sections." +
		"-->" +
		"<!--" +
		"<gsf:template name=\"sectionContent\">" +
			"<xsl:call-template name=\"wrappedSectionImage\"/>" +
			"<xsl:call-template name=\"wrappedSectionText\"/>" +
		"</gsf:template>" +
		"-->";
		// @formatter:on
		
		DISPLAY_DEFAULT_FORMAT = DISPLAY_DEFAULT_FORMAT_TEMP;
	}
	static final private String DISPLAY = "display";

	//The default browse format
	static final private String CLASSIFIER_DEFAULT_FORMAT;
	static
	{
		// @formatter:off
		String CLASSIFIER_DEFAULT_FORMAT_TEMP = "" +
		"<gsf:template match=\"documentNode\">" + 
			"<td valign=\"top\">" + 
				"<gsf:link type=\"document\">" +
					"<gsf:icon type=\"document\"/>" +
				"</gsf:link>" +
			"</td>" +
			"<td valign=\"top\">" +
				"<gsf:link type=\"source\">" +
					"<gsf:choose-metadata>" +
						"<gsf:metadata name=\"thumbicon\"/>" +
						"<gsf:metadata name=\"srcicon\"/>" +
					"</gsf:choose-metadata>" +
				"</gsf:link>" +
			"</td>" +
			"<td valign=\"top\">" +
				"<gsf:link type=\"document\">" +
					"<xsl:call-template name=\"choose-title\"/>" +
				"</gsf:link>" +
				"<gsf:switch>" +
					"<gsf:metadata name=\"Source\"/>" +
					"<gsf:when test=\"exists\"><br/><i>(<gsf:metadata name=\"Source\"/>)</i></gsf:when>" +
				"</gsf:switch>" +
			"</td>" +
		"</gsf:template>" +
		"<gsf:template match=\"classifierNode[@classifierStyle = 'VList']\">" +
			"<td valign=\"top\">" +
				"<gsf:link type=\"classifier\" style=\"static\">" +
					"<gsf:icon type=\"classifier\"/>" +
				"</gsf:link>" +
			"</td>" +
			"<td valign=\"top\">" +
		            "<gsf:link type=\"classifier\">" +
				"<gsf:metadata name=\"Title\"/>" +
                            "</gsf:link>" +
			"</td>" +
		"</gsf:template>" +
		"<gsf:template match=\"classifierNode[@classifierStyle = 'HList']\">" +
			"<gsf:link type=\"classifier\">" +
				"<gsf:metadata name=\"Title\"/>" +
			"</gsf:link>" +
		"</gsf:template>";
		CLASSIFIER_DEFAULT_FORMAT = CLASSIFIER_DEFAULT_FORMAT_TEMP;
		// @formatter:on
	}
	static final private String CLASSIFIER_DEFAULT = "browse";
	static final private String SEARCHTYPE_FORMAT = "plain,simpleform,advancedform";
	static final private String SEARCHTYPE = "searchType";
	static final private String[] FEATURE_NAME = { SEARCH, GLOBAL, DISPLAY, CLASSIFIER_DEFAULT, SEARCHTYPE };
	static final private String[] FEATURE_FORMAT = { SEARCH_FORMAT, GLOBAL_FORMAT, DISPLAY_DEFAULT_FORMAT, CLASSIFIER_DEFAULT_FORMAT, SEARCHTYPE_FORMAT };

	static private HashMap default_format_map = null;
	static private HashMap default_format_formated_map = null;

	/** The controls used to edit the format commands. */
	private Control controls = null;// an interface
	/** A reference */
	private DOMProxyListModel format_list_model = null;

	//private DOMProxyListModel feature_list_model = null;

	/** Constructor. */
	public Format4gs3Manager()
	{//pass the internal structure
		Element root = CollectionDesignManager.collect_config.getDocumentElement();
		format_list_model = new DOMProxyListModel(root, StaticStrings.FORMAT_STR, new Format4gs3());
		initDefault(format_list_model, FEATURE_NAME, FEATURE_FORMAT);
		initFormatMap(FEATURE_NAME, FEATURE_FORMAT);

	}

	private void initFormatMap(String[] feature_name, String[] feature_format)
	{
		default_format_map = new HashMap();
		default_format_formated_map = new HashMap();
		for (int i = 0; i < feature_name.length; i++)
		{
			default_format_map.put(feature_name[i], feature_format[i]);
			default_format_formated_map.put(feature_name[i], Format4gs3.toFormatedFormat(feature_format[i]));
		}

	}

	public void initDefault(DOMProxyListModel model, String[] feature_name, String[] feature_format)
	{
		// Establish all of the format objects.
		for (int i = 0; i < model.getSize(); i++)
		{
			model.getElementAt(i);//get those objects cached
		}
		for (int i = 0; i < feature_name.length; i++)
		{
			if (getFormat(model, feature_name[i]) == null)
			{
				model.add(new Format4gs3(feature_name[i], feature_format[i]));

			}
		}
	}

	/**
	 * Method to remove a format.
	 * 
	 * @param format
	 *            The <strong>Format</strong> to remove.
	 */
	private void removeFormat(DOMProxyListModel model, Format4gs3 format)
	{
		model.remove(format);
	}

	private Format4gs3 getFormat(DOMProxyListModel model, String name)
	{

		for (int index = 0; index < model.getSize(); index++)
		{
			Format4gs3 format = (Format4gs3) model.getElementAt(index);
			if (format.getFeatureName().equals(name))
			{
				return format;
			}
		}
		return null;
	}

	private void addFormat(Element parent, Format4gs3 format)
	{
		if (!format_list_model.contains(format))
		{

			format_list_model.add(parent, format, null);
		}
	}

	public void destroy()
	{
		if (controls != null)
		{
			controls.destroy();
			controls = null;
		}
	}

	/**
	 * Method to retrieve this managers controls.
	 * 
	 * @return the Control for this collection.
	 */
	public Control getControls()
	{
		if (controls == null)
		{
			controls = new FormatControl();
		}
		//controls.gainFocus();
		return controls;
	}

	/**
	 * Called when the detail mode has changed which in turn may cause several
	 * design elements to be available/hidden
	 * 
	 * @param mode
	 *            the new mode as an int
	 */
	public void modeChanged(int mode)
	{

	}

	/** updates the format and feature model */
	public synchronized void refresh()
	{
		for (int i = 0; i < format_list_model.getSize(); i++)
		{
			Format4gs3 format = (Format4gs3) format_list_model.getElementAt(i);
			format.update();
			format = null;
		}
		//call the gainFocus() and in turn the buildFeatureModel() method to get the feature combobox refreshed as well
		if (controls == null)
		{
			controls = new FormatControl();
		}
		controls.gainFocus();

		//format_list_model.refresh(); //this call is not necessary as it is included in gainFocus()
	}

	private ArrayList buildFeatureModel()
	{
		// Rebuild feature model.
		ArrayList feature_model = new ArrayList();
		//This will display 'choose a feature' and is used when the format feature panel is first gained focus
		feature_model.add(new Entry(""));

		for (int i = 0; i < format_list_model.getSize(); i++)
		{
			Format4gs3 format = (Format4gs3) format_list_model.getElementAt(i);
			String feature_name = format.getFeatureName();
			if (!feature_name.startsWith(Classifier.CLASSIFIER_PREFIX))
			{
				feature_model.add(new Entry(format.getFeatureName()));

			}
		}
		// Now the classifiers.
		Element root = CollectionDesignManager.collect_config.getDocumentElement();
		NodeList classifier_list = root.getElementsByTagName(StaticStrings.CLASSIFY_ELEMENT);
		for (int j = 0; j < classifier_list.getLength(); j++)
		{
			feature_model.add(new Entry(CollectionDesignManager.classifier_manager.getClassifier(j)));

		}
		//Collections.sort (feature_model);
		return feature_model;
	}

	private String addSurroundingTags(String xml)
	{
		return "<ROOTELEMENT>" + xml + "</ROOTELEMENT>";
	}

	private String removeSurroundingTags(String xml)
	{
	  return xml.replace("<ROOTELEMENT>\n", "").replace("<ROOTELEMENT>", "").replace("</ROOTELEMENT>", "").replace("<ROOTELEMENT/>","");
	}

	public class FormatControl extends JPanel implements Control
	{

		private ArrayList feature_model;
		private boolean ignore_event = false;
		private boolean ready = false; // Are these controls available to be refreshed
		private JButton add_button;
		private JButton remove_button;
		private JButton default_button;
		private JComboBox feature_combobox;
		private JList format_list;
		private NumberedJTextArea editor_textarea;
		private JTextArea editor_msgarea;
		private JPanel validation_msg_panel;
		private JPanel selection_pane;
		private final Dimension FIELD_SIZE = new Dimension(200, 30);
		private boolean newtext = true;
		private Format4gs3 previousFormat = null;
		private Format4gs3 currentFormat = null;
		private boolean fresh = true;

		public FormatControl()
		{
			feature_model = buildFeatureModel();

			// Create
			JPanel header_pane = new DesignPaneHeader("CDM.GUI.Formats", "formatstatements");

			format_list = new JList(format_list_model);

			selection_pane = new JPanel();
			JPanel feature_pane = new JPanel();
			JLabel feature_label = new JLabel(Dictionary.get("CDM.FormatManager.Feature"));

			feature_combobox = new JComboBox(feature_model.toArray());
			feature_combobox.setOpaque(!Utility.isMac());
			feature_combobox.setPreferredSize(FIELD_SIZE);
			feature_combobox.setEditable(false);
			feature_combobox.setToolTipText(Dictionary.get("CDM.FormatManager.Feature_Tooltip"));

			JPanel center_pane = new JPanel();
			JPanel editor_pane = new JPanel();

			// NumberedJTextArea comes with undo and redo buttons already hooked up to listeners
			editor_textarea = new NumberedJTextArea(Dictionary.get("CDM.FormatManager.Add_Tooltip"));

			default_button = new GLIButton(Dictionary.get("CDM.FormatManager.Default"), Dictionary.get("CDM.FormatManager.Default_Tooltip"));
			JPanel button_pane = new JPanel();
			add_button = new GLIButton(Dictionary.get("CDM.FormatManager.Add"), Dictionary.get("CDM.FormatManager.Add_Tooltip"));
			add_button.setEnabled(false);

			remove_button = new GLIButton(Dictionary.get("CDM.FormatManager.Remove"), Dictionary.get("CDM.FormatManager.Remove_Tooltip"));
			remove_button.setEnabled(false);

			// Connect
			add_button.addActionListener(new AddListener());
			remove_button.addActionListener(new RemoveListener());
			default_button.addActionListener(new DefaultListener());
			feature_combobox.addActionListener(new FeatureListener());
			editor_textarea.getDocument().addDocumentListener(new EditorListener());

			format_list.addListSelectionListener(new FormatListListener());

			// Layout
			JPanel format_list_pane = new JPanel();
			format_list_pane.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
			format_list_pane.setLayout(new BorderLayout());
			format_list_pane.add(new JScrollPane(format_list), BorderLayout.CENTER);

			feature_pane.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
			feature_pane.setLayout(new BorderLayout(5, 0));
			feature_pane.add(feature_label, BorderLayout.WEST);
			feature_pane.add(feature_combobox, BorderLayout.CENTER);

			JPanel rupanel = new JPanel();
			rupanel.setLayout(new GridLayout(1, 2));
			rupanel.add(editor_textarea.undoButton);
			rupanel.add(editor_textarea.redoButton);

			editor_pane.setLayout(new BorderLayout());
			editor_pane.add(new JScrollPane(editor_textarea), BorderLayout.CENTER);

			validation_msg_panel = new JPanel();
			JLabel validation_label = new JLabel(Dictionary.get("CDM.FormatManager.MessageBox"));
			editor_msgarea = new JTextArea();

			editor_msgarea.setCaretPosition(0);
			editor_msgarea.setLineWrap(true);
			editor_msgarea.setRows(3);
			editor_msgarea.setWrapStyleWord(false);
			editor_msgarea.setEditable(false);
			editor_msgarea.setToolTipText(Dictionary.get("CDM.FormatManager.MessageBox_Tooltip"));
			validation_msg_panel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
			validation_msg_panel.setLayout(new BorderLayout(5, 0));
			validation_msg_panel.add(validation_label, BorderLayout.WEST);
			validation_msg_panel.add(new JScrollPane(editor_msgarea), BorderLayout.CENTER);

			selection_pane.setLayout(new BorderLayout());
			selection_pane.add(validation_msg_panel, BorderLayout.NORTH);
			selection_pane.add(rupanel, BorderLayout.SOUTH);
			selection_pane.add(editor_pane, BorderLayout.CENTER);

			button_pane.setLayout(new GridLayout(1, 3));
			button_pane.add(add_button);
			button_pane.add(remove_button);
			button_pane.add(default_button);

			center_pane.setLayout(new BorderLayout());
			center_pane.add(feature_pane, BorderLayout.NORTH);
			center_pane.add(selection_pane, BorderLayout.CENTER);
			center_pane.add(button_pane, BorderLayout.SOUTH);

			JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
			splitPane.add(format_list_pane, JSplitPane.TOP);
			splitPane.add(center_pane, JSplitPane.BOTTOM);
			splitPane.setDividerLocation(150);

			setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
			setLayout(new BorderLayout());
			add(header_pane, BorderLayout.NORTH);
			add(splitPane, BorderLayout.CENTER);
			ready = true;

			TestingPreparation.setNamesRecursively(this);
		}

		public void destroy()
		{
		}

		/**
		 * Overriden to ensure that the instructions pane is scrolled to the
		 * top.
		 */
		public void gainFocus()
		{
			if (ready)
			{

				format_list_model.refresh();
				// Update the feature model, trying to maintain the same selected object
				Object selected_feature = feature_combobox.getSelectedItem();
				feature_combobox.setSelectedItem(selected_feature);
				feature_model = buildFeatureModel();
				feature_combobox.setModel(new DefaultComboBoxModel(feature_model.toArray()));
			}
		}

		public void loseFocus()
		{
			//validate the templates. If not wellformed, pop up an alert; otherwise, do nothing.
			String msg = XMLTools.parse(editor_textarea.getText());
			if (msg.startsWith(XMLTools.NOTWELLFORMED))
			{
				JOptionPane.showMessageDialog(null, msg, XMLTools.NOTWELLFORMED, JOptionPane.ERROR_MESSAGE);
			}
			format_list_model.refresh();
		}

		public Format4gs3 getCurrentFormat()
		{
			return (Format4gs3) format_list.getSelectedValue();

		}

		/**
		 * Listens for clicks on the add button, and if the relevant details are
		 * provided adds a new format. Note that formats are responsible for
		 * codecing the values into something that can be a) stored in a DOM and
		 * b) written to file
		 */
		private class AddListener implements ActionListener
		{

			public void actionPerformed(ActionEvent event)
			{

				ignore_event = true; // Prevent format_list excetera propagating events

				String format_str = editor_textarea.getText();
				Entry entry = (Entry) feature_combobox.getSelectedItem();
				String feature_name = entry.getClassifier().getPositionString();

				Format4gs3 format = new Format4gs3(feature_name, format_str);
				Element e = format.getClassifyElement();
				addFormat(e, format);
				existingFormat(format.getFeatureName().startsWith(Classifier.CLASSIFIER_PREFIX)); // set the appropriate enable/disable on the interface

				// Update list selection (make the new added format highlighted on the format list panel)
				format_list.setSelectedValue(format, true);

				format = null;

				ignore_event = false;
			}
		}

		private class EditorListener implements DocumentListener
		{

			public void changedUpdate(DocumentEvent e)
			{
				update();
			}

			public void insertUpdate(DocumentEvent e)
			{
				update();
				updateUndo("insert");

			}

			public void removeUpdate(DocumentEvent e)
			{
				update();
				updateUndo("remove");

			}

			private void updateUndo(String from)
			{

				if (!newtext)
				{
				    editor_textarea.undoButton.setEnabled(true);
				}

				if (editor_textarea.getText().length() != 0 && newtext)
				{
					newtext = false;
				}
			}

			public void update()
			{
				if (!format_list.isSelectionEmpty())
				{
					Format4gs3 format = (Format4gs3) format_list.getSelectedValue();
					String format_str = editor_textarea.getText();
					String msg = XMLTools.parse(format_str);
					editor_msgarea.setText(msg);

					if (msg.startsWith(XMLTools.WELLFORMED))
					{
						format.setPureFormat(format_str);
						format.update();
						format_list_model.refresh(format);
						editor_msgarea.setBackground(Color.white);
						FormatPane.setPreviewButton(true);
					}
					else
					{
						editor_msgarea.setBackground(Color.red);
						FormatPane.setPreviewButton(false);
					}
				}
				else
				{
					add_button.setEnabled(false);
				}
			}
		}

		private class FeatureListener implements ActionListener
		{
			public void actionPerformed(ActionEvent event)
			{
				editor_textarea.undoButton.setEnabled(false);
				editor_textarea.redoButton.setEnabled(false);
				default_button.setEnabled(true);
				newtext = true;

				if (ignore_event == true)
				{
				        editor_textarea.discardAllEdits();
					return;
				}

				ignore_event = true;
				// Add is only enabled if there isn't already a format for the choosen feature and part.
				//Create a dummy format and test if itsa already in the model
				Entry entry = (Entry) feature_combobox.getSelectedItem();
				String feature_name = entry.getFeatureName();

				Format4gs3 format = getFormat(format_list_model, feature_name);

				if (format != null)
				{
					///ystem.err.println("There is an existing format!");
					format_list.setSelectedValue(format, true);
					Element formatElem = null;
					try
					{
						InputSource is = new InputSource(new ByteArrayInputStream(addSurroundingTags(format.getPureFormat()).getBytes("utf-8")));
						formatElem = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is).getDocumentElement();
					}
					catch (Exception ex)
					{
						ex.printStackTrace();
					}

					if (formatElem != null)
					{
						StringBuffer sb = new StringBuffer();
						XMLTools.xmlNodeToString(sb, formatElem, true, "  ", 0);
						editor_textarea.setText(removeSurroundingTags(sb.toString()));
					}
					else
					{
						editor_textarea.setText(format.getPureFormat());
					}
					editor_textarea.setCaretPosition(0);

					existingFormat(feature_name.startsWith(Classifier.CLASSIFIER_PREFIX));
				}
				// Otherwise there is no existing format, then this feature must be a classifier (CL1, 2, ...)
				// we display the ClassifierDefault for this format
				else
				{
					//Fist reset the format list panel
					format_list.clearSelection();

					if (feature_name.equals(""))
					{
						editor_textarea.setText("");

					}
					else
					{
						//Only for debugging purposes
						if (entry.getClassifier() == null)
						{
							DebugStream.println("It should be a classifier or choose a feature. What is it? " + entry.toString());
						}

						editor_textarea.setText(Format4gs3.toFormatedFormat(CLASSIFIER_DEFAULT_FORMAT));
						editor_textarea.setCaretPosition(0);
						newFormat();
					}
				}
				ignore_event = false;
				editor_textarea.discardAllEdits();
			}
		}

		private class FormatListListener implements ListSelectionListener
		{
			public void valueChanged(ListSelectionEvent event)
			{
			        editor_textarea.undoButton.setEnabled(false);
				editor_textarea.redoButton.setEnabled(false);
				default_button.setEnabled(true);
				newtext = true;

				if (!ignore_event && !event.getValueIsAdjusting())
				{

					if (!format_list.isSelectionEmpty())
					{
						ignore_event = true;
						Format4gs3 format = (Format4gs3) format_list.getSelectedValue();
						String feature_name = format.getFeatureName();
						Entry entry = null;
						if (feature_name.startsWith(Classifier.CLASSIFIER_PREFIX))
						{
							entry = new Entry(format.getClassifier());
						}
						else
						{
							entry = new Entry(feature_name);
						}
						feature_combobox.setSelectedItem(entry);

						existingFormat(format.getFeatureName().startsWith(Classifier.CLASSIFIER_PREFIX));

						Element formatElem = null;
						try
						{
							InputSource is = new InputSource(new ByteArrayInputStream(addSurroundingTags(format.getPureFormat()).getBytes("utf-8")));
							formatElem = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is).getDocumentElement();
						}
						catch (Exception ex)
						{
							ex.printStackTrace();
						}

						if (formatElem != null)
						{
							StringBuffer sb = new StringBuffer();
							XMLTools.xmlNodeToString(sb, formatElem, true, "  ", 0);
							editor_textarea.setText(removeSurroundingTags(sb.toString()));
						}
						else
						{
							editor_textarea.setText(format.getPureFormat());
						}
						editor_textarea.setCaretPosition(0);

						ignore_event = false;
					}

				}
				editor_textarea.discardAllEdits();
			}

		}

		private class RemoveListener implements ActionListener
		{
			public void actionPerformed(ActionEvent event)
			{
				if (!format_list.isSelectionEmpty())
				{
					// Remove the current format
					Format4gs3 format = (Format4gs3) format_list.getSelectedValue();
					removeFormat(format_list_model, format);
					// Change buttons
					add_button.setEnabled(true);
					feature_combobox.setSelectedItem(new Entry(""));
					newFormat();
				}
			}
		}

		private class DefaultListener implements ActionListener
		{
			public void actionPerformed(ActionEvent event)
			{
				newtext = false;
				if (!ignore_event)
				{
					Entry entry = (Entry) feature_combobox.getSelectedItem();
					String feature_name = entry.getFeatureName();
					Format4gs3 format = getFormat(format_list_model, feature_name);

					if (format != null)
					{
						if (format.isClassifier() == true)
						{
							editor_textarea.setText((String) default_format_formated_map.get(CLASSIFIER_DEFAULT));
							editor_textarea.setCaretPosition(0);
							remove_button.setEnabled(true);
						}
						else
						{
							editor_textarea.setText((String) default_format_formated_map.get(format.getFeatureName()));
							editor_textarea.setCaretPosition(0);
							remove_button.setEnabled(false);
						}
					}
					else
					{
						editor_textarea.setText((String) default_format_formated_map.get(CLASSIFIER_DEFAULT));
						editor_textarea.setCaretPosition(0);
						remove_button.setEnabled(false);
						add_button.setEnabled(true);
					}
				}
			}
		}

		private void newFormat()
		{
			editor_textarea.setEditable(false);
			editor_textarea.setBackground(Color.lightGray);
			editor_textarea.setToolTipText(Dictionary.get("CDM.FormatManager.Editor_Disabled_Tooltip"));

			editor_textarea.undoButton.setEnabled(false);
			editor_textarea.redoButton.setEnabled(false);
			add_button.setEnabled(true);
			remove_button.setEnabled(false);
			default_button.setEnabled(false);
			FormatPane.setPreviewButton(true);
		}

		private void existingFormat(boolean enableRemoveButton)
		{
			editor_textarea.setEditable(true);
			editor_textarea.setBackground(Color.white);
			editor_textarea.setToolTipText(Dictionary.get("CDM.FormatManager.Editor_Tooltip"));
			add_button.setEnabled(false);
			remove_button.setEnabled(enableRemoveButton);
			default_button.setEnabled(true);
			FormatPane.setPreviewButton(true);
		}
	}

	/**
	 * This object provides a wrapping around an entry in Format4gs3, which is
	 * tranparent.
	 */
	// This class is used for display in the feature combobox
	private class Entry implements Comparable
	{
		private Classifier classifier = null;
		private String feature_name = null;

		public Entry(Object object)
		{
			if (object instanceof Classifier)
			{
				classifier = (Classifier) object;
				feature_name = classifier.getPositionString();
			}
			else if (object instanceof String)
			{
				feature_name = (String) object;
			}
			else
			{
				feature_name = "";
			}
		}

		public Entry(String text)
		{
			this.feature_name = text;
		}

		public int compareTo(Object object)
		{
			if (object == null)
			{
				return 1;
			}
			if (toString() == null)
			{
				return -1;
			}
			else
			{
				String object_str = object.toString();
				if (object_str == null)
				{
					return 1;
				}
				return toString().compareTo(object_str);
			}
		}

		public boolean equals(Object object)
		{
			if (compareTo(object) == 0)
			{
				return true;
			}
			return false;
		}

		public Classifier getClassifier()
		{
			return classifier;
		}

		public String toString()
		{
			if (classifier != null)
			{
				// Return the classifier name - with its CL index shown, and all its metadata options as well
				return classifier.getPositionString() + StaticStrings.SPACE_CHARACTER + classifier.toString();
			}
			if (feature_name.equals(""))
			{
				return "<html><body><i>" + "Choose a feature" + "</i></body></html>";
			}
			return feature_name;
		}

		public String getFeatureName()
		{
			return feature_name;
		}
	}
}
