/**
 *#########################################################################
 *
 * 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.GComboBox;
import org.greenstone.gatherer.gui.GLIButton;
import org.greenstone.gatherer.gui.ModalDialog;
import org.greenstone.gatherer.gui.SimpleMenuBar;
import org.greenstone.gatherer.gui.TestingPreparation;
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 class is resposible for storing the indexes which have been assigned to this collection and the default index, 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 class SearchIndexManager extends BaseIndexManager {

    static final private Dimension FIELD_SIZE = new Dimension(200,30);
    static final private String ALLFIELDS = "allfields";

    public SearchIndexManager(Element indexes, String current_build_type) {

      super(indexes, current_build_type, StaticStrings.INDEX_ELEMENT, StaticStrings.INDEX_DEFAULT_ELEMENT, (current_build_type.equals(BuildTypeManager.BUILD_TYPE_MG)?new MGIndex():new Index()));

      this.controls_title_key = "CDM.IndexManager.Indexes";
      this.new_button_tooltip_key = "CDM.IndexManager.New_Button_Tooltip";
      this.edit_button_tooltip_key = "CDM.IndexManager.Edit_Button_Tooltip";
      this.remove_button_tooltip_key = "CDM.IndexManager.Remove_Button_Tooltip";
      this.default_indicator_key = "CDM.IndexManager.Default_Index_Indicator";
      this.nip_new_index_key = "CDM.IndexManager.New_Index";
      this.nip_edit_index_key = "CDM.IndexManager.Edit_Index";
      this.nip_source_label_key = "CDM.IndexManager.Source";
      this.nip_source_tooltip_key = "CDM.IndexManager.Source_Tooltip";
      this.nip_custom_label_key = "CDM.IndexManager.Custom";
      this.nip_custom_tooltip_key = "CDM.IndexManager.Custom_Tooltip";

      this.nip_add_index_button_key = "CDM.IndexManager.Add_Index";
      this.nip_add_index_tooltip_key = "CDM.IndexManager.Add_Index_Tooltip";
      this.nip_replace_index_button_key = "CDM.IndexManager.Replace_Index"; 
      this.nip_replace_index_tooltip_key = "CDM.IndexManager.Replace_Index_Tooltip";  
      this.nip_add_all_index_button_key = "CDM.IndexManager.AddAll";
      this.nip_add_all_index_tooltip_key = "CDM.IndexManager.AddAll_Tooltip";
      
    }


    public void buildTypeChanged(String new_build_type) {
	if (build_type.equals(new_build_type)) {
	    return;
	}
	// we don;t care about this if old or new is not MG as MGPP and 
	// Lucene have the same index specification
	if (!build_type.equals(BuildTypeManager.BUILD_TYPE_MG) && !new_build_type.equals(BuildTypeManager.BUILD_TYPE_MG)) {
	    return;
	}
	boolean mg_to_mgpp = true;
	if (new_build_type.equals(BuildTypeManager.BUILD_TYPE_MG)) {
	    mg_to_mgpp = false;
	}
	if (mg_to_mgpp) {
	    changeToMGPPIndexes();
	} else {
	    changeToMGIndexes();
	}
	build_type  = new_build_type;
	// its really hard to transfer defaults between mgpp/lucene and mg indexes, so we'll just set the first one to be the default
	Index first_index = (Index) getElementAt(0);
	setDefault(first_index);
	first_index = null;
    }
    
    private void changeToMGIndexes() {
	this.setClassType(new MGIndex());
	Element mgpp_element = root;
	// Retrieve and assign MG element and default index element
	Element mg_element = CollectionDesignManager.collect_config.getMGIndexes();
	mg_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.TRUE_STR);
	NodeList indexes = mg_element.getElementsByTagName(StaticStrings.INDEX_ELEMENT);
	// Replace mgpp element with mg element
	setRoot(mg_element);
	
	if(indexes.getLength() == 0) {
	  // we need to create some based on the mgpp indexes
	  
	  // If the current mgpp index includes a text one, then generate text indexes for each of the registered levels.
	  Index index = getIndex(StaticStrings.TEXT_STR);
	  if(index != null) {
	    ArrayList levels = CollectionDesignManager.index_manager.getLevels();
	    int level_size = levels.size();
	    for(int i = 0; i < level_size; i++) {
	      IndexOption level = (IndexOption) levels.get(i);
	      Index new_index = new MGIndex(level.getName(), index.getSources());
	      // Try to retrieve existing metadatum
	      String source_str = new_index.getID();
	      SearchMeta metadatum = CollectionDesignManager.searchmeta_manager.getMetadatum(source_str, new_index.getType(), false);
	      // If no metadata was found, add new pseudo metadata using the id
	      if(metadatum == null) {
		  metadatum = new SearchMeta(source_str, new_index.getType());
		metadatum.setAssigned(true);
		metadatum.setValue(source_str);
	      }
	      // If it was found, ensure it is assigned
	      else {
		metadatum.setAssigned(true);
	      }
	      source_str = null;
	      addIndex(new_index, metadatum);
	      new_index = null;
	      level = null;
	    }
	  }
	}
	
	// Unassign mgpp element 
	mgpp_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.FALSE_STR);
	mgpp_element = null;
	
    }

    private void changeToMGPPIndexes() {
	this.setClassType(new Index());
	Element mg_element = root;
	// Retrieve and assign the MGPP indexes element.
	Element mgpp_element = CollectionDesignManager.collect_config.getMGPPIndexes();
	mgpp_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.TRUE_STR);
	NodeList indexes = mgpp_element.getElementsByTagName(StaticStrings.INDEX_ELEMENT);
	if(indexes.getLength() != 0) {
	    // we just reinstate the indexes we had before the change
	    setRoot(mgpp_element);
	} else {
	    // If the MGPP indexes element is empty (ie was created by CollectionConfiguration), generate new MGPP index from the existing index
	    
	    ArrayList sources_list = new ArrayList();

	    // We first use details from the default index if any
	    if(default_index != null) {
		ArrayList sources = default_index.getSources();
		sources_list.addAll(sources);
	    }
	    int size = getSize();
	    for(int i = 0; i < size; i++) {
		Index index = (Index) getElementAt(i);
		ArrayList sources = index.getSources();
		sources.removeAll(sources_list);
		sources_list.addAll(sources);
		index = null;
	    }
	    // Replace mg element with mgpp element
	    setRoot(mgpp_element);
	    
	    // We now have a list of sources, so create new indexes based on these
	    int sources_list_size = sources_list.size();
	    for(int j = 0; j < sources_list_size; j++) {
		Object source_object = sources_list.get(j);
		String source_str = null;
		if(source_object instanceof MetadataElement) {
		    source_str = ((MetadataElement) source_object).getFullName();
		}
		else {
		    source_str = source_object.toString();
		}
		ArrayList new_sources = new ArrayList();
		new_sources.add(source_object);
		source_object = null;
		Index new_index = new Index(new_sources);
		// Try to retrieve existing metadatum
		source_str = new_index.getID();
		SearchMeta metadatum = CollectionDesignManager.searchmeta_manager.getMetadatum(source_str, new_index.getType(), false);
		// If no metadata was found, add new pseudo metadata using the id
		if(metadatum == null) {
		    metadatum = new SearchMeta(source_str, new_index.getType());
		    metadatum.setAssigned(true);
		    metadatum.setValue(source_str);
		}
		// If it was found, ensure it is assigned
		else {
		    metadatum.setAssigned(true);
		}
		source_str = null;
		addIndex(new_index, metadatum);
		metadatum = null;
		new_index = null;
		new_sources = null;
		source_str = null;
	    }	      
	    
	}

	// Unassign MG element
	mg_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.FALSE_STR);
	mg_element = null;
	
    }
    


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




    /** 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(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.INDEX_DEFAULT_ELEMENT);             
		if (build_type.equals(BuildTypeManager.BUILD_TYPE_MG)) {
		    default_index = new MGIndex(default_index_element);
		} else {
		    default_index = new Index(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);
		}
	    } else {
		if (build_type.equals(BuildTypeManager.BUILD_TYPE_MG) &&! (default_index instanceof org.greenstone.gatherer.cdm.MGIndex)) {
		    default_index = new MGIndex(default_index.getElement());
		} else if (!build_type.equals(BuildTypeManager.BUILD_TYPE_MG) && default_index instanceof org.greenstone.gatherer.cdm.MGIndex) {
		    default_index = new Index(default_index.getElement());
		}
	    }
	    default_index.setAssigned(true);
	    default_index.setSources(index.getSources());
	    if (build_type.equals(BuildTypeManager.BUILD_TYPE_MG)) {
		((MGIndex)default_index).setLevel(((MGIndex)index).getLevel());
	    }
	    
	}
	else {
	    if(default_index != null) {
		default_index.setAssigned(false);
	    }
	}
    }

  
  private class SearchIndexControl
    extends IndexControl { 
    
    public SearchIndexControl() {
      super();
      
      TestingPreparation.setNamesRecursively(this);
    }
    
    
    /** we want our own custom new index prompt for search indexes */
    protected NewIndexPrompt createNewIndexPrompt(String build_type, Index index) {
      return new NewSearchIndexPrompt(build_type, index);
        
    }
  	
    /** we customise this to add text box, allfields, and add all, select all buttons etc */
	protected class NewSearchIndexPrompt 
	    extends NewIndexPrompt {

  	    private JCheckBox text_checkbox;
	    // mg uses a level box
	    private JComboBox level_combobox;
	    // mgpp has a allfields selector
	    private JCheckBox allfields_box;

	     private JButton select_all_button;
	     private JButton select_none_button;
          //private JButton add_all_button;
	    
	    private boolean mgpp_enabled = false;
	    private boolean editing = false;
	
          public NewSearchIndexPrompt(String build_type, Index existing_index) {
            super(build_type, existing_index);	    
          }
          
          /** inside here is where we customise our controls */
          protected void generateContents(String build_type, Index existing_index) {
            if (build_type.equals(BuildTypeManager.BUILD_TYPE_MG)) {
              mgpp_enabled = false;
            } else {
              mgpp_enabled = true;
            }
            super.generateContents(build_type, existing_index);
            text_checkbox = new JCheckBox(Dictionary.get("CDM.IndexManager.Text_Source"));
            text_checkbox.setToolTipText(Dictionary.get("CDM.IndexManager.Text_Source_Tooltip"));
            text_checkbox.setComponentOrientation(Dictionary.getOrientation());                
            text_checkbox.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent event) {
                validateAddOrReplaceButton();
              }
            });
            // if (existing_index == null && mgpp_enabled) {
            //   button_pane.setLayout(new GridLayout(2,3,5,0));
            //   JPanel tmp = new JPanel();
            //   tmp.setComponentOrientation(Dictionary.getOrientation());
            //   button_pane.add(tmp);
            // } else {
              button_pane.setLayout(new GridLayout(2,2,5,0));
              //}
            
            select_all_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_All"), Dictionary.get("CDM.IndexManager.Select_All_Tooltip"));
            select_all_button.addActionListener(new ActionListener() {
              
              public void actionPerformed(ActionEvent event) {
                text_checkbox.setSelected(true);
                source_list.setAllTicked();
                validateAddOrReplaceButton();
              }
            });
            
            select_none_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_None"), Dictionary.get("CDM.IndexManager.Select_None_Tooltip"));
            select_none_button.addActionListener(new ActionListener() {
              
              public void actionPerformed(ActionEvent event) {
                text_checkbox.setSelected(false);
                source_list.clearTicked();
                validateAddOrReplaceButton();
              }
            });
            
            
            button_pane.add(select_all_button);
            button_pane.add(select_none_button);

            // if (existing_index == null && mgpp_enabled) {
            //   add_all_button = new GLIButton(Dictionary.get("CDM.IndexManager.Add_All"), Dictionary.get("CDM.IndexManager.Add_All_Tooltip"));
            //   add_all_button.setEnabled(true);
            //   add_all_button.addActionListener(new AddAllIndexActionListener());
            //   button_pane.add(add_all_button);
            // }

            button_pane.add(add_or_replace_button);
            button_pane.add(cancel_button);
            details_pane.add(text_checkbox, BorderLayout.NORTH);
            // do type specific stuff
            if (mgpp_enabled) {
              // allfields
              allfields_box = new JCheckBox(Dictionary.get("CDM.IndexManager.Allfields_Index"));
              allfields_box.addItemListener(new AllFieldsBoxListener());
              allfields_box.setComponentOrientation(Dictionary.getOrientation());
              //JLabel allfields_label = new JLabel(Dictionary.get("CDM.IndexManager.Allfields_Index"));
              extra_pane.add(allfields_box);
              
              
            } else {
              // index level
              JLabel level_label = new JLabel(Dictionary.get("CDM.IndexManager.Level"));
              level_label.setComponentOrientation(Dictionary.getOrientation());
              
              level_combobox = new JComboBox();
              level_combobox.setOpaque(false);
              level_combobox.setPreferredSize(FIELD_SIZE);
              // Note the order of these must be the same as the
              // level order in Index
              level_combobox.addItem(StaticStrings.DOCUMENT_STR);//Dictionary.get("CDM.LevelManager.Document"));
              level_combobox.addItem(StaticStrings.SECTION_STR);//Dictionary.get("CDM.LevelManager.Section"));
              level_combobox.addItem(StaticStrings.PARAGRAPH_STR);//Dictionary.get("CDM.LevelManager.Paragraph"));
              level_combobox.setEditable(false);
              level_combobox.setToolTipText(Dictionary.get("CDM.IndexManager.Level_Tooltip"));
              level_combobox.setComponentOrientation(Dictionary.getOrientation());
              level_combobox.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                  validateAddOrReplaceButton();
                }
              });
              JPanel level_pane = new JPanel();
              level_pane.setComponentOrientation(Dictionary.getOrientation());
              level_pane.setLayout(new BorderLayout());
              level_pane.add(level_label, BorderLayout.LINE_START);
              level_pane.add(level_combobox, BorderLayout.CENTER);
              extra_pane.add(level_pane);

            }
            // if we are editing, fill in the controls
            if (existing_index !=null) {
              ArrayList sources = existing_index.getSources();
              if (mgpp_enabled && sources.get(0).equals(ALLFIELDS)) {
                allfields_box.setSelected(true);
                source_list.setEnabled(false);
		custom_field.setEnabled(false);
              } else {
		  source_list.setTickedObjects(sources.toArray());
		  source_list.setEnabled(true);
                if (sources.contains(StaticStrings.TEXT_STR)) {
                  text_checkbox.setSelected(true);
                }
              } 
              if (!mgpp_enabled && existing_index instanceof MGIndex) {
                level_combobox.setSelectedIndex(((MGIndex)existing_index).getLevel());
              }
		    
            }

	    // Setting custom names on select widgets to avoid naming collisions,
	    // as default names are based on type and both are checkboxes.
	    TestingPreparation.setIndividualSubcomponentNames(this, text_checkbox, allfields_box);
	    
          }

	    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)&& !item.equals(StaticStrings.TEXT_STR) && !item.equals(ALLFIELDS)) {
			result += item+",";
		    }
		}
		return result;

	    }

	    // Checks that specified index not already in the collection
	    // I think base version of this is sufficient
	    protected void validateAddOrReplaceButtonOld() {
		Index index;
		ArrayList sources;
		if (mgpp_enabled && allfields_box.isSelected()) {
		    sources = new ArrayList();
		    sources.add(ALLFIELDS);
		    index = new Index(sources);
		    
		} else if (text_checkbox.isSelected() || 
			   !source_list.isNothingTicked()) {
		    sources = source_list.getTicked();
		    if (text_checkbox.isSelected()) {
			sources.add(0, StaticStrings.TEXT_STR);
		    }		    
		    if (mgpp_enabled) {
			index = new Index(sources);
		    } else {
			index = new MGIndex(level_combobox.getSelectedIndex(), sources);
		    }
		} else {
		    // nothing selected
		    add_or_replace_button.setEnabled(false);
		    return;
		}
		
		sources = null;
		if (index_model.contains(index)) {
		    add_or_replace_button.setEnabled(false);
		}
		else {
		    add_or_replace_button.setEnabled(true);
		}
		
	    }

	    protected Index generateNewIndex() {
		Index index = null;
		ArrayList sources;
		String custom_text = custom_field.getText();
		if (mgpp_enabled && allfields_box.isSelected()) {
		    sources = new ArrayList();
		    sources.add(ALLFIELDS);
		    index = new Index(sources);
		}
		else if (text_checkbox.isSelected() || !source_list.isNothingTicked() || !custom_text.equals("")) {
		    sources = source_list.getTicked();
		    if (text_checkbox.isSelected()) {
			sources.add(0, StaticStrings.TEXT_STR);
		    }
		    addCustomItemsToSources(sources, custom_text);
		    if(mgpp_enabled) {
			index = new Index(sources);
		    } else {
			index = new MGIndex(level_combobox.getSelectedIndex(), sources);
		    }
		}
		return index;
	    }

    
	    private class AllFieldsBoxListener
		implements ItemListener {
		
		public void itemStateChanged(ItemEvent event) {
		    if (event.getStateChange() == ItemEvent.SELECTED) {
			source_list.setEnabled(false);
			custom_field.setEnabled(false);
                        text_checkbox.setEnabled(false);
		    } else if (event.getStateChange() == ItemEvent.DESELECTED) {
			source_list.setEnabled(true);
			custom_field.setEnabled(true);
                        text_checkbox.setEnabled(true);
		    }
		    validateAddOrReplaceButton();
		}
		
	    }


	} // NewSearchIndexPrompt
  }// SearchIndexControl
} // SearchIndexManager

