/* Title:		Parameter.java
 * Version	
 * Author		Sada
 * Company: USDA-ARS
 * Date:    July, 2003
 * Description: Holds parameter information (name and  value)
 * 
 */
package usda.weru.mcrew;

import java.util.*;

import org.w3c.dom.*;
import org.w3c.dom.traversal.*;

/**
 * Holds parameter information (name and  value) and the methods that are used for
 * data retrieval and manipulation.
 */
public class Parameter implements Cloneable {

    String mParameterName = "";
    Hashtable<String, String> mParameter;

    /**
     * Default constructor tha builds the object initializing some data structures 
     * which holds the required values.
     */
    public Parameter() {
        mParameterName = "";
        mParameter = new Hashtable<>();
    }

    /**
     * Single parameter constructor that takes in the name of the parameter as 
     * its argument.
     * @param pName The name of the parameter.
     */
    public Parameter(String pName) {
        mParameterName = pName;
        mParameter = new Hashtable<>();
    }

    /**
     * Two parameter constructor that takes in the name of the parameter and the
     * value to be assigned to it as its arguments.
     * @param pName The name of the parameter.
     * @param pValue The value to be assigned to that parameter.
     */
    public Parameter(String pName, String pValue) {
        mParameter = new Hashtable<>();
        mParameter.put(XMLConstants.sname, pName);
        mParameter.put(XMLConstants.svalue, pValue);
    }

    /**
     * Getter method that fetches the object associated with the parameter as its value.
     * @return The object as the parameter value from the hashtable that stores it 
     * based on parameter's name as its key.
     */
    public Object getValue() {
        if (mParameter != null) {
            return mParameter.get(XMLConstants.svalue);
        }
        return null;
    }

    /**
     * This function can be used to set the value of the parameter 
     * @param pVal The new value to be set as a replacement value.
     */
    public void setValue(String pVal) {
        if (mParameter != null) {
            mParameter.remove(XMLConstants.svalue); // remove old value
            mParameter.put(XMLConstants.svalue, pVal);
        }
    }

    /**
     * Fetches the value associated with the name attribute of the parameter
     * @return The name of the parameter.
     */
    public String getName() {
        if (mParameter != null) {
            String param = mParameter.get(XMLConstants.sname);
            ////System.out.println( " Parameter : getName() : " + param);
            return param;
        }
        return null;
    }

    /**
     * Gets the value of the parameter pName stored in the vector as objects.
     * @param pName The argument pName is 'name' or 'value' under which the objects are stored
     * @return The value stored as string object.
     */
    public String get(String pName) {
        if (mParameter != null) {
            if (pName.equals(XMLConstants.sparamname)) {
                return mParameter.get(XMLConstants.sname);
            }

            return mParameter.get(pName); //the argument pName is 'name' or 'value' under which the objects are also stored
        }
        return null;
    }

    /**
     * This method initializes by retrieving the node info for the parameters
     * and populating the data structures like hashtables with the node name as key 
     * and the text data from the XML file as their data values.
     * @param pNode The node whose data need to be initialized/updated.
     */
    public void initialize(Node pNode) {
        /* Parameters are stored this way
         KEY 			OBJECT
         "name"		"killflg"
         "value"		2
         i.e there are 2 entries in the mParameter table corresponding to the <param> entity in the data XML files
         */
        DocumentTraversal traversable;

        if (pNode.getOwnerDocument() == null) {
            traversable = (DocumentTraversal) pNode;
        } else {
            traversable = (DocumentTraversal) pNode.getOwnerDocument();
        }

        TreeWalker walker = traversable.createTreeWalker(pNode, NodeFilter.SHOW_ALL, null, false);

        Node nodeChild = walker.firstChild();
        while (nodeChild != null) {
            String nodeChildName = nodeChild.getNodeName();
            String nodeChildData = XMLDoc.getTextData(nodeChild);
                       // //System.out.println("Parameter :initialize()" + " nodeChildName = " + nodeChildName + ", nodeChildData = " + nodeChildData );

            if ((nodeChildName != null) && (nodeChildData != null)) {
                mParameter.put(nodeChildName, nodeChildData);
            }

            /*Node textNode = walker.firstChild();
             String nodeChildData = "";
             if(textNode != null) 
             nodeChildData = textNode.getNodeValue();
             mParameter.put(nodeChildName, nodeChildData);

             walker.parentNode(); */
            nodeChild = walker.nextSibling();
        }

    }

