/**
 *#########################################################################
 *
 * 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
	    CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + subcollection_index.getID());
	    metadatum.setValue(subcollection_index.getID());
	    CollectionDesignManager.collectionmeta_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.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + subcollection_index.getID());

	    // 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.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + old_subcollection.getID());
	// Add new one
	CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + new_subcollection.getID());
	metadatum.setValue(new_subcollection.getID());
	CollectionDesignManager.collectionmeta_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 set_default_button;
	private JList subcollection_index_list;
	private JTextField subcollection_index_name_textfield;


	/** Constructor.
	 */
	public SubcollectionIndexControls()
	{
	    super();

	    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();
	    JLabel index_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Subindexes"));
	    subcollection_index_list = new JList(model);
	    subcollection_index_list.setCellRenderer(new SubcollectionIndexListCellRenderer());
 	    subcollection_index_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	    subcollection_index_list.setVisibleRowCount(2);

	    JPanel movement_pane = new JPanel();
	    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();
	    JPanel details_pane = new JPanel();
	    JPanel labels_pane = new JPanel();
	    JPanel boxes_pane = new JPanel();
	    JPanel content_pane = new JPanel();

	    JLabel source_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Source"));
	    
	    source_list = new CheckList(false);
	    source_list.setListData(sources);
	    source_list.setToolTipText(Dictionary.get("CDM.SubcollectionIndexManager.Source_Tooltip"));
	    
	    JPanel button_pane = new JPanel();
	    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);
	    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.EAST);

	    labels_pane.setLayout(new BorderLayout());
	    labels_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));
	    labels_pane.add(source_label, BorderLayout.CENTER);
	    
	    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.WEST);
	    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);

	}

	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;
 		    }
 		}
	    }

	    // 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();
	    }
	}


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