/**
 *#########################################################################
 *
 * 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.
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * 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 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.NonWhitespaceField;
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.w3c.dom.*;

/** This class maintains a list of subcollections within our collection.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.4
 */
public class SubcollectionManager
    extends DOMProxyListModel {

    static final private String DISABLED_CONTROLS = "Disabled";
    static final private String ENABLED_CONTROLS  = "Normal";
    static final private String CLASS_DICTIONARY_NAME = "CDM.SubcollectionManager.";

    /** The controls used to edit the settings of this manager. */
    private Control controls = null;

    private DOMProxyListModel model;

    /** Constructor. 
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.cdm.CollectionConfiguration
     * @see org.greenstone.gatherer.cdm.CollectionDesignManager
     * @see org.greenstone.gatherer.cdm.DOMProxyListModel
     * @see org.greenstone.gatherer.cdm.Subcollection
     */
    public SubcollectionManager() {
	super(CollectionDesignManager.collect_config.getDocumentElement(), StaticStrings.SUBCOLLECTION_ELEMENT, new Subcollection());
	DebugStream.println("SubcollectionManager: " + getSize() + " subcollections parsed.");
	this.model = this;
    }

    /** Method to add a new subcollection.
     * @param subcollection the Subcollection to add
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.cdm.CollectionConfiguration
     * @see org.greenstone.gatherer.cdm.DOMProxyListModel
     * @see org.greenstone.gatherer.collection.CollectionManager
     */
    private void addSubcollection(Subcollection subcollection) {
	if(!contains(subcollection)) {
	    Element element = subcollection.getElement();
	    // Locate where we should insert this new subcollection. 
	    Node target_node = CollectionConfiguration.findInsertionPoint(element);
	    // Failing that we insert immediately after a language string
	    add(root, subcollection, target_node);
	}
    }

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

    /** Method to retrieve the controls for this manager.
     * @return the Control used to edit the subcollection data
     */
    public Control getControls() {
	if(controls == null) {
	    controls = new SubcollectionControl();
	    //TestingPreparation.setNamesRecursively(this, "define_filters");
	}
	return controls;
    }

    /** Method to retrieve a certain subcollection by its name.
     * @param name a String which is used as the key for finding the matching subcollection
     * @return the requested Subcollection or null if no such subcollection exists.
     */
    private Subcollection getSubcollection(String name) {
	Subcollection result = null;
	int size = getSize();
	for(int i = 0; i < size; i++) {
	    Subcollection subcollection = (Subcollection) getElementAt(i);
	    if(subcollection.getName().equals(name)) {
		result = subcollection;
	    }
	}
	return result;
    }


    /** 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) {
     }    

    /** Method to remove the given subcollection.
     * @param subcollection the Subcollection you want to remove
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.collection.CollectionManager
     */
    private void removeSubcollection(Subcollection subcollection) {
	remove(subcollection);
    }

    private void updateSubcollection(Subcollection subcollection, String name, boolean include, String source, String pattern, String flags) {
	subcollection.setFlags(flags);
	subcollection.setInclusive(include);
	subcollection.setName(name);
	subcollection.setPattern(pattern);
	subcollection.setSource(source);
	refresh(subcollection);
    }

    /** This class creates a JPanel containing serveral more controls used for editing subcollection information. */
    private class SubcollectionControl
	extends JPanel 
	implements Control, ChangeListener
    {
	private JButton add_button;
	private JButton remove_button;
	private JButton update_button;
	private JComboBox source_combobox;
	private JList subcollection_list;
	private JTabbedPane tabbed_pane;
	private JTextField flags_field;
	private JTextField match_field;
	private JTextField name_field;
	private JRadioButton exclude_button;
	private JRadioButton include_button;

	/** Constructor */
	public SubcollectionControl() {
	    // Create
	    JPanel header_pane = new DesignPaneHeader("CDM.GUI.Subcollections", "partitionindexes");

	    tabbed_pane = new JTabbedPane();
	    tabbed_pane.addChangeListener(this);
            tabbed_pane.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel button_pane_3 = new JPanel();
	    button_pane_3.setComponentOrientation(Dictionary.getOrientation());
            add_button = new GLIButton(Dictionary.get("CDM.SubcollectionManager.Add"), Dictionary.get("CDM.SubcollectionManager.Add_Tooltip"));
	    add_button.setEnabled(false);
	    
	    remove_button = new GLIButton(Dictionary.get("CDM.SubcollectionManager.Remove"), Dictionary.get("CDM.SubcollectionManager.Remove_Tooltip"));
	    remove_button.setEnabled(false);
	    
	    update_button = new GLIButton(Dictionary.get("CDM.SubcollectionManager.Replace"), Dictionary.get("CDM.SubcollectionManager.Replace_Tooltip"));
	    update_button.setEnabled(false);
	    
	    JPanel button_pane = new JPanel();
            button_pane.setComponentOrientation(Dictionary.getOrientation());
	    JPanel button_pane_1 = new JPanel();
            button_pane_1.setComponentOrientation(Dictionary.getOrientation());
	    include_button = new JRadioButton(Dictionary.get("CDM.SubcollectionManager.Include"));
	    include_button.setComponentOrientation(Dictionary.getOrientation());
            include_button.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	    include_button.setOpaque(false);
	    
	    exclude_button = new JRadioButton(Dictionary.get("CDM.SubcollectionManager.Exclude"));
	    exclude_button.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	    exclude_button.setOpaque(false);
	    exclude_button.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel flags_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Flags"));
	    flags_label.setComponentOrientation(Dictionary.getOrientation());
            
	    flags_field = new NonWhitespaceField();
	    flags_field.setToolTipText(Dictionary.get("CDM.SubcollectionManager.Flags_Tooltip"));
            flags_field.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel inclusive_pane = new JPanel();
            inclusive_pane.setComponentOrientation(Dictionary.getOrientation());
	    JLabel inclusive_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Inclusive"));
	    inclusive_label.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel match_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Match"));
	    match_label.setComponentOrientation(Dictionary.getOrientation());
            
	    match_field = new JTextField();
	    match_field.setToolTipText(Dictionary.get("CDM.SubcollectionManager.Match_Tooltip"));
	    match_field.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel name_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Name"));
	    name_label.setComponentOrientation(Dictionary.getOrientation());
            
	    name_field = new NonWhitespaceField();
	    name_field.setToolTipText(Dictionary.get("CDM.SubcollectionManager.Name_Tooltip"));
	    
	    JLabel source_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Source"));
	    source_label.setComponentOrientation(Dictionary.getOrientation());
            
	    ArrayList every_metadata_set_element = MetadataSetManager.getEveryMetadataSetElement();
	    Vector source_model = new Vector(every_metadata_set_element);
	    source_model.add(0, StaticStrings.FILENAME_STR);
	    source_combobox = new JComboBox(source_model);
            source_combobox.setComponentOrientation(Dictionary.getOrientation());
	    source_combobox.setOpaque(false);
	    source_combobox.setToolTipText(Dictionary.get("CDM.SubcollectionManager.Source_Tooltip"));
	    
	    subcollection_list = new JList(model);
	    subcollection_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	    JPanel subcollection_pane = new JPanel();
            subcollection_pane.setComponentOrientation(Dictionary.getOrientation());
	    ButtonGroup bg = new ButtonGroup();
	    bg.add(include_button);
	    bg.add(exclude_button);
	    include_button.setSelected(true);
	    JPanel subcollection_list_pane = new JPanel();
            subcollection_list_pane.setComponentOrientation(Dictionary.getOrientation());
	    JLabel subcollection_list_label = new JLabel(Dictionary.get("CDM.SubcollectionManager.Assigned"));
	    subcollection_list_label.setComponentOrientation(Dictionary.getOrientation());

	    // Add listeners
	    SubCollectionChangeListener cl = new SubCollectionChangeListener();
	    add_button.addActionListener(new AddSubCollectionListener());
	    add_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
	    remove_button.addActionListener(new RemoveSubCollectionListener());
	    remove_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
	    update_button.addActionListener(new UpdateSubCollectionListener());
	    update_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
	    exclude_button.addActionListener(cl);
	    include_button.addActionListener(cl);
	    source_combobox.addActionListener(cl);
	    flags_field.getDocument().addDocumentListener(cl);
	    match_field.getDocument().addDocumentListener(cl);
	    name_field.getDocument().addDocumentListener(cl);
	    subcollection_list.addListSelectionListener(new SubCollectionListListener());

	    inclusive_pane.setLayout(new GridLayout());
	    inclusive_pane.add(include_button);
	    inclusive_pane.add(exclude_button);

	    button_pane_1.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
	    button_pane_1.setLayout(new GridLayout(5, 2));
	    button_pane_1.add(name_label);
	    button_pane_1.add(name_field);
	    button_pane_1.add(source_label);
	    button_pane_1.add(source_combobox);
	    button_pane_1.add(match_label);
	    button_pane_1.add(match_field);
	    button_pane_1.add(inclusive_label);
	    button_pane_1.add(inclusive_pane);
	    button_pane_1.add(flags_label);
	    button_pane_1.add(flags_field);

	    button_pane_3.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
	    button_pane_3.setLayout(new GridLayout(1,3));
	    button_pane_3.add(add_button);
	    button_pane_3.add(update_button);
	    button_pane_3.add(remove_button);

	    button_pane.setLayout(new BorderLayout());
	    button_pane.add(button_pane_1, BorderLayout.CENTER);
	    button_pane.add(button_pane_3, BorderLayout.SOUTH);

	    subcollection_list_pane.setLayout(new BorderLayout());
	    subcollection_list_pane.add(subcollection_list_label, BorderLayout.NORTH);
	    subcollection_list_pane.add(new JScrollPane(subcollection_list), BorderLayout.CENTER);
	    subcollection_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
	    subcollection_pane.setLayout(new BorderLayout());
	    subcollection_pane.add(subcollection_list_pane, BorderLayout.CENTER);
	    subcollection_pane.add(button_pane, BorderLayout.SOUTH);

	    tabbed_pane.addTab(Dictionary.get("CDM.SubcollectionManager.Subcollection_Controls"), subcollection_pane);
	    tabbed_pane.addTab(Dictionary.get("CDM.SubcollectionManager.Subindex_Controls"), (JPanel) CollectionDesignManager.subcollectionindex_manager.getControls());
	    tabbed_pane.addTab(Dictionary.get("CDM.SubcollectionManager.Language_Controls"), (JPanel) CollectionDesignManager.language_manager.getControls());

	    setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
	    setLayout(new BorderLayout());
	    add(header_pane, BorderLayout.NORTH);
	    add(tabbed_pane, BorderLayout.CENTER);
	    
	    // Setting custom names on select widgets to avoid naming collisions,
	    // as default names are based on type and both are checkboxes.
	    TestingPreparation.setIndividualSubcomponentNames(this, name_field, source_combobox,
							      match_field, flags_field);
	}
       	
	/** Method to unregister any listeners to avoid memory leaks.
	 */
	public void destroy() {
	}

	public void gainFocus() {
	    // Rebuild the sources combobox
	    ArrayList every_metadata_set_element = MetadataSetManager.getEveryMetadataSetElement();
	    Vector source_model = new Vector(every_metadata_set_element);
	    source_model.add(0, "Filename"); // Add filename as a possible source.
	    source_combobox.setModel(new DefaultComboBoxModel(source_model));
	}

	public void loseFocus() {
	}
	
	public void stateChanged(ChangeEvent event)
	{
	    if (tabbed_pane.getSelectedIndex() == 1) {
		CollectionDesignManager.subcollectionindex_manager.getControls().gainFocus();
	    }
	    if (tabbed_pane.getSelectedIndex() == 2) {
		CollectionDesignManager.language_manager.getControls().gainFocus();
	    }
	}

	/** Listens for actions apon the 'add' button in the SubcollectionManager controls, and if detected calls the addSubcollection method of the manager with a newly created subcollection. */
	private class AddSubCollectionListener
	    implements ActionListener {
	    /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we wish to retrieve information from the various edit controls, and if we have sufficient data to build a new subcollection do so.
	     * @param event An <strong>ActionEvent</strong> containing information about the event.
	     * @see org.greenstone.gatherer.cdm.Subcollection
	     */
	    public void actionPerformed(ActionEvent event) {
		String name = name_field.getText();
		String source = null;
		Object object = source_combobox.getSelectedItem();
		if (object instanceof MetadataElement) {
		    MetadataElement metadata_element = (MetadataElement) object;
		    source = metadata_element.getFullName();
		}
		else {
		    source = object.toString();
		}
		String pattern = match_field.getText();
		String flags = flags_field.getText();
		if(name.length() > 0 && (source == null || source.length() > 0) && pattern.length() > 0) {
		    Subcollection subcollection = new Subcollection(name, include_button.isSelected(), source, pattern, flags);
		    addSubcollection(subcollection);
		    // Change the selection to the new subcollection
		    subcollection_list.setSelectedValue(subcollection, true);
		}
		add_button.setEnabled(false);
	    }
	}

	/** This class listens for any key entry in a text field, selection change in a combobox or button click, and updates a subcollection as appropriate. Its also convenient to use this class to test if the add button should be active yet. */
	private class SubCollectionChangeListener
	    implements DocumentListener, ActionListener {
	    /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to record that somethings changed, then validate the controls.
	     * @param event An <strong>ActionEvent</strong> containing information about the event.
	     */
	    public void actionPerformed(ActionEvent event) {
		validateAdd();
	    }

	    public void changedUpdate(DocumentEvent event) {
		validateAdd();
	    }

	    public void insertUpdate(DocumentEvent event) {
		validateAdd();

	    }

	    public void removeUpdate(DocumentEvent event) {
		validateAdd();
	    }

	    /** Method to validate the current subcollection editor values, and enable or disable controls (add button) based on said values. */
	    private void validateAdd() {
		if(name_field.getText().length() > 0 && match_field.getText().length() > 0) {
		    if (getSubcollection(name_field.getText()) == null) {
			add_button.setEnabled(true);
		    } else {
			add_button.setEnabled(false);
		    }
		}
		else {
		    add_button.setEnabled(false);
		}
	    }
	}

	/** Listens for actions apon the 'remove' button in the SubcollectionManager controls, and if detected calls the remove method of the manager with the SubIndex selected for removal. */
	private class RemoveSubCollectionListener
	    implements ActionListener {
	    /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subcolleciton selected, and if so remove both it and any subindexes based on it.
	     * @param event An <strong>ActionEvent</strong> containing information about the event.
	     * @see org.greenstone.gatherer.cdm.Subcollection
	     */
	    public void actionPerformed(ActionEvent event) {
		if(!subcollection_list.isSelectionEmpty()) {
		    Subcollection subcollection = (Subcollection)subcollection_list.getSelectedValue();
		    removeSubcollection(subcollection);
		    // And remove subcollection indexes dependant on this subcollection
		    CollectionDesignManager.subcollectionindex_manager.removeSubcollectionIndexes(subcollection);
		}
		remove_button.setEnabled(false);
	    }
	}

	private class UpdateSubCollectionListener
	    implements ActionListener {
	    public void actionPerformed(ActionEvent event) {
		if(!subcollection_list.isSelectionEmpty()) {
		    Subcollection subcollection = (Subcollection)subcollection_list.getSelectedValue();
		    String name = name_field.getText();
		    String source = null;
		    Object object = source_combobox.getSelectedItem();
		    if (object instanceof MetadataElement) {
			MetadataElement metadata_element = (MetadataElement) object;
			source = metadata_element.getFullName();
		    }
		    else {
			source = object.toString();
		    }
		    String pattern = match_field.getText();
		    String flags = flags_field.getText();
		    if(name.length() > 0 && (source == null || source.length() > 0) && pattern.length() > 0) {
			updateSubcollection(subcollection, name, include_button.isSelected(), source, pattern, flags);
		    }
		}
	    }
	}

	/** This class listens for selections in the list on the subcollections pane of the SubcollectionManager, and updates the controls as necessary to reflect selection. */
	private class SubCollectionListListener
	    implements ListSelectionListener {
	    /** Any implementation of ListSelectionListener must include this method so we can be informed when the selection changes. In this case we want to execute any changes the users made to the entry, then update the controls with details of the new selection.
	     * @param event A <strong>ListSelectionEvent</strong> containing information related to this event.
	     * @see org.greenstone.gatherer.cdm.Subcollection
	     */
	    public void valueChanged(ListSelectionEvent event) {
		// Wait until the event stabilises to avoid processing it multiple times
		if (event.getValueIsAdjusting() == true) {
		    return;
		}
		// Now the entry
		if(!subcollection_list.isSelectionEmpty()) {
		    Subcollection subcollection = (Subcollection) subcollection_list.getSelectedValue();
		    flags_field.setText(subcollection.getFlags());
		    include_button.setSelected(subcollection.isInclusive());
		    exclude_button.setSelected(!subcollection.isInclusive());
		    match_field.setText(subcollection.getPattern());
		    name_field.setText(subcollection.getName());
		    String s = subcollection.getSource();
		    int pos = 0;
		    Object value = source_combobox.getItemAt(pos);
		    //ystem.err.println("Search for: " + s);
		    while (value != null) {
			if (value instanceof MetadataElement) {
			    MetadataElement metadata_element = (MetadataElement) value;
			    String metadata_element_name = metadata_element.getFullName();
			    //ystem.err.print("Compare to: " + e_name);
			    if (metadata_element_name.equals(s)) {
				source_combobox.setSelectedIndex(pos);
				value = null;
				//ystem.err.println(" - Match");
			    }
			    else {
				pos++;
				value = source_combobox.getItemAt(pos);
				//ystem.err.println(" - Fail");
			    }
			}
			else if(value.toString().equals(s)) {
			    source_combobox.setSelectedIndex(pos);
			    value = null;
			}
			else {
			    pos++;
			    value = source_combobox.getItemAt(pos);
			}
		    }
		    // Can't add one thats already there.
		    add_button.setEnabled(false);
		    // You can update or remove it though...
		    remove_button.setEnabled(true);
		    update_button.setEnabled(true);
		}
		else {
		    flags_field.setText("");
		    include_button.setSelected(true);
		    match_field.setText("");
		    name_field.setText("");
		    source_combobox.setSelectedIndex(0);
		    remove_button.setEnabled(false);
		    update_button.setEnabled(false);
		}
	    }
	}
    }
}
