/* Title:		ActionMeta
 * Version	
 * Author		Sada, Manmohan
 * Company: USDA-ARS
 * Date:    July, 2003
 */
package usda.weru.mcrew;

import java.util.Hashtable;
import java.util.Vector;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.TreeWalker;

/**
 * This class defines an Action. It holds information ABOUT the paremetres
 * of a particular action as obtained from operation_defn.xml,
 * operation_display.xml, operation_lang.xml.
 */
public class ActionMeta {

    private final Hashtable<String, ParameterMeta> mParameters; //a hash table of parameters for this operation - holds ParameterMetas
    String mActionName;
    Vector<String> mParameterNames; //List of parameter names for any quick access
		/* mParameterNames is kept as a Vector as the vector maintains the order or elements
     * This helps when reading data from older weps file which have only parameter values and no names
     * So, the order of paramter (as obtained from the operation_Defn or crop_defn file)
     * is important as the values are stored in that order.
     */
    Identity mActionId;
    Vector<Identity> mGroupId; //Holds a list of identites representing what group(s) a process belongs to.

    /**
     * Constructor that instantiates the data structure that holds the data
     * from these XML file associated with a particular action
     */
    public ActionMeta() {
        mActionId = new Identity();
        mParameters = new Hashtable<String, ParameterMeta>();
        mParameterNames = new Vector<String>();
    }

    /**
     * Constructor that instantiates the data structure that holds the data
     * from these XML file associated with a particular action
     * @param pActionId The identity of the action object whose data is being
     * populated.
     */
    public ActionMeta(Identity pActionId) {
        mActionId = pActionId;
        mParameters = new Hashtable<String, ParameterMeta>();
        mParameterNames = new Vector<String>();
    }

    /**
     * Constructor that instantiates the data structure that holds the data
     * from these XML file associated with a particular action
     * @param pActionId The identity of the action object whose data is being
     * populated.
     * @param pParameterMeta The Meta data of the parameter for the action object whose
     * meta data is also the parameter being passed.
     */
    public ActionMeta(Identity pActionId, ParameterMeta pParameterMeta) {
        mActionId = pActionId;
        mParameters = new Hashtable<String, ParameterMeta>();
        mParameters.put(pParameterMeta.getName(), pParameterMeta);
        mParameterNames = new Vector<String>();
        mParameterNames.add(pParameterMeta.getName());
    }

    /**
     * Initializes the action nodes with the meta data to the respective
     * sections categorized on the node child name.
     * @param pNode The node that needs to be initialized.
     */
    public void initialize(Node pNode) {
        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(); // nodeChild is 'identity' or 'actioname' or 'paramdefn'

        while (nodeChild != null) {
            String nodeChildName = nodeChild.getNodeName();

            if (nodeChildName.equals(XMLConstants.sidentity)) {
                Node code = walker.firstChild();
                String codeValue = XMLDoc.getTextData(code);
                //String codeValue = walker.firstChild().getNodeValue();
                //walker.parentNode(); // move the walker to the level of code / id. 

                Node id = walker.nextSibling();
                String idValue = XMLDoc.getTextData(id);
                //String idValue = walker.firstChild().getNodeValue();
                //walker.parentNode(); //get to the level of id /code
                Identity identity = new Identity(idValue, codeValue);

                mActionId = identity;

                walker.parentNode(); // get to the level of identity

            } else if (nodeChildName.equals(XMLConstants.sactionname)) {
                String actionname = walker.firstChild().getNodeValue();
                mActionName = actionname;

                walker.parentNode(); // get ot the level of actionname                                
            } else if (nodeChildName.equals(XMLConstants.sparamdefn)) {
                ParameterMeta parameterMeta = new ParameterMeta();

                Node paramdefn = nodeChild;
                parameterMeta.initialize(paramdefn);
                String paramname = parameterMeta.get(XMLConstants.sparamname);
                mParameters.put(paramname, parameterMeta); // parameters are stored according to their names
                mParameterNames.add(paramname);

                //walker is already at the level of paramdefn
            } else if (nodeChildName.equals(XMLConstants.sgroupid)) // this node exists for only processes i.e identity.Code = P
            {
                Vector<Identity> groupId = new Vector<Identity>(); //to store all the identities of group(s) to which a a process can belong				                                
                Node identityNode = walker.firstChild();
                while (identityNode != null) {
                    Identity identity = new Identity();
                    identity.initialize(identityNode);
                    groupId.add(identity);
                    identityNode = walker.nextSibling();
                }

                mGroupId = groupId;
                walker.parentNode();

            }

            nodeChild = walker.nextSibling();
        } // end while

    }

