/**
 *#########################################################################
 *
 * 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.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.gui.GLIButton;
import org.greenstone.gatherer.util.CheckList;
import org.greenstone.gatherer.util.JarTools;
import org.greenstone.gatherer.util.StaticStrings;
import org.w3c.dom.*;


/** This class maintains a list of indexes partitions for the purpose of defining subcollections.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.4
 */
public class SubcollectionIndexManager
extends DOMProxyListModel
{
	static final private Dimension FIELD_SIZE = new Dimension(200, 30);

	private Control controls;
	private DOMProxyListModel model;
	private SubcollectionIndex default_index;


	/** Constructor. */
	public SubcollectionIndexManager(Element subindexes)
	{
		super(subindexes, StaticStrings.INDEX_ELEMENT, new SubcollectionIndex());
		DebugStream.println("SubcollectionIndexManager: " + getSize() + " subcollection indexes parsed.");
		this.model = this;

		// Parse and retrieve the default index
		NodeList default_index_elements = CollectionConfiguration.getElementsByTagName(StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT);
		if (default_index_elements.getLength() > 0) {
			default_index = new SubcollectionIndex((Element) default_index_elements.item(0));
		}
	}


	/** Method to add a subindex.
	 * @param subindex a SubcollectionIndex
	 * @see org.greenstone.gatherer.Gatherer
	 * @see org.greenstone.gatherer.collection.CollectionManager
	 */
	private void addSubcollectionIndex(SubcollectionIndex subcollection_index)
	{
		if (!contains(subcollection_index)) {
			// add a pseudo metadata
		    SearchMeta metadatum = new SearchMeta(subcollection_index.getID(), SearchMeta.TYPE_PARTITION);
			metadatum.setValue(subcollection_index.getID());
			CollectionDesignManager.searchmeta_manager.addMetadatum(metadatum);
			add(getSize(), subcollection_index);
		}
	}


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


	public Control getControls()
	{
		if (controls == null) {
			controls = new SubcollectionIndexControls();
		}
		return controls;
	}


	/** Method to get all of the subindexes set.
	 * @return an ArrayList containing all the defined indexes
	 */
	public ArrayList getSubcollectionIndexes()
	{
		return children();
	}

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

	}

	private int moveSubcollectionIndex(SubcollectionIndex subcollection_index, boolean move_up)
	{
		// Determine the current position of the subcollection index
		int position = indexOf(subcollection_index);
		int new_position;

		// Attempt to move the subcollection index up
		if (move_up) {
			// Check it's not already at the top
			if (position == 0) {
				return position;
			}

			// This automatically removes the index first, as an Element can only exist once in a particular document
			new_position = position - 1;
			addBefore(subcollection_index, (SubcollectionIndex) getElementAt(new_position));
		}

		// Attempt to move the subcollection index down
		else {
			// Check it's not already at the bottom
			if (position == (getSize()) - 1) {
				return position;
			}

			// This automatically removes the index first, as an Element can only exist once in a particular document
			new_position = position + 1;
			addAfter(subcollection_index, (SubcollectionIndex) getElementAt(new_position));
		}

		return new_position;
	}


	/** Method to remove a certain subcollection index. */
	private void removeSubcollectionIndex(SubcollectionIndex subcollection_index)
	{
		if (subcollection_index != null) {
			// Remove any current metadata from this index
		    CollectionDesignManager.searchmeta_manager.removeMetadata(subcollection_index.getID(), SearchMeta.TYPE_PARTITION);

			// Check if the index removed happens to be the default index
			if (default_index != null && default_index.equals(subcollection_index)) {
				setDefault(null);
			}

			// Remove the index
			remove(subcollection_index);
		}
	}


	/** Method to remove all of the subindexes that contain a certain subcollection.
	 * @param subcollection the Subcollection that has been removed
	 * @see org.greenstone.gatherer.cdm.Subcollection
	 * @see org.greenstone.gatherer.cdm.SubcollectionIndex
	 */
	public void removeSubcollectionIndexes(Subcollection subcollection)
	{
		String subcollection_name = subcollection.getName();
		int size = getSize();
		for(int i = size - 1; i >= 0; i--) {
			SubcollectionIndex subcollection_index = (SubcollectionIndex) getElementAt(i);
			if (subcollection_index.getSources().contains(subcollection_name)) {
				removeSubcollectionIndex(subcollection_index);
			}
		}
	}

	private void replaceSubcollectionIndex(SubcollectionIndex old_subcollection, SubcollectionIndex new_subcollection) {
		// Remove old collection meta
	    CollectionDesignManager.searchmeta_manager.removeMetadata(old_subcollection.getID(), SearchMeta.TYPE_PARTITION);
		// Add new one
		SearchMeta metadatum = new SearchMeta(new_subcollection.getID());
		metadatum.setValue(new_subcollection.getID());
		CollectionDesignManager.searchmeta_manager.addMetadatum(metadatum);
		if(default_index != null && default_index.equals(old_subcollection)) {
			setDefault(new_subcollection);
		}

		// get the position of the old one
		int position = indexOf(old_subcollection);
		remove(old_subcollection);
		add(position, new_subcollection);
	}

	/** Method to set the default subcollection index.
	 * @param index The <strong>SubcollectionIndex</strong> to use as the default index.
	 * @see org.greenstone.gatherer.Gatherer
	 * @see org.greenstone.gatherer.collection.CollectionManager
	 * @see org.greenstone.gatherer.cdm.SubcollectionIndex
	 */
	private void setDefault(SubcollectionIndex subcollection_index)
	{
		if (subcollection_index != null) {
			if (default_index == null) {
				// Create the default index element, and place immediately after indexes element.
				Element default_index_element = root.getOwnerDocument().createElement(StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT);
				default_index = new SubcollectionIndex(default_index_element);
				Node target_node = CollectionConfiguration.findInsertionPoint(default_index_element);
				if (target_node != null) {
					root.getOwnerDocument().getDocumentElement().insertBefore(default_index_element, target_node);
				}
				else {
					root.getOwnerDocument().getDocumentElement().appendChild(default_index_element);
				}
			}
			default_index.setAssigned(true);
			default_index.setSources(subcollection_index.getSources());
		}
		else {
			if (default_index != null) {
				default_index.setAssigned(false);
			}
		}
	}


	/** This class creates a set of controls for editing the indexes. */
	private class SubcollectionIndexControls
	extends JPanel
	implements Control
	{
		private CheckList source_list;
		private JButton add_button;
		private JButton move_down_button;
		private JButton move_up_button;
		private JButton remove_button;
		private JButton replace_button;
		private JButton select_all_button;
		private JButton select_none_button; 
		private JButton set_default_button;
		private JList subcollection_index_list;
		private JTextField subcollection_index_name_textfield;


		/** Constructor.
		 */
		public SubcollectionIndexControls()
		{
			super();
                        this.setComponentOrientation(Dictionary.getOrientation());   
			ArrayList sources = new ArrayList();
			ListModel source_model = CollectionDesignManager.subcollection_manager;
			for (int i = 0; i < source_model.getSize(); i++) {
				sources.add(source_model.getElementAt(i));
			}

			// Creation
			JPanel assigned_indexes_pane = new JPanel();
                        assigned_indexes_pane.setComponentOrientation(Dictionary.getOrientation());
			JLabel index_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Subindexes"));
                        index_label.setComponentOrientation(Dictionary.getOrientation());
			subcollection_index_list = new JList(model);
			subcollection_index_list.setCellRenderer(new SubcollectionIndexListCellRenderer());
			subcollection_index_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			subcollection_index_list.setVisibleRowCount(2);
                        subcollection_index_list.setComponentOrientation(Dictionary.getOrientation());
                        
			JPanel movement_pane = new JPanel();
			movement_pane.setComponentOrientation(Dictionary.getOrientation());
                        move_up_button = new GLIButton(Dictionary.get("CDM.Move.Move_Up"), JarTools.getImage("arrow-up.gif"), Dictionary.get("CDM.Move.Move_Up_Tooltip"));
			move_up_button.setEnabled(false);

			move_down_button = new GLIButton(Dictionary.get("CDM.Move.Move_Down"), JarTools.getImage("arrow-down.gif"), Dictionary.get("CDM.Move.Move_Down_Tooltip"));
			move_down_button.setEnabled(false);

			set_default_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Set_Default_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Set_Default_Subindex_Tooltip"));
			set_default_button.setEnabled(false);

			JPanel index_pane = new JPanel();
                        index_pane.setComponentOrientation(Dictionary.getOrientation());
			JPanel details_pane = new JPanel();
			details_pane.setComponentOrientation(Dictionary.getOrientation());
			JPanel labels_pane = new JPanel();
			labels_pane.setComponentOrientation(Dictionary.getOrientation());
                        
			select_all_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_All"), Dictionary.get("CDM.IndexManager.Select_All_Tooltip"));
			select_none_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_None"), Dictionary.get("CDM.IndexManager.Select_None_Tooltip"));
			
			
			JPanel boxes_pane = new JPanel();
			boxes_pane.setComponentOrientation(Dictionary.getOrientation());
                        JPanel content_pane = new JPanel();
                        content_pane.setComponentOrientation(Dictionary.getOrientation());
                        
			JLabel source_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Source"));
                        source_label.setComponentOrientation(Dictionary.getOrientation());
                        
			source_list = new CheckList(false);
			source_list.setListData(sources);
			source_list.setToolTipText(Dictionary.get("CDM.SubcollectionIndexManager.Source_Tooltip"));
			
			select_all_button.setEnabled(isSelectAllEnabled());
			select_none_button.setEnabled(isSelectAllEnabled());

			JPanel button_pane = new JPanel();
			button_pane.setComponentOrientation(Dictionary.getOrientation());
                        
                        add_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Add_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Add_Subindex_Tooltip"));
			add_button.setEnabled(false);

			remove_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Remove_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Remove_Subindex_Tooltip"));
			remove_button.setEnabled(false);

			replace_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Replace_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Replace_Subindex_Tooltip"));
			replace_button.setEnabled(false);
						

			// Listeners
			add_button.addActionListener(new AddListener());
			add_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			move_down_button.addActionListener(new MoveListener(false));
			move_down_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			move_up_button.addActionListener(new MoveListener(true));
			move_up_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			remove_button.addActionListener(new RemoveListener());
			remove_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			replace_button.addActionListener(new ReplaceListener());
			replace_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			select_all_button.addActionListener(new SelectAllListener());			
			select_all_button.addActionListener(CollectionDesignManager.buildcol_change_listener);			
			select_none_button.addActionListener(new SelectNoneListener());
			select_none_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			set_default_button.addActionListener(new SetDefaultListener());
			set_default_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
			subcollection_index_list.addListSelectionListener(new AssignedListListener());
			source_list.addListSelectionListener(new SourceListListener());		
			
			
			// Layout
			movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
			movement_pane.setLayout(new GridLayout(3,1));
			movement_pane.add(move_up_button);
			movement_pane.add(move_down_button);
			movement_pane.add(set_default_button);

			assigned_indexes_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
			assigned_indexes_pane.setLayout(new BorderLayout());
			assigned_indexes_pane.add(index_label, BorderLayout.NORTH);
			assigned_indexes_pane.add(new JScrollPane(subcollection_index_list), BorderLayout.CENTER);
			assigned_indexes_pane.add(movement_pane, BorderLayout.LINE_END);

			labels_pane.setLayout(new BorderLayout());
			labels_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));
			
			// add the select all and select none buttons to the label panel			 
			GridBagLayout gridbag = new GridBagLayout();			
			GridBagConstraints c = new GridBagConstraints();
			labels_pane.setLayout(gridbag);
			
			c.fill = GridBagConstraints.BOTH;		
			c.weightx = 1.0;
			c.weighty = 1.0;
			c.gridx = 0;
			c.gridy = 0;
			
			gridbag.setConstraints(source_label, c);
			labels_pane.add(source_label);
			
			c.fill = GridBagConstraints.HORIZONTAL;
			c.weighty = 0.0;
			c.gridy = 1;
			
			gridbag.setConstraints(select_all_button, c);
			labels_pane.add(select_all_button);
			
			c.gridy = 2;
			gridbag.setConstraints(select_none_button, c);
			labels_pane.add(select_none_button);	
			
			
			boxes_pane.setLayout(new BorderLayout());
			boxes_pane.add(new JScrollPane(source_list), BorderLayout.CENTER);

			details_pane.setLayout(new BorderLayout());
			details_pane.add(labels_pane, BorderLayout.LINE_START);
			details_pane.add(boxes_pane, BorderLayout.CENTER);

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

			index_pane.setLayout(new BorderLayout());
			index_pane.add(details_pane, BorderLayout.CENTER);
			index_pane.add(button_pane, BorderLayout.SOUTH);

			content_pane.setLayout(new BorderLayout());
			content_pane.add(assigned_indexes_pane, BorderLayout.NORTH);
			content_pane.add(index_pane, BorderLayout.CENTER);

			setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
			setLayout(new BorderLayout());
			add(content_pane, BorderLayout.CENTER);
		}


		public void destroy()
		{
		}


		public void gainFocus()
		{
			// Reload the source list
			ArrayList sources = new ArrayList();
			ListModel source_model = CollectionDesignManager.subcollection_manager;
			for (int i = 0; i < source_model.getSize(); i++) {
				sources.add(source_model.getElementAt(i));
			}
			source_list.setListData(sources);

			// Refresh the subcollection index list
			subcollection_index_list.updateUI();

			clearControls();

		}


		public void loseFocus() {
		}

		private void clearControls() {
			subcollection_index_list.clearSelection();
			source_list.clearTicked();
			add_button.setEnabled(false);
			remove_button.setEnabled(false);
			replace_button.setEnabled(false);
			set_default_button.setEnabled(false);
			move_down_button.setEnabled(false);
			move_up_button.setEnabled(false);
			select_all_button.setEnabled(isSelectAllEnabled());			
			select_none_button.setEnabled(isSelectAllEnabled());
		}
		
		private boolean isSelectAllEnabled(){
		  	return source_list.getModel().getSize() > 0 ? true : false;		  	
		}

		private void updateControlsWithSelectedIndex()
		{
			SubcollectionIndex selected_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
			if (selected_index == null) {
				source_list.clearTicked();
				return;
			}

			// Display the selected subcollection index's sources
			source_list.clearTicked();
			source_list.setTickedObjects(selected_index.getSources().toArray());
		}


		private void validateButtons() {

			boolean add_enabled = false;
			boolean replace_enabled = false;

			// Can't add a new index if no sources are selected
			if (!source_list.isNothingTicked()) {
				ArrayList sources = source_list.getTicked();
				SubcollectionIndex subcollection_index = new SubcollectionIndex(sources.toArray());

				// Subcollection index already exists: can't add, but can replace if the name has changed
				if (!model.contains(subcollection_index)) {
					add_enabled = true;
					if (!subcollection_index_list.isSelectionEmpty()) {
						replace_enabled = true;
					}
				}
			}
			
			select_all_button.setEnabled(isSelectAllEnabled());
			select_none_button.setEnabled(isSelectAllEnabled());

			// We should now know the add_button state
			add_button.setEnabled(add_enabled);
			replace_button.setEnabled(replace_enabled);
		}


		private class AddListener
		implements ActionListener {
		  
			public void actionPerformed(ActionEvent event)
			{
				if (!source_list.isNothingTicked()) {
					ArrayList sources = source_list.getTicked();
					SubcollectionIndex subcollection_index = new SubcollectionIndex(sources.toArray());
					addSubcollectionIndex(subcollection_index);
					clearControls();
				}
			}
		}


		private class MoveListener
		implements ActionListener
		{
			private boolean move_up;

			public MoveListener(boolean move_up)
			{
				this.move_up = move_up;
			}

			public void actionPerformed(ActionEvent event)
			{
				// Retrieve the selected subcollection index
				SubcollectionIndex subcollection_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
				if (subcollection_index != null) {
					int new_position = moveSubcollectionIndex(subcollection_index, move_up);
					// Ensure the subcollection index that moved is still selected
					subcollection_index_list.setSelectedIndex(new_position);					
				}
			}
		}

		private class RemoveListener
		implements ActionListener
		{
			public void actionPerformed(ActionEvent event) {

				SubcollectionIndex delete_me = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
				if (delete_me != null) {
					removeSubcollectionIndex(delete_me);
				}
			}
		}


		private class ReplaceListener
		implements ActionListener {

			public void actionPerformed(ActionEvent event) {

				if (subcollection_index_list.isSelectionEmpty()|| source_list.isNothingTicked()) {
					// This should never happen, but just in case...
					replace_button.setEnabled(false);
					return;
				}
				SubcollectionIndex old_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
				ArrayList sources = source_list.getTicked();
				SubcollectionIndex new_index = new SubcollectionIndex(sources.toArray());
				replaceSubcollectionIndex(old_index, new_index);
			}
		}


		private class SetDefaultListener
		implements ActionListener {

			public void actionPerformed(ActionEvent event)
			{
				SubcollectionIndex subcollection_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
				if (subcollection_index != null) {
					setDefault(subcollection_index);
					// This should cause a repaint of just the desired row
					subcollection_index_list.setSelectedValue(subcollection_index, true);
				}
				set_default_button.setEnabled(false);
			}
		}


		private class SourceListListener
		implements ListSelectionListener {

			public void valueChanged(ListSelectionEvent event) {
				if (event.getValueIsAdjusting()) {
					return;
				}
				validateButtons();
			}
		}
		
		private class SelectAllListener
		implements ActionListener {
			
			public void actionPerformed(ActionEvent event){
				if(select_all_button.isEnabled()){
					source_list.setAllTicked();
					validateButtons();
				}
			}
		}
		
		private class SelectNoneListener
		implements ActionListener {
			
			public void actionPerformed(ActionEvent event){
				if(select_none_button.isEnabled()){
					source_list.clearTicked();
					validateButtons();
				}
			}
		}


		/** Listens for selections within the assigned subcollection indexes list  */
		private class AssignedListListener
		implements ListSelectionListener {

			public void valueChanged(ListSelectionEvent event) {

				if (event.getValueIsAdjusting()) {
					return;
				}

				if (subcollection_index_list.isSelectionEmpty()) {
					clearControls();
					return;
				}

				int i = subcollection_index_list.getSelectedIndex();
				int size = subcollection_index_list.getModel().getSize();
				
				select_all_button.setEnabled(isSelectAllEnabled());
				select_none_button.setEnabled(isSelectAllEnabled());

				// Enable the buttons appropriately
				remove_button.setEnabled(true);
				replace_button.setEnabled(false);
				add_button.setEnabled(false);
				SubcollectionIndex selected_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue();
				set_default_button.setEnabled(default_index == null || !default_index.equals(selected_index));
				if (i > 0) {
					move_up_button.setEnabled(true);					
				}
				else {
					move_up_button.setEnabled(false);
				}
				if (i < size-1){
					move_down_button.setEnabled(true);
				}
				else {
					move_down_button.setEnabled(false);
				}

				// Need to fill in the rest of the bits
				updateControlsWithSelectedIndex();
			}
		}


		private class SubcollectionIndexListCellRenderer
		extends DefaultListCellRenderer
		{
			/** Return a component that has been configured to display the specified value. */
			public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
				JLabel component = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
				if (default_index != null && default_index.equals(value)) {
					component.setText(component.getText() + " " + Dictionary.get("CDM.SubcollectionIndexManager.Default_Partition_Indicator"));
				}
				return component;
			}
		}
	}
}