    /**
     *
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Parameter other = (Parameter) obj;
        if (this.mParameterName != other.mParameterName && (this.mParameterName == null || !this.mParameterName.equals(other.mParameterName))) {
            return false;
        }
        if (this.mParameter != other.mParameter && (this.mParameter == null || !this.mParameter.equals(other.mParameter))) {
            return false;
        }
        return true;
    }

    /**
     *
     * @return
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 17 * hash + (this.mParameterName != null ? this.mParameterName.hashCode() : 0);
        return hash;
    }

    /**
     * This method initializes by retrieving the node info for the parameters
     * and populating the data structures like hashtables with the node name as key 
     * and the text data from the XML file as their data values.
     * @param pName The name of the parameter whose value is to be initialised.
     * @param pValue The value to be set during initializarion of the parameter
     * with name pName.
     */
    public void initialize(String pName, String pValue) {
        mParameter.put(XMLConstants.sname, pName);
        mParameter.put(XMLConstants.svalue, pValue);
    }

    /**
     * This method is used to change the values of the parameter to whatever value
     * pParam is set to.
     * @param pParam The parameter with new set of values to be assigned.
     */
    public void changeValue(Parameter pParam) {
        String newValue = pParam.get(XMLConstants.svalue);
        if (newValue != null) {
            mParameter.remove(XMLConstants.svalue);
            mParameter.put(XMLConstants.svalue, newValue);
        } else {
        }	//System.err.println("Parameter:changeValue()->" +"Theres no value for parameter "+ mParameter.get(XMLConstants.sname));
    }

    /**
     * Fetches the parameter node by creating a name and a valu node for each parameter 
     * and finally appending it to the document as a new node.
     * @param doc The document that the node should be appended to
     * @return The new parameter node that was just created.
     */
    public Node getNode(Document doc) {
        Node paramNode = doc.createElement(XMLConstants.sparam);

        if (mParameter == null) {
            return paramNode;
        }

        Node nameNode = doc.createElement(XMLConstants.sname);
        String name = mParameter.get(XMLConstants.sname);
        XMLDoc.setTextData(nameNode, name, doc); // set the name as text node child of nameNode

        Node valueNode = doc.createElement(XMLConstants.svalue);
        String value = mParameter.get(XMLConstants.svalue);
        XMLDoc.setTextData(valueNode, value, doc);

        paramNode.appendChild(nameNode);
        paramNode.appendChild(valueNode);

        return paramNode;
    }

    /**
     * Provides with a carbon copy of the existing parameter object.
     * @return The new copy of the parameter object.
     */
    @Override
    public Object clone() {
        try {
            Parameter copy = (Parameter) super.clone();
            //copy.mParameterName = (String)mParameterName.clone(); // wont work as clone() in Object is protected
            copy.mParameterName = mParameterName;
            // copy.mParameter = (Hashtable)mParameter.clone(); // This just creates a shallow copy of the Hashtable
            copy.mParameter = new Hashtable<String,String>();

            Enumeration<String> e = mParameter.keys();
            while (e.hasMoreElements()) {
                String key = e.nextElement();
                String value = mParameter.get(key);

                String keyCopy = key;
                String valueCopy = value;
                copy.mParameter.put(keyCopy, valueCopy);
            }
            return copy;
        } catch (CloneNotSupportedException e) {
			//System.err.println("Parameter:clone():" + "Clone Not supported!");

        }
        return null;
    }

}