    /**
     * Updates the action nodes with the meta data with the respective
     * sections categorized on the node child name. Any new data written
     * is updated by this method.
     * @param pNode The node whose data needs to be updated.
     */
    public void update(Node pNode) {
        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(); // nodeChild is 'identity' or 'actioname' or 'paramdefn' or 'categoryname'

        while (nodeChild != null) {
            String nodeChildName = nodeChild.getNodeName();
            if ((nodeChildName.equals(XMLConstants.sparamlang)) || (nodeChildName.equals(XMLConstants.sparamdisplay))) {
                Node langORdisplayNode = nodeChild;

                Node paramnameNode = walker.firstChild();
                String paramname = XMLDoc.getTextData(paramnameNode);
                ////System.out.println("ActionMeta : update() : Parameter is : "+ paramname );
                //String paramname = walker.firstChild().getNodeValue();
                //walker.parentNode(); //back to paramname level
                ParameterMeta parameterMeta = mParameters.get(paramname);
                /*Enumeration e1 =  mParameters.keys();
                 while(e1.hasMoreElements()){
                 String pname = (String)e1.nextElement();
                 //System.out.println("ActionMeta : update() : Parameter-Name is : " + pname);
                 }
                 JOptionPane.showMessageDialog(new JPanel(), "Parameter Names", "Set of Parameter Names", JOptionPane.WARNING_MESSAGE);
                 */ if (parameterMeta != null) {
                    parameterMeta.update(langORdisplayNode);
                } else {
                    //System.out.println("Some parameter is missing in "+ paramname + " @ActionMeta:update()");
                }
                nodeChild = walker.parentNode(); // back to paramlang/ paramdisplay level
            }

            nodeChild = walker.nextSibling();
        } //end while
    }

    /**
     * This method adds the meta data to the parameters that compose the action
     * objects metadata.
     * @param pMeta The ParameterMeta object that that is to be added to the actionmeta.
     */
    public void addParameterMeta(ParameterMeta pMeta) {
        String paramname = pMeta.get(XMLConstants.sparamname);
        mParameters.put(paramname, pMeta); // parameters are stored according to their names
        mParameterNames.add(paramname);

    }

    /**
     * Getter method to access the identity of the action object whose metadata
     * is being modified or populated.
     * @return Returns the identity object associated with the corresponding
     * action object.
     */
    public Identity getIdentity() {
        return mActionId;
    }

    /**
     * Getter method to access the parameter names associated with that action
     * object whose metadata consists of parameter objects.
     * @return Vector containing the parameter names from that action object.
     */
    public Vector<String> getParameterNames() {
        return mParameterNames;
    }

    /**
     * Getter method that tells what all groups this action object can be used
     * with when pulling the required set of data from crop objects.
     * @return Returns the vector containing Group IDs to which this action
     * can be linked.
     */
    public Vector<Identity> getGroupId() {
        return mGroupId;
    }

    /**
     * Getter method that fetches the ParameterMeta object associated with the
     * parameter name being passed as an argument to the method. This Parameter
     * meta object is a part of the action object's MetaData.
     * @param pParamName Neme of the patameter whose metadata is being requested.
     * @return The ParameterMeta object associated with paramName that is
     * being passed as an argument.
     */
    public ParameterMeta getParameterMeta(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        return paramMeta;
    }

    /**
     * Returns the String value associated with the pvalueName in the
     * ParameterMeta object that corresponds to the String argument paramName
     * passed.
     * @param pParamName Used to get the Meta object associated with this paramName
     * @param pValueName Used for the retrieval of value associated with this
     * valueName from the ParameterMeta object
     * @return Returns the String value associated with the pvalueName in the
     * ParameterMeta object
     */
    public String get(String pParamName, String pValueName) {
        if (mParameters != null) {
            ParameterMeta parameterMeta = mParameters.get(pParamName);
            if (parameterMeta != null) {
                return parameterMeta.get(pValueName);
            }
        }

        return null;
    }

    /**
     * Fetches the datatype of the parameter with pParamName passed as an
     * argument. They are also mentioned in the operation/crop defn XML
     * files.a
     * @param pParamName Name of the parameter whose type is to be found out.
     * @return Returns the datatype of the paramter(pParamName)
     */
    public String getParamType(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getType();
        }

