/**
 *#########################################################################
 *
 * 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.
 *
 * Copyright (C) 2024 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.gui;

// for RSyntaxTextArea editor's search functionality:
//import org.fife.ui.rtextarea.SearchEngine;
//import org.fife.ui.rtextarea.SearchContext;

//import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;
import org.greenstone.gatherer.util.XMLTools;

import org.w3c.dom.*;

import java.io.File;

import java.awt.*;
import java.util.*;

public class CollectionConfigFileEditor extends XMLFileEditor 
{

    // Enum to define elements the user can add
    // enum available from Java 1.5
    // https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
    // https://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html
    protected enum ElementOption {	
	HIDE_COLLECTION (Dictionary.get("CollectionConfigFileEditor.Hide_Collection")),
	SECURE_COLLECTION (Dictionary.get("CollectionConfigFileEditor.Secure_Collection")),
	SECURE_ALL_DOCUMENTS(Dictionary.get("CollectionConfigFileEditor.Secure_All_Documents")),
	SECURE_CERTAIN_DOCUMENTS(Dictionary.get("CollectionConfigFileEditor.Secure_Certain_Documents")),
	ADD_ANOTHER_DOCUMENTSET(Dictionary.get("CollectionConfigFileEditor.Add_Another_DocumentSet")),
	ADD_ANOTHER_EXCEPTION(Dictionary.get("CollectionConfigFileEditor.Add_Another_Exception"));	
	
	protected final String displayString;
	// Constructor may not be declared as public
	ElementOption(String displayStr) {
	    // interesting that Enum implementation classes don't
	    // have to call protected superclass constructor
	    displayString = displayStr;
	}

	// override default behaviour of returning the ENUM string itself
	// to display the displayString instead
	public String toString() {
	    return displayString;
	}
    }

  protected String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };

  public CollectionConfigFileEditor(File config_file) {
    super(config_file);
  }

  /** implement this if you want pre defined elements to add */
  protected ElementOption[] getElementOptions() {
    return ElementOption.values();
  }
  
  protected boolean addElementsEnabled() {
    return true;
  }
  protected String getElementsXMLFileString() {
    return "xml/elementsForInsertion.xml";
  }


  protected void customActionOnSave() {

    // close and reopen the collection in GLI
    String current_collection_filepath = Gatherer.c_man.getCollection().getCollectionPath();
    ////Gatherer.c_man.closeCollection(); // explicit save was necessary after all
    // closeCollection() doesn't auto-save
    ////Gatherer.c_man.loadCollection(current_collection_filepath);
    
    
    // Fixing bug: Edit > colcfg wasn't being saved on Save when client-GLI.
    // And in fact, it wasn't properly saving in GLI for some GLI panes.
    // That's because save needs to be explicitly called. (Didn't understand how GLI's
    // "autosave" worked until now. GLI > File > Close a collection saves it, but underneath
    // it calles SAVEandCloseCollection.
    
    // Works - but arduous: have to load in the entire collection just because cfg changed?
    // Especial pain with client-GLI where reloading takes forever.
    //Gatherer.g_man.saveThenCloseCurrentCollection();
    //Gatherer.c_man.loadCollection(current_collection_filepath);
    
    // Better way: don't have to close and reopen the entire collection after editing colCfg.xml
    // This just updates GLI's interface with the current collCfg.xml.
    // Fingers crossed this works for client-GLI, because then the coll won't need to be closed
    // and reopened each time after Edit > collectionCfg.xml gets saved.
    Gatherer.c_man.saveCollection(); // should save colcfg whether GLI or client-GLI
    Gatherer.c_man.reloadAfterConfigFileEdited();
    //Gatherer.g_man.updateUI(); // didn't do what I hoped it would, so is this relevant here?
    // It's done when a new collection is loaded, so may be useful?
    // But all my tests concerning GLI > Edit > collConfig.xml worked out without it
    
    
    
  }
  
  protected void addElementToFile() {
    ElementOption elementToAdd = (ElementOption)chooseElementComboBox.getSelectedItem();
    addElementToConfigFile(elementToAdd);
    editor.setCaretPosition(0);
  }
    /** Method to add the selected security-related element to the config file
     * so that the user can then edit it to behave as they wish.
     */
    protected void addElementToConfigFile(ElementOption elementOption) {

	// if the user just undid a previous add operation, then the editor would be empty
	// and can't be parsed. In such a case, don't try to add any elements into the editor
	String config_xml_str = editor.getText();
	if(config_xml_str.equals("")) {
	    editor_msgarea.setText(Dictionary.get("CollectionConfigFileEditor.Press_Undo_Once_More_First"));
	    editor_msgarea.setBackground(Color.orange);
	    return;
	}
	Document config_xml_doc = XMLTools.getDOM(config_xml_str);
	// The addElementButton would not have been enabled/available for pressing
	// and we wouldn't be here unless the collConfig xml parses fine.	

	Element targetParent = config_xml_doc.getDocumentElement(); // tentative target element
	

	final String SECURITY = "security"; // just looking for security elements to insert
	final String EXCEPTION = "exception"; // just looking for security elements to insert
	final String DOCUMENTSET = "documentSet"; // just looking for security elements to insert
	
	NodeList children = elements_available_for_insertion.getElementsByTagName(SECURITY);	    

	Element elementToAdd = null;
	boolean contentsChanged = false;
	// To find out if the collectionConfig.xml has any security nodes in it already
	NodeList configXMLSecurityNodes = config_xml_doc.getElementsByTagName(SECURITY);

	
	// Switch on an Enum type is interesting:
	// don't have to do "case ElementOption.HIDE_COLLECTION:"
	// and just "case HIDE_COLLECTION:" is fine
	// see https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

	switch(elementOption) {
	case HIDE_COLLECTION:
          Element metadataList = (Element)XMLTools.getChildByTagName(targetParent, "metadataList");
          if (metadataList != null) {
	    Element publicElement = XMLTools.getNamedElement(
					      metadataList, // parent
					      "metadata", // element name
					      "name", // attribute name
					      "public" // attribute value
				      );
	    
	    if(publicElement != null) {
              XMLTools.setNodeText(publicElement, "false");
              contentsChanged = true;
              // elementToAdd remains null, as we don't need to add anything
	    } else { // collConfig.xml missing <metadata name="public">true</metadata>
		// need to add in the necessary element from elements_available_for_insertion XML,
		// which is set to hide
              targetParent = metadataList;
              elementToAdd = XMLTools.getNamedElement(
					elements_available_for_insertion.getDocumentElement(), // parent
					"metadata", // element name
					"name", // attribute name
					"public" // attribute value
				);
	    }
          }
          break;
	    
	case SECURE_COLLECTION:
	case SECURE_ALL_DOCUMENTS:
	case SECURE_CERTAIN_DOCUMENTS:
	    if(configXMLSecurityNodes.getLength() == 0) {
		elementToAdd = (Element)children.item(elementOption.ordinal()-1);
	    } else {
		editor_msgarea.setText(Dictionary.get("CollectionConfigFileEditor.Config_Already_Has_Security_Element"));
		editor_msgarea.setBackground(Color.orange);
	    }
	    break;
	    
	    
	case ADD_ANOTHER_DOCUMENTSET:
	case ADD_ANOTHER_EXCEPTION:
	    
	    if(configXMLSecurityNodes.getLength() > 0) {

		// get first security element child of collectionConfigXML file.
		// There should at most be one
		targetParent = (Element)configXMLSecurityNodes.item(0);
		Element lastSecurityElement = (Element)children.item(children.getLength()-1);
	    
		if(elementOption == ElementOption.ADD_ANOTHER_DOCUMENTSET) {
		    // Get the immediate child called documentSet, not any other descendants by that name
		    // i.e. not the child <exception> element's child called <documentSet>, but the last
		    // <security> element's <documentSet> element
		    elementToAdd = (Element)XMLTools.getChildByTagName(lastSecurityElement, DOCUMENTSET);
		}
		else { // ElementOption.ADD_ANOTHER_EXCEPTION:

		    // Get the last <security> element's sole <exception> child
		    elementToAdd = (Element)XMLTools.getChildByTagName(lastSecurityElement, EXCEPTION);
		}
	    }
	    else {
		// Can't Add Another DocSet/Exception element if the collectionConfigXML has no
		// Security element in place to add them into.
		// Display an error/warning if no security element and don't do anything.	    
		
		targetParent = null;
		editor_msgarea.setText(Dictionary.get("CollectionConfigFileEditor.Need_Security_Element_To_Add"));
		editor_msgarea.setBackground(Color.orange);
	    }
	    break;
	}

        // end switch
	if(elementToAdd != null) {
	    Node copiedNode = config_xml_doc.importNode(elementToAdd, true);
	    targetParent.appendChild(copiedNode);
            contentsChanged = true;
        }

        if (contentsChanged) {
	    StringBuffer xml_str_buf = new StringBuffer();
	    // now set the text in the XML editor to reflect the changes to the doc	    
	    XMLTools.xmlNodeToString(xml_str_buf, config_xml_doc.getDocumentElement(), true, "  ", 0); 
	    editor.setText(xml_str_buf.toString()); // display the xml contents with error and all	    
	} // TODO: sadly, the act of doing a editor.setText() adds 2 actions to undo history instead of 1
    }
   




}
