/**
 *#########################################################################
 *
 * 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.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.util.StaticStrings;
import org.w3c.dom.*;


/** This class provides an interface to any class that contains a list of Arguments.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
abstract public class ArgumentContainer 
extends ArrayList 
implements Comparable, DOMProxyListEntry, Serializable  {

	// The DOM Element this container is modelled on
	protected Element element;
	protected boolean is_abstract = false;
	protected ArgumentContainer super_container;
	protected String description;
	protected String name;

	/** Constructor used in DOMProxyListModel initializations
	 */
	public ArgumentContainer() {
	}

	public ArgumentContainer(Element element, ArgumentContainer base_container) {

		super();
		this.element = element;
		this.name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE);

		// Parse in any argument options for this container, keeping a list of the ones found
		HashMap known_arguments = new HashMap();
		NodeList option_elements = element.getElementsByTagName(StaticStrings.OPTION_ELEMENT);
		int option_elements_length = option_elements.getLength();
		for(int i = 0; i < option_elements_length; i++) {
			Element option_element = (Element) option_elements.item(i);
			Argument argument = new Argument(option_element);
			///ystem.err.println("Rebuilding existing argument: " + argument.getName());
			known_arguments.put(argument.getName(), argument);
		}
		// If a base classifier was given
		if(base_container != null) {
			// Copy the details, and add a reference to whatever the base container's super container is
			description = base_container.getDescription();
			// Now search through the 'dummy' arguments belonging to the base container. For each found, if it is already assigned, fill out further details such as type. If any are found that are not already assigned for this container, copy them and add them, but without a value.
			ArrayList all_arguments = base_container.getArguments();
			int argument_count = all_arguments.size();
			for(int j = 0; j < argument_count; j++) {
				Argument base_argument = (Argument) all_arguments.get(j);
				String base_argument_name = base_argument.getName();
				///ystem.err.println("Library indicates this container should have an argument: " + base_argument_name);
				Argument existing_argument = (Argument) known_arguments.get(base_argument_name);
				// Found an existing argument. Complete its details
				if(existing_argument != null) {
					///ystem.err.println("Found existing argument. Filling out details.");
					known_arguments.remove(base_argument_name);
					existing_argument.setDisplayName(base_argument.getDisplayName());
					existing_argument.setDefaultValue(base_argument.getDefaultValue());
					existing_argument.setDescription(base_argument.getDescription());
					existing_argument.setOptions(base_argument.getOptions());
					existing_argument.setRequired(base_argument.isRequired());
					existing_argument.setType(base_argument.getType());
					existing_argument.setMinimum(base_argument.getMinimum());
					existing_argument.setMaximum(base_argument.getMaximum());
					existing_argument.setOwner(base_argument.getOwner());
					existing_argument.setHiddenGLI(base_argument.isHiddenGLI());
					add(existing_argument);
				}
				// No existing argument. Copy base_argument and add it, but set its assigned flag to false 
				else {
					///atherer.println("No such argument. Adding new, unassigned, argument.");
					// The trick thing is that we have to create a new element in the DOM as well.
					Argument new_argument = base_argument.copy();
					Element argument_element = CollectionConfiguration.createElement(StaticStrings.OPTION_ELEMENT);
					argument_element.setAttribute(StaticStrings.NAME_ATTRIBUTE, base_argument_name);
					argument_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.FALSE_STR);
					new_argument.setElement(argument_element);
					// All done. Add it.
					element.appendChild(argument_element);
					add(new_argument);
				}
			}
		}

		// The first time we do this, the args come from the config file and
		// may contain invalid args. Here we remove them from the DOM.
		if (!known_arguments.isEmpty()) {
			Set entry_set = known_arguments.entrySet();
			Iterator entry_iterator = entry_set.iterator();
			while (entry_iterator.hasNext()) {
				Argument arg = (Argument)((Map.Entry)entry_iterator.next()).getValue();
				Element e = arg.getElement();
				element.removeChild(e);
			}
		}
	}




	/** Constructor used for the plugins that are in the DOMProxyList */
	/** Method to add an argument to this container. Only adds the argument if it isn't already present.
	 * @param argument The <strong>Argument</strong> to add.
	 */
	public void addArgument(Argument argument) {
		if(element == null && !contains(argument)) {
			add(argument);
			argument.setOwner(name);
		}
	}

	/** Method to compare two containers for ordering.
	 * @param object The container we are comparing to, as an <strong>Object</strong>.
	 * @return An <i>int</i> specifying the container order, using values as set out in String.
	 * @see java.lang.String#compareTo
	 */
	public int compareTo(Object object) {
		if(object == null ) {
			return -1;
		}
		return toString().compareTo(object.toString());
	}

	/** Method to determine if two containers are equal.
	 * @param object The container to test against, as an <strong>Object</strong>.
	 * @return <i>true</i> if they match (based on the equals method), <i>false</i> otherwise.
	 */

	public boolean equals(Object object) {
		return (compareTo(object) == 0);
	}

	abstract public DOMProxyListEntry create(Element element);
	/** Method to retrieve an argument by its name.
	 * @param name The name of the argument as a <strong>String</strong>.
	 * @return The <strong>Argument</strong> requested, or <i>null</i> if no such argument.
	 */
	public Argument getArgument(String name) {
		// The name given may still include the '-'
		if(name.startsWith("-")) {
			name = name.substring(1);
		}
		//ArrayList arguments = getArguments(true, true);
		ArrayList arguments = getArguments();
		for(int i = 0; i < arguments.size(); i++) {
			Argument argument = (Argument)arguments.get(i);
			if(argument.getName().equals(name)) {
				return argument;
			}
		}
		return null;
	}

	/** Method to retrieve the list of arguments from this container. Note that this method returns both the containers arguments plus its 'supers' arguments if any, and alphabetically orders them.
	 * @return the arguments within a ArrayList
	 */
	public ArrayList getArguments() {
		ArrayList arguments = new ArrayList();
		arguments.addAll(this);
		if(super_container != null) {
			ArrayList remainder = super_container.getArguments();
			remainder.removeAll(arguments);
			arguments.addAll(remainder);
		}
		return arguments;

	}

	public String getDescription() {
		return description;
	}

	public Element getElement() {
		return element;
	}
	/** Method to retrieve the name associated with this argument container.
	 * @return the name as a String
	 */
	public String getName() {
		if(name == null && element != null) {
			name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE);
		}
		return name;
	}

	public boolean isAbstract() {
		return is_abstract;
	}

	public boolean isAssigned() {
		return (element != null && !element.getAttribute(StaticStrings.ASSIGNED_ATTRIBUTE).equals(StaticStrings.FALSE_STR));
	}

	public void setAssigned(boolean assigned) {
		if(element != null) {
			element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, (assigned ? StaticStrings.TRUE_STR : StaticStrings.FALSE_STR));
		}
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public void setElement(Element element) {
		this.element = element;
	}

	public void setIsAbstract(boolean is_abstract) {
		this.is_abstract = is_abstract;
	}

	public void setName(String name) {
		this.name = name;
	}

	/** Method to set the value of the super_container.
	 * @param super_container The new value of super_container as a <strong>ArgumentContainer</strong>, or <i>null</i> if this class has no inheritance.
	 */
	public void setSuper(ArgumentContainer super_container) {
		this.super_container = super_container;
	}

	public String toString()
	{
		if (element == null) {
			return name;
		}

		if (name == null) {
			name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE);
		}

		StringBuffer text = new StringBuffer(" ");
		if (!isAssigned()) {
			text.append("#");
		}
		text.append(name);

		ArrayList arguments = getArguments();
		for (int i = 0; i < arguments.size(); i++) {
			Argument argument = (Argument) arguments.get(i);
			if (argument.isAssigned()) {
				text.append(" ");
				text.append(argument.toString());
			}
		}

		return text.toString();
	}

	public ArrayList getArguments(boolean include_normal, boolean include_custom){
		return this;
	}
}