        return null;
    }

    /* Returns the primary unit or alternate unit based upon pUnitId
     * @param pUnitId This can be either XMLConstants.kprimaryunit or XMLConstants.kAlternateunit
     */
    /**
     * Getter method that tells us the Units in which the parameters are being
     * calculated. They are also mentioned in the operation/crop defn XML
     * files.a
     * @param pParamName Name of the parameter whose units are to be found out.
     * @param pUnitId The identity of the unit
     * @return The unit of that parameter from parameterMeta.
     */
    public String getParamUnit(String pParamName, int pUnitId) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getUnit(pUnitId);
        }

        return null;
    }

    /**
     * Getter method that tells us the prompts(description about parameter names)
     * in which the parameters are being calculated. They are also mentioned in
     * the operation/crop 'lang' XML files.
     * @param pParamName Name od the parameter whose prompt is to be retrieved
     * @return The value of the prompt for that parameter.
     */
    public String getParamPrompt(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getPrompt();
        }

        return null;
    }

    /* This method is replaced with the generalized method below
     */
    /**
     * Gets the attribute(hidden/viewable/editable) that tells whether to
     * display the parameter in the operation/crop dialog screen or in the
     * main MCREW screen.
     * @param pParamName The Name of the parameter whose display attribute is requested
     * @return Returns the attribute value(hidden/editable/viewable)
     */
    public String getParamDisplayAttribute(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getDispAtt();
        }

        return null;
    }

    /**
     * Method that gets the attributes associated with the parameter. Refer to
     * the valueattribute tag from the crop/operation display XML files.
     * @param pParamName The Name of the parameter whose attribute value is requested
     * @param pAttributeName The Name of the attribute whose value is requested
     * @return The attribute value of the parameter with pParamName &
     * pAttributeName
     */
    public String getParamAttribute(String pParamName, String pAttributeName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getAttribute(pAttributeName);
        }

        return null;
    }

    /**
     * Gets the Hashtable that stores the parameter choice descriptions of the
     * choice list options provided in a table cell of MCREW/Operation_Dlg
     * /Crop_Dlg. These choices can be viewed from the 'lang' XML
     * files(operation/crop)
     * @param pParamName The parameter name whose choice list is being requested.
     * @return The hashtable consisting of the choice list associated with
     * pParamName
     */
    public Hashtable<String,String> getParamChoices(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            return paramMeta.getChoices();
        }

        return null;
    }

    /**
     * Method that converts the parameter Meta data from the primary unit to an
     * alternate units.(English to Metric units or vice-versa)
     * @param pParamName The Name of the parameter whose metadata needs to be converted
     * to the alternate unit.
     * @param pPrimaryValue The primary unit value (metadata itself) of the parameter
     * which will be converted to the alternate units.
     * @return The converted alternate unit value in a string format.
     */
    public String convertToAlternateUnits(String pParamName, String pPrimaryValue) {
        ParameterMeta paramMeta = mParameters.get(pParamName);

        String returnValue = paramMeta.convertToAlternateUnits(pPrimaryValue);

        return returnValue;
    }

    /**
     * Method that converts the parameter Meta data from the alternate unit to
     * the primary units.(English to Metric units or vice-versa whichever is set
     * as the alternate unit)
     * @param pParamName The Name of the parameter whose metadata needs to be converted
     * to the primary unit from alternate unit.
     * @param pPrimaryValue The alternate unit value (metadata itself) of the parameter
     * which will be converted to the primary unit.
     * @return The converted primary unit value in a string format.
     */
    public String convertToPrimaryUnits(String pParamName, String pPrimaryValue) {
        ParameterMeta paramMeta = mParameters.get(pParamName);

        String returnValue = paramMeta.convertToPrimaryUnits(pPrimaryValue);

        return returnValue;
    }

    /**
     * Method that converts the parameter Meta data from its existing datatype
     * to the mentioned object type in the operation/crop display XML file.
     * Look at the tag valueattribute
     * @param pParamName The Name of the parameter whose metadata needs to be converted
     * to the type mentioned in the crop/operation display XML files.
     * @param pValue The value of the parameter metadata that needs to be converted
     * to the type mentioned in the crop/operation display XML files.
     * @return The converted primary unit value in a string format.
     */
    public String convertToProperType(String pParamName, String pValue) {
        ParameterMeta paramMeta = mParameters.get(pParamName);

        String returnValue = paramMeta.convertToProperType(pValue);

        return returnValue;
    }

    /**
     * Gets the name of the action object whose meta data is being modified or
     * accessed.
     * @return The action object's name
     */
    public String getActionName() {
        //System.out.println("ActionMeta : getActionName : Action Name is :" + mActionName);
        return mActionName;
    }
}
