/**
 *#########################################################################
 *
 * 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.border.BevelBorder;
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.GComboBox;
import org.greenstone.gatherer.gui.GLIButton;
import org.greenstone.gatherer.gui.ModalDialog;
import org.greenstone.gatherer.gui.SimpleMenuBar;
import org.greenstone.gatherer.metadata.MetadataElement;
import org.greenstone.gatherer.metadata.MetadataSetManager;
import org.greenstone.gatherer.util.CheckList;
import org.greenstone.gatherer.util.JarTools;
import org.greenstone.gatherer.util.XMLTools;
import org.greenstone.gatherer.util.StaticStrings;
import org.w3c.dom.*;
/** This is a base class, inherited by indexes, sortfields, facets, which all pretty much do the same thing just with different element names. It is responsible for storing the indexes/sorts/facets which have been assigned to this collection and the default index (if there is one), and providing methods for interacting with both these data pools. It also knows how to turn itself into a String as it would be displayed in the collection configuration file.
 */
public abstract class BaseIndexManager
    extends DOMProxyListModel 
    implements BuildTypeManager.BuildTypeListener {

    /** A reference to ourselves so our inner methods have access. */
    protected DOMProxyListModel index_model = null;
    /** The default index. */
    protected Index default_index = null;

    protected Control controls = null;
    protected String build_type = null;
    
    // XML element names
    protected String index_element_name = null;
    protected String index_default_element_name = null;

    // Disctionary key strings for the interface - set these in subclass
    protected String controls_title_key = null;
    protected String new_button_key = "CDM.IndexManager.New";
    protected String new_button_tooltip_key = null;
    protected String edit_button_key = "CDM.IndexManager.Edit";
    protected String edit_button_tooltip_key = null;
    protected String remove_button_key = "CDM.IndexManager.Remove";
    protected String remove_button_tooltip_key = null;
    protected String default_indicator_key = null;
    protected String set_default_tooltip_key = "CDM.IndexManager.Set_Default_Tooltip";


    protected String nip_new_index_key = null;
    protected String nip_edit_index_key = null;
    protected String nip_source_label_key = null;
    protected String nip_source_tooltip_key = null;
    protected String nip_custom_label_key = null;
    protected String nip_custom_tooltip_key = null;
    protected String nip_add_index_button_key = null;
    protected String nip_add_index_tooltip_key = null; 
    protected String nip_replace_index_button_key = null;
    protected String nip_replace_index_tooltip_key = null; 
    protected String nip_add_all_index_button_key = null; 
    protected String nip_add_all_index_tooltip_key = null;

    static final protected Dimension PROMPT_SIZE = new Dimension(500,500);

    public BaseIndexManager(Element indexes, String current_build_type, String index_element_name, String index_default_element_name, Index class_type) {

	super(indexes, index_element_name, class_type);
	DebugStream.println(this.getClass().getSimpleName()+": " + getSize() + " items parsed.");
	this.index_element_name = index_element_name;
	this.index_default_element_name = index_default_element_name;

	index_model = this;
      
	// Parse and retrieve the default index - if we should have one
	if (this.index_default_element_name != null) {
	    NodeList default_index_elements = CollectionConfiguration.getElementsByTagName(this.index_default_element_name);
	    if(default_index_elements.getLength() > 0) {
		default_index = createIndex((Element)default_index_elements.item(0));
	    }
	}
	build_type = current_build_type;
    }

    protected Index createIndex(Element elem) {
	return (Index)getClassType().create(elem);
    }

    protected Index createIndex(ArrayList sources) {
	return (Index)((Index)getClassType()).create(sources); 
    }

    /** Method to add a new index.
     * @param index The <strong>Index</strong> to add.
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.collection.CollectionManager
     */
    protected void addIndex(Index index, SearchMeta metadatum) {
	///ystem.err.println("Adding an index: " + index.toString());
	if(!contains(index)) {
	    CollectionDesignManager.searchmeta_manager.addMetadatum(metadatum);
	    // Retrieve the currently last index
	    if(getSize() > 0) {
		Index last_index = (Index)getElementAt(getSize() - 1);
		addAfter(index, last_index);
		
	    }
	    else {
		add(index);
		// Also set this index as the default one, 
		setDefault(index);
	    }
	}
	else {
	    JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.IndexManager.Index_Exists"), Dictionary.get("General.Warning"), JOptionPane.WARNING_MESSAGE);
	}
    }

    /** change based on indexing type */
    public void buildTypeChanged(String new_build_type) {

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

    /** Method to retrieve a certain index, as referenced by an index number.
     * @param index An <i>int</i> which indicates the position of the desired index.
     * @return The <strong>Index</strong> at the given index, or <i>null</i> if no such index exists.
     */
    public Index getIndex(int index) {
	if(0 <= index && index < getSize()) {
	    return (Index)getElementAt(index);
	}
	return null;
    }

    /** Method to retrieve a certain index, given its id.
     * @param id the id of the index as a String
     * @return the Index that matches id, or null if no such index exists
     */
    public Index getIndex(String id) {
	int size = getSize();
	for(int i = 0; i < size; i++) {
	    Index index = (Index) getElementAt(i);
	    if(index.getID().equals(id)) {
		return index;
	    }
	}
	return null;
    }

    public ArrayList getIndexes() {
	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) {

    }

    protected void moveIndex(Index index, boolean move_up)
    {
	// Determine the current position of the index
	int position = indexOf(index);
	// Determine if it can be moved, ie if its not already at the top trying to move up, or at the bottom trying to move down.
	if(position == -1) {
	    return;
	}
	if(position == 0 && move_up) {
	    return;
	}
	if(position == (getSize()) - 1 && !move_up) {
	    return;
	}
	
	// Ok, move up
	if (move_up) {
	    position--;
	    remove(index);
	    add(position, index);
	}

	// Or move down
	else {
	    position++;
	    remove(index);
	    add(position, index);
	}
    }



    /** Method to remove a certain index.
     * @param index the Index to remove.
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.cdm.CollectionDesignManager
     * @see org.greenstone.gatherer.cdm.CollectionMetaManager
     * @see org.greenstone.gatherer.collection.CollectionManager
     */
    protected void removeIndex(Index index) {
	if(index != null) {
	    // Remove any current metadata for this index
	    CollectionDesignManager.searchmeta_manager.removeMetadata(index.getID(), index.getType());
	    // Remove the index
	    remove(index);
	    // Check if the index removed happens to be the default index
	    if(default_index != null && default_index.equals(index)) {
		// If so our first solution is to set the first index to be default
		if(getSize() > 0) {
		    Index another_index = (Index) getElementAt(0);
		    setDefault(another_index);
		    another_index = null;
		}
		else {
		    default_index.setAssigned(false);
		}
	    }
	}
    }


    /* replace an index in the list. new index may have the same sources but a
       different name, or may be a new index altogether */
    protected void replaceIndex(Index old_index, Index new_index,
			      SearchMeta coll_meta) {
	if (old_index == null || new_index == null || coll_meta == null) {
	    return;
	}
	if (!old_index.getID().equals(new_index.getID()) && contains(new_index)) {
	    // shoudl we output an error??
	    return;
	}
	// Remove the old index coll meta
	CollectionDesignManager.searchmeta_manager.removeMetadata( old_index.getID(), old_index.getType());
	// Add the new coll meta
	CollectionDesignManager.searchmeta_manager.addMetadatum(coll_meta);

	// get the position of the old one
	int position = indexOf(old_index);
	// remove it
	remove(old_index);
	// add the new one at that position
	add(position, new_index);
    }


    /** Method to set the default index.
     * @param index the new default Index
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.collection.CollectionManager
     */
    public void setDefault(Index index) {
	if (this.index_default_element_name == null) {
	    return; // no default needed
	}
	if(index != null) {
	    if(default_index == null ) {
		// Create the default index element, and place immediately after indexes element.
		Element default_index_element = root.getOwnerDocument().createElement(this.index_default_element_name);
		default_index = createIndex(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(index.getSources());
	    
	}
	else {
	    if(default_index != null) {
		default_index.setAssigned(false);
	    }
	}
    }

  

    protected class IndexControl
	extends JPanel
	implements Control {

	protected JList index_list;
	protected JButton move_down_button;
	protected JButton move_up_button;
	protected JButton set_default_button;

	protected JButton new_button;
	protected JButton edit_button;
	protected JButton remove_button;

	protected boolean has_default = false;
	public IndexControl() {
	    super();
            this.setComponentOrientation(Dictionary.getOrientation());
	    if (index_default_element_name != null) {
		has_default = true;
	    }
	    // Creation
	    JPanel assigned_indexes_pane = new JPanel();
	    assigned_indexes_pane.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel index_label = new JLabel(Dictionary.get(controls_title_key));
            index_label.setComponentOrientation(Dictionary.getOrientation());
            
	    index_list = new JList(index_model);
	    index_list.setCellRenderer(new IndexListRenderer());
	    index_list.setVisibleRowCount(6);
	    index_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            index_list.addMouseListener(new ClickListener());
            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.IndexManager.Set_Default"), Dictionary.get(set_default_tooltip_key));
	    set_default_button.setEnabled(false);
	    
	    JPanel button_pane = new JPanel();
	    button_pane.setComponentOrientation(Dictionary.getOrientation());
            
            new_button = new GLIButton(Dictionary.get(new_button_key)+StaticStrings.FURTHER_DIALOG_INDICATOR, Dictionary.get(new_button_tooltip_key));
	    new_button.setEnabled(true);
	    
	    edit_button = new GLIButton(Dictionary.get(edit_button_key)+StaticStrings.FURTHER_DIALOG_INDICATOR, Dictionary.get(edit_button_tooltip_key));
	    edit_button.setEnabled(false);
	    
	    remove_button = new GLIButton(Dictionary.get(remove_button_key), Dictionary.get(remove_button_tooltip_key));
	    remove_button.setEnabled(false);
	    	  
	    // Listeners
	    new_button.addActionListener(new NewIndexListener());
	    edit_button.addActionListener(new EditIndexListener());
	    remove_button.addActionListener(new RemoveIndexListener());
	    remove_button.addActionListener(CollectionDesignManager.buildcol_change_listener);

	    index_list.addListSelectionListener(new IndexListListener());

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

	   
	    // 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);
	    if (has_default) {
		set_default_button.addActionListener(new SetDefaultListener());
		movement_pane.add(set_default_button);
	    }
	    assigned_indexes_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
	    assigned_indexes_pane.setLayout(new BorderLayout());
	    assigned_indexes_pane.add(index_label, BorderLayout.NORTH);
	    assigned_indexes_pane.add(new JScrollPane(index_list), BorderLayout.CENTER);
	    assigned_indexes_pane.add(movement_pane, BorderLayout.LINE_END);

	    button_pane.setLayout(new GridLayout(1,3,5,0));
	    button_pane.add(new_button);
	    button_pane.add(edit_button);
	    button_pane.add(remove_button);

	    //setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
            setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
	    setLayout(new BorderLayout());
	    add(assigned_indexes_pane, BorderLayout.CENTER);
	    add(button_pane, BorderLayout.SOUTH);
	    
	}
	public void loseFocus() {}
	public void gainFocus() {}
	public void destroy() {}

      protected NewIndexPrompt createNewIndexPrompt(String build_type, Index index) {
        return new NewIndexPrompt(build_type, index);
        
      }
	/** Listens for double clicks apon the list and react as if the configure button was pushed. */
	protected class ClickListener
	    extends MouseAdapter {
	    /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
	     * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
	    */
    public void mouseClicked(MouseEvent event) {
      if(event.getClickCount() == 2 ) {
        if(!index_list.isSelectionEmpty()) {
          Index index = (Index) index_list.getSelectedValue();
          NewIndexPrompt nip = createNewIndexPrompt(build_type, index);
          nip.destroy();          
        }
      }
    }
  }

  
	protected class IndexListListener
	    implements ListSelectionListener {
	    
	    /** This method is called whenever the source list selection changes. When it does we need to fill in the various parts of the list description panel
	     * @param event A <strong>ListSelectionEvent</strong> containing further information about the list selection.
	     */
	    public void valueChanged(ListSelectionEvent event)
	    {
		if (event.getValueIsAdjusting()) {
		    return;
		}
		
                set_default_button.setEnabled(true);
		Object value = index_list.getSelectedValue();
		if (value == null) {
		    move_down_button.setEnabled(false);
		    move_up_button.setEnabled(false);
		    remove_button.setEnabled(false);
		    edit_button.setEnabled(false);
		    set_default_button.setEnabled(false);
		    return;
		}

		// Enable the buttons appropriately
		remove_button.setEnabled(true);
		edit_button.setEnabled(true);
		set_default_button.setEnabled(default_index == null || !default_index.equals(value));
		int i = index_list.getSelectedIndex();
		int size = index_list.getModel().getSize();
		move_up_button.setEnabled((i>0));
		move_down_button.setEnabled((i<size-1));
	    }
	}

	protected class IndexListRenderer
	    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(default_indicator_key));
		}
		return component;
	    }

	}

	protected class NewIndexListener
	    implements ActionListener {

	    public void actionPerformed(ActionEvent event) {
		NewIndexPrompt nip = createNewIndexPrompt(build_type, null);
		nip.destroy();
	    }
	}
	
	protected class EditIndexListener
	    implements ActionListener {

	    public void actionPerformed(ActionEvent event) {
		Index index = (Index) index_list.getSelectedValue();
		NewIndexPrompt nip = createNewIndexPrompt(build_type, index);
		nip.destroy();
	    }
	}

	protected class RemoveIndexListener
	    implements ActionListener {
	    
	    public void actionPerformed(ActionEvent event) {
		
		int i = index_list.getSelectedIndex();
		if (i != -1) {
		    removeIndex((Index) index_list.getSelectedValue());
		}
		int size = index_list.getModel().getSize();
		if (i == size) {
		    i--;
		}
		index_list.setSelectedIndex(i);
		// This will produce an event on the list, updating the other buttons
	    }
	}

	protected class MoveListener
	    implements ActionListener {

	    protected boolean move_up;

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

	    public void actionPerformed(ActionEvent event) {
		// Retrieve the selected index
		Index index = (Index) index_list.getSelectedValue();
		if(index != null) {
		    moveIndex(index, move_up);
		    // Ensure the index that moved is still selected
		    index_list.setSelectedValue(index, true);
		    index = null;
		}
	    }
	}

	protected class SetDefaultListener
	    implements ActionListener {

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

	

	protected class NewIndexPrompt 
	    extends ModalDialog {
	    
	    NewIndexPrompt new_index_prompt = null;

	    protected CheckList source_list;
	    protected JTextField custom_field;
	    protected JButton add_or_replace_button;
	    protected JButton add_all_button;
	    protected JButton cancel_button;

	    protected JPanel content_pane;
	    protected JPanel details_pane;
	    protected JPanel extra_pane;
	    protected JPanel button_pane;
	    protected boolean editing = false;
	
		
	    public NewIndexPrompt(String build_type, Index existing_index) {
		super(Gatherer.g_man, true);
                this.setComponentOrientation(Dictionary.getOrientation());
                
		new_index_prompt = this;

		setModal(true);
		setSize(PROMPT_SIZE);
		if (existing_index != null) {
		    setTitle (Dictionary.get(nip_edit_index_key));
		    editing = true;
		} else {
		    setTitle(Dictionary.get(nip_new_index_key));
		}
		// simple help menu, set to searchindexes section
		setJMenuBar(new SimpleMenuBar("searchindexes"));

		content_pane = (JPanel)this.getContentPane();
                content_pane.setComponentOrientation(Dictionary.getOrientation());
                generateContents(build_type, existing_index);
	    
		// Display on screen.
		Dimension screen_size = Configuration.screen_size;
		setLocation((screen_size.width - PROMPT_SIZE.width) / 2, (screen_size.height - PROMPT_SIZE.height) / 2);
		screen_size = null;
		setVisible(true);
	    
	    }

          
	    /** Method which actually forces the dialog to be shown on screen.
	     * @return <i>true</i> if the user completed configuration and pressed ok, <i>false</i> otherwise.
	     */
	    public boolean display() {
		setVisible(true);
		return true;
		//    return success;
	    }
	
	    public void destroy() {
	    }

          protected void generateContents(String build_type, Index existing_index) {
		ArrayList new_data = new ArrayList();
		new_data.addAll(MetadataSetManager.getEveryMetadataSetElement());

		details_pane = new JPanel();
                details_pane.setComponentOrientation(Dictionary.getOrientation());
		
		JLabel source_label = new JLabel(Dictionary.get(nip_source_label_key));
                source_label.setComponentOrientation(Dictionary.getOrientation());
		source_list = new CheckList(false);                
		source_list.setListData(new_data);
		source_list.setToolTipText(Dictionary.get(nip_source_tooltip_key));
		source_list.addListSelectionListener(new SourceListListener());

		extra_pane = new JPanel();
		extra_pane.setComponentOrientation(Dictionary.getOrientation());

		JPanel custom_field_pane = new JPanel();
		//custom_field_pane.setPreferredSize(ROW_SIZE);
		

		JLabel custom_label = new JLabel(Dictionary.get(nip_custom_label_key));
		custom_label.setComponentOrientation(Dictionary.getOrientation());
		custom_field = new JTextField();
		custom_field.setComponentOrientation(Dictionary.getOrientation());
		custom_field.setCaretPosition(0);
		custom_field.setToolTipText(Dictionary.get(nip_custom_tooltip_key));

		button_pane = new JPanel();
		button_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		button_pane.setComponentOrientation(Dictionary.getOrientation());
                
		if (editing) {
		    button_pane.setLayout(new GridLayout(0,2,5,0));
		} else {
		     button_pane.setLayout(new GridLayout(0,3,5,0));
		}
		if (existing_index != null) {
		    // replace
		    add_or_replace_button = new GLIButton(Dictionary.get(nip_replace_index_button_key), Dictionary.get(nip_replace_index_tooltip_key));
		    add_or_replace_button.addActionListener(new ReplaceIndexListener());
		} else {
		    //new
		    add_or_replace_button = new GLIButton(Dictionary.get(nip_add_index_button_key), Dictionary.get(nip_add_index_tooltip_key));
		    add_or_replace_button.addActionListener(new AddIndexListener());
		    // create the add all button, which we only have for "add"
		    add_all_button = new GLIButton(Dictionary.get(nip_add_all_index_button_key), Dictionary.get(nip_add_all_index_tooltip_key));
		    add_all_button.addActionListener(new AddAllActionListener());

		}

		add_or_replace_button.addActionListener(CollectionDesignManager.buildcol_change_listener);
		add_or_replace_button.setEnabled(false);
		button_pane.add(add_or_replace_button);
		if (!editing) {
		    button_pane.add(add_all_button);
		}
		// always have a cancel button
		cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel"));
		cancel_button.setEnabled(true);
		cancel_button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
			    new_index_prompt.dispose();
			}
		    });
		button_pane.add(cancel_button);

		//Layout
		
		custom_field_pane.setLayout(new BorderLayout());
		custom_field_pane.add(custom_label,BorderLayout.LINE_END);
		custom_field_pane.add(custom_field, BorderLayout.CENTER);
		
		extra_pane.setLayout(new GridLayout(0,1,5,5));
		extra_pane.add(custom_field_pane);

		details_pane.setLayout(new BorderLayout(10,10));
		details_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		details_pane.add(new JScrollPane(source_list), BorderLayout.CENTER);
		details_pane.add(extra_pane, BorderLayout.SOUTH);

		// if we are editing, fill in the controls
		if (existing_index !=null) {
		    ArrayList sources = existing_index.getSources();
		    source_list.setTickedObjects(sources.toArray());
		    source_list.setEnabled(true);
		    custom_field.setText(getCustomItemsFromSources(sources)); 
		}
		custom_field.getDocument().addDocumentListener(new CustomFieldListener());
		content_pane.setLayout(new BorderLayout());
		content_pane.add(source_label, BorderLayout.NORTH);
		content_pane.add(details_pane, BorderLayout.CENTER);
		content_pane.add(button_pane, BorderLayout.SOUTH);

            
          }

	    protected Index generateNewIndex() {
		Index index = null;
		ArrayList sources;
		String custom_text = custom_field.getText();
		if (!source_list.isNothingTicked() || !custom_text.equals("")) {
		    sources = source_list.getTicked();
		    addCustomItemsToSources(sources, custom_text);
		
		    index = createIndex(sources);
		}
		return index;
	    }

	    protected void addCustomItemsToSources(ArrayList sources, String custom_text) {
		if (!custom_text.equals("")) {
		    String[] custom_items = custom_text.split(",");
		    for (int i = 0; i < custom_items.length; i++) {
			sources.add(custom_items[i]);
		    }
		    
		}
	    }

	    protected String getCustomItemsFromSources(ArrayList sources) {
		ArrayList all_items = source_list.getAll();
		String result = "";
		for (int i=0; i < sources.size(); i++) {
		    Object item = sources.get(i);
		    if (!all_items.contains(item)) {
			result += item+",";
		    }
		}
		return result;

	    }
	    // add/replace is only active if something is specified AND the specified index is not in the list already.
	    protected void validateAddOrReplaceButton() {
		Index index = generateNewIndex();
		if (index == null || index_model.contains(index)) {
		    add_or_replace_button.setEnabled(false);
		} else {
		    add_or_replace_button.setEnabled(true);
		}
		// we enable the add_all button if we have 2 or more sources ticked
		// unless we are in "edit index"
		if (!editing) {
		    if (source_list.isEnabled() && source_list.numTicked() > 1) {
			add_all_button.setEnabled(true);
		    } else {
			add_all_button.setEnabled(false);
		    } 
		}
	    }

	    
	    protected SearchMeta generateSearchMeta(Index index) {
		SearchMeta metadatum = new SearchMeta(index.getID(), index.getType());
		if (use_macro_as_display_name(index.getID())) {
		    metadatum.setValue(get_macro_name(index.getID()));
		} else {
		    metadatum.setValue(index.getID());
		}
		return metadatum;
	    }
	    
	    protected class AddIndexListener
		implements ActionListener
	    {
		public void actionPerformed(ActionEvent event)
		{
		    Index index = generateNewIndex();
		    if (index != null) {
			
			SearchMeta metadatum = generateSearchMeta(index);
			// Add the index
			addIndex(index, metadatum);
			index_list.setSelectedValue(index, true);
		    }
		    new_index_prompt.dispose();		    
		}
	    }

	    /** add all selected sources as separate indexes (fields).  */
	    private class AddAllActionListener
	        implements ActionListener {

	        public void actionPerformed(ActionEvent event) {
	            ArrayList sources = source_list.getTicked();

	            ArrayList new_sources = new ArrayList();
	            for(int i = 0; i < sources.size(); i++) {
	        	Object source = sources.get(i);

	        	// Create new index
	        	new_sources.clear();
	        	new_sources.add(source);
	        	Index index = createIndex(new_sources);
	        	if(!index_model.contains(index)) {
			    SearchMeta metadatum = generateSearchMeta(index);
	        	    addIndex(index, metadatum);
	        	}
	        	source = null;
	        	index = null;
	            }
	            new_sources = null;
	            new_index_prompt.dispose();
		
	        }
	    }


	    protected class SourceListListener
		implements ListSelectionListener {
	    
		public void valueChanged(ListSelectionEvent event) {
		    if (event.getValueIsAdjusting()) {
			return;
		    }
		    validateAddOrReplaceButton();
		}
	    }
	
	    protected class CustomFieldListener
		implements DocumentListener {
		public void changedUpdate(DocumentEvent e) {
		    validateAddOrReplaceButton();
		}

		/** Gives notification that there was an insert into the document. */
		public void insertUpdate(DocumentEvent e) {
		    validateAddOrReplaceButton();
		}
		
		/** Gives notification that a portion of the document has been removed. */
		public void removeUpdate(DocumentEvent e) {
		    validateAddOrReplaceButton();
		}
		
	    }

	    protected class ReplaceIndexListener
		implements ActionListener {
	    
		public void actionPerformed(ActionEvent event)
		{
		    Index index = generateNewIndex();
		    if (index != null) {
			SearchMeta metadatum = generateSearchMeta(index);
			// replace the index
			replaceIndex((Index) index_list.getSelectedValue(), index, metadatum);
			index_list.setSelectedValue(index, true);
		    }
		    new_index_prompt.dispose();
		}
	    }

	    /**
	     * If the index is built with DC metadata, use macro variable as the display text, 
	     * so that translations of DC metadata can be displayed in the search field drop-down list 
	     * when interface language changes.
	     *   
	     * @param index_id Current index id.
	     * @return Whether macro variable should be used.
	     */
	    protected boolean use_macro_as_display_name(String index_id) {

		if (index_id.indexOf(":") != -1) {
		    index_id = index_id.substring(index_id.indexOf(":") + 1);
		}
		
		String field = null;
		String[] fields = index_id.split(",");
		for (int i = 0; i < fields.length; i++) {
		    String s = fields[i];
		    if (s.indexOf(".") != -1) {	  
			s = s.substring(s.indexOf(".") + 1);
		    }
		    if (field == null) {
			field = s;
		    }
		    else if (!field.equals(s)) {
			return false;
		    }
		}
				
		field = field.toLowerCase();
		if (field.equals("text") || field.equals("title") || field.equals("creator") || field.equals("subject") || 
		    field.equals("description") || field.equals("publisher") || field.equals("contributor") || field.equals("date") || 
		    field.equals("type") || field.equals("format") || field.equals("identifier") || field.equals("source") || 
		    field.equals("language") || field.equals("relation") || field.equals("coverage") || field.equals("rights")) {			  
		    return true;
		}
		
		return false;
	    }

	    /**
	     * Get the corresponding macro variable name, eg. _labelTitle_, _labelCreator_.
	     *   
	     * @param index_id Current index id.
	     * @return Name of the macro variable.
	     */
	    protected String get_macro_name(String index_id) {
		if (index_id.indexOf(":") != -1) {
		    index_id = index_id.substring(index_id.indexOf(":") + 1);
		}
		if (index_id.indexOf(",") != -1) {
		    index_id = index_id.substring(0, index_id.indexOf(","));
		}
		if (index_id.indexOf(".") != -1) {	  
		    index_id = index_id.substring(index_id.indexOf(".") + 1);
		}	
		
		return "_label" + index_id + "_";		
	    }
	}
    }
}
