/* Title: ParameterMeta.java
 * Version 1.1
 * Author		Manmohan, Sada
 * Company: USDA-ARS
 * Date:    June, 2005
 * 
 */
package usda.weru.mcrew;

import java.text.NumberFormat;
import java.util.Hashtable;
import org.w3c.dom.NamedNodeMap;
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 represents the Mcrew parameter. Holds information about a parameter 
 * such as name, type, prompt, display attribute and value attribute.
 * Edit history
 * @author Manmohan, Sada, Sudhir Kaul  
 * @date June 2005
*/

public class ParameterMeta
{
	static NumberFormat numberFormat = NumberFormat.getInstance();
	
//	private String name;
//	private String prompt;
//	private String type;
//	private String unit;
//	private String displayAttribute;
//	private String valueAttribute;
	
	Hashtable mParameter;
        Hashtable <String, Hashtable > mGroup;
    /**
     * The nuimber of digits permitted after the decimal point for a fractional number.
     */    
	public static final int kMaximumFractionDigits = 10;
	
	static 
	{
		numberFormat.setGroupingUsed(false);
		numberFormat.setParseIntegerOnly(true);
	}
		
    /**
     * Default constructor for creating a new object.
     */    
	public ParameterMeta()
	{
		mParameter = new Hashtable();
                mGroup = new Hashtable <String, Hashtable> ();
	}
	
	// unused constructor 
    /**
     * Six argument constructor that takes in all the meta data needed for creating 
     * a parameter meta object that gets associated with a parameter object.
     * @param pname name of the parameter whose meta data is being updated
     * @param pprompt The promp information visible in various operation & crop tables.
     * @param ptype The datatype of the parameters i:e the type of data it will hold
     * @param punit The units in which this data will exist i:e Metric units or English units
     * @param pDisplayAtt Tells us if the parameters are visible, editable or hidden
     * @param pValueAtt Tells us if the attribute is numeric entry, choice list, string entry or multi-line text.
     */    
	public ParameterMeta(String pname, String pprompt, String ptype,  String punit, String pDisplayAtt, String pValueAtt ) 
	{
//		this.name = pname;
//		this.prompt = pprompt;
//		this.type= ptype;
//		this.unit = punit;
//		this.displayAttribute = pDisplayAtt;
//		this.valueAttribute = pValueAtt;
	}
	
    /**
     * Initializes the meta object with the node data that is being passed as an argument.
     * @param pNode The node object whose data is used for initialization purpose.
     */    
	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();
		while(nodeChild != null)
		{
			String nodeChildName = nodeChild.getNodeName();
                       	String nodeChildData = "";
                        
			if(nodeChildName.equals(XMLConstants.scomment))
			{	nodeChild = walker.nextSibling();
				continue; // Theres no need to put comments into the data structure
			}

					//Node textNode = walker.firstChild();
                        
                        nodeChildData = XMLDoc.getTextData(nodeChild);
                        
			if(nodeChildData == null){
//                            //System.out.println( " ParameterMeta : initialize() : nodeChildName With NULL-DATA inserted (PARENT) : " + nodeChild.getParentNode().getNodeName() + " & Current Node is : " + nodeChild.getNodeName() );
                            nodeChildData = "";
                        }
				//if(textNode != null) //Some nodes have no data eg some paramtype in crop_defn
				//	nodeChildData = textNode.getNodeValue();
//                        //System.out.println( " ParameterMeta : initialize() : nodeChildName inserted  : " + nodeChildName + " & nodeChildData inserted  : " + nodeChildData );
			mParameter.put(nodeChildName, nodeChildData);
                        //System.out.println(nodeChildName);
                        

			nodeChild = walker.nextSibling();
		}
	}
	
    /**
     * This method updates the meta data of the parameteMeta object's node that is 
     * being passed as an argument. 
     * @param pNode The node object whose data is being updated.
     */    
	public void update(Node pNode)
	{
		Hashtable paramChoiceTable = null; // to store paramchoice strings if any exists
		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(); // can be paramname, paramprompt, paramunit, displayattribute and valueattribute
		while(nodeChild != null)
		{
			String nodeChildName = nodeChild.getNodeName();

                        
			if( (nodeChildName.equals(XMLConstants.sparamname)) || (nodeChildName.equals(XMLConstants.scomment)) )
			{ // Theres no need to put comment data into data structure. param name is already there
				nodeChild = walker.nextSibling();
				continue;
			}
			else if(nodeChildName.equals(XMLConstants.sconversion))
			{
				String addendValue = "0"; 
				String factorValue = "1.0"; //either one or both vcan be present
				
				Node grandChild1 = walker.firstChild(); //Can be factor or addend i.e either one or both can be present in the file
                                Node grandChild2 = walker.nextSibling();
				if(grandChild1 != null)
				{
					if(grandChild1.getNodeName().equals(XMLConstants.sfactor)){
						factorValue = XMLDoc.getTextData(grandChild1); // walker.firstChild().getNodeValue();
//                                                //System.out.println( " ParameterMeta : Update() : Conversion : Factor Name is  : " + grandChild1.getNodeName() + " & Factor-VALUE is  : " + factorValue );
                                        }
					else{
						addendValue = XMLDoc.getTextData(grandChild1); // walker.firstChild().getNodeValue();
//                                              //System.out.println( " ParameterMeta : Update() : Conversion : Addend Name is  : " + grandChild1.getNodeName() + " & Addend-VALUE is  : " + addendValue );
                                        }
						//walker.parentNode();
                                }	
				if(grandChild2 != null){
					{
						if(grandChild2.getNodeName().equals(XMLConstants.saddend))
							addendValue = XMLDoc.getTextData(grandChild2); // walker.firstChild().getNodeValue();
						else
							factorValue = XMLDoc.getTextData(grandChild2); // walker.firstChild().getNodeValue();
							
						 //walker.parentNode();
					}
				}
                                ////System.out.println( " ParameterMeta : Update() : Conversion : Factor Name before INSERTION is  : " + XMLConstants.sfactor + " & Factor-VALUE is before INSERTION : " + factorValue );
				mParameter.put(XMLConstants.sfactor, factorValue);
                                ////System.out.println( " ParameterMeta : Update() : Conversion : saddend Name before INSERTION is  : " + XMLConstants.saddend + " & addend-VALUE is before INSERTION : " + addendValue );
				mParameter.put(XMLConstants.saddend, addendValue);
				
				/* Node myCurrentNode = walker.getCurrentNode();
                               // //System.out.println( " ParameterMeta : Update() : Conversion : NODE-NAME is  : " + myCurrentNode.getNodeName() + " & NODE-VALUE is  : " + myCurrentNode.getNodeValue() );
                                
                                 Node myParentNode = walker.parentNode()
                                
                                Node myGrandParentNode = walker.parentNode();
                                //System.out.println( " ParameterMeta : Update() : Conversion : PARENT Node-Name is  : " + myParentNode.getNodeName() + " & PARENT Node-Value is  : " + myParentNode.getNodeValue() );
                                //System.out.println( " ParameterMeta : Update() : Conversion : GrandPARENT Node-Name is  : " + walker.getCurrentNode().getNodeName() + " & GrandPARENT Node-Value is  : " + walker.getCurrentNode().getNodeValue() );
                                */
                                walker.parentNode().getParentNode();
                                
			}
			else if(nodeChildName.equals(XMLConstants.sparamchoice))
			{
				String nodeChildData = XMLDoc.getTextData(nodeChild);
				NamedNodeMap attributeMap = nodeChild.getAttributes();
				Node valueAttributeNode = null;
				
				if(attributeMap != null)
					valueAttributeNode = attributeMap.getNamedItem(XMLConstants.svalue);
								
				if(valueAttributeNode == null)
				{
					//System.err.println("  ==== ParameterMeta === : == update(): == " + "Theres are no values with the the paramchoice "+ nodeChildData + " Discarding paramchoice");
					nodeChild = walker.nextSibling();
					continue;
				}
				
				String choiceValue = XMLDoc.getTextData(valueAttributeNode);   // VERY IMPORTANT
                //System.out.println("====ParameterMeta ==== : == update(): === The CHOICE values with : " + choiceValue + " IS : "  + nodeChildData );
				
				if(paramChoiceTable == null) // VERY IMPORTANT
				{  /* Every paramchoice has a integer value associated with it. The values are in no order, for eg 
					  * one choice can have a value of 0 and other 14 for some parameter. A hash table is used to store 
					  * each paramchoice string with the paramchoice value as the key.
					  */
					paramChoiceTable = new Hashtable();  // VERY IMPORTANT
					mParameter.put(XMLConstants.sparamchoice, paramChoiceTable);  // VERY IMPORTANT
                    //System.out.println("====ParameterMeta ==== : == update(): === paramChoice is " + XMLConstants.sparamchoice );
				} 
				
				paramChoiceTable.put(choiceValue, nodeChildData); // VERY IMPORTANT
			}
			else
			{       ////System.out.println("ParameterMeta : update(): NODECHILD INFO : nodeChildName : " + nodeChildName + " & nodeChildData : " + nodeChildData ); 
				String nodeChildData = XMLDoc.getTextData(nodeChild); // walker.firstChild().getNodeValue();
				if(nodeChildData != null){ // if in case value null
					mParameter.put(nodeChildName, nodeChildData);
                                        if(nodeChildName.equals(XMLConstants.sdisplayattribute) || nodeChildName.equals(XMLConstants.svalueattribute) || nodeChildName.equals(XMLConstants.sformat)){
                                            Node nodeParent = findParentNode(XMLConstants.scategory, nodeChild);
                                            if (nodeParent != null){
                                                Node nodeCategoryName = nodeParent.getFirstChild();
                                                String groupName = XMLDoc.getTextData(nodeCategoryName);
                                                setGroupAttribute(groupName, nodeChildName, nodeChildData);
                                            }
                                            
                                        }
                                }
			}
			
			nodeChild = walker.nextSibling();
		} // end while(nodeChild != null)
	}
        
        private Node findParentNode(String parentName, Node node){
            if (node == null) return null;
            String nodeName = node.getNodeName();
            if (nodeName.equals(parentName)){
                return node;
            }
            else{
               return findParentNode(parentName, node.getParentNode());
            }                       
        }
        
        
	
    /**
     * Display category is unused for any paramters except for crops. This method 
     * sets the name of each category that will be used in a crop classifying parameters
     * that go under them. 
     * @param pCategoryName The name to be set for the category.
     */    
	public void setDisplayCategory(String pCategoryName)
	{
		mParameter.put(XMLConstants.sdisplaycategory, pCategoryName); // 
		////System.out.println("ParameterMeta:" + "display category set to "+ pCategoryName);
	}

    /**
     * Fetches the data value associated with the parameter name pName being passed
     * as argument to the method. 
     * @param pName The name of the parameter whose value is being seeked.
     * @return The meta data for the parameter with name pName
     */    
	public String get(String pName)
	{
		return (String)mParameter.get(pName);
	}
	
	/* All the below function values can also be retrieved by the above get() function by passing the 
	 * appropriate parameter
	 */
	 
	 
    /**
     * 45
     * @return are
     */    
	public String getName() {
		return (String)mParameter.get(XMLConstants.sname);
	}

    /**
     * wt
     * @return fw
     */    
	public String getPrompt() {
		return (String)mParameter.get(XMLConstants.sparamprompt);
	}

    /**
     * wer
     * @return ert
     */    
	public String getType() {
		return (String)mParameter.get(XMLConstants.sparamtype);
	}

    /**
     * ert
     * @param pUnitId at
     * @return asdf
     */    
	public String getUnit(int pUnitId) 
	{
		if(pUnitId == XMLConstants.kPrimaryUnit)
			return (String)mParameter.get(XMLConstants.sparamunit);
		else
			return (String)mParameter.get(XMLConstants.sparamaltunit);
	}
	
    /**
     * Returns primary unit of the parameter
     * @return The unit in which the parameter exists.
     */    
	public String getUnit() 
	{
		return (String)mParameter.get(XMLConstants.sparamunit);
	}
	
	/*
	public String getAlternateUnit() 
	{
		return (String)mParameter.get(XMLConstants.sparamaltunit);
	}
	*/
	
    /**
     * This methid is replace with the generalized method below
     * @return The nodename's data value.
     */    
	public String getDispAtt() {
		//return (String)mParameter.get(XMLConstants.sdisplayattribute);
                String x = (String)mParameter.get(XMLConstants.sdisplayattribute);
                return x;
	}

	/* 
	 */
    /**
     * This method gets the values of attributes like display or value which are the node names whose 
     * actual values are being requested.
     * @param pAttributeName This argument can be either XMLConstants.sdisplayAtribute or svalueAttribute
     * @return The value associated with that attribute i:e display or value.
     */    
	public String getAttribute(String pAttributeName)
	{
		return (String)mParameter.get(pAttributeName);
	}
        
    /**
     * Used for the tabs in the crop dialog
     */    
	public String getGroupAttribute(String pGroupName, String pAttributeName)
	{
            Hashtable groupHashTable = mGroup.get(pGroupName);
            //Was there a group hashtable?  return null
            if(groupHashTable == null){
                return (String)mParameter.get(pAttributeName);
            }
            return (String) groupHashTable.get(pAttributeName);
	}      
        public void setGroupAttribute(String pGroupName, String pAttributeName, String pAttributeValue)
	{
            //First we need the group's hashtable;
            Hashtable groupHashTable = mGroup.get(pGroupName);
            //Was there a group hashtable?  If not create one.
            if(groupHashTable == null){
                groupHashTable = new Hashtable();
                mGroup.put(pGroupName, groupHashTable);
            }
            
            groupHashTable.put(pAttributeName, pAttributeValue);

	}    
	
    /**
     * The choices that exists in the choice list of each parameter for a crop or 
     * operation object.
     * @return The hashtable that stores all the vectors containing each choice list 
     * options or details.
     */    
	public Hashtable getChoices()
	{	
		return (Hashtable)mParameter.get(XMLConstants.sparamchoice);
	}
	
	/*
	public double getAlternateValue(double pPrimaryValue)
	{
		double factor = 1.0, addend = 0.0;
		
		if(mParameter != null)
		{
			factor = ((Double)(mParameter.get(XMLConstants.sfactor))).doubleValue();
			addend = ((Double)(mParameter.get(XMLConstants.saddend))).doubleValue();
		}
		
		double returnValue = pPrimaryValue * factor + addend;
		
		return returnValue;
	}
	*/
	
    /**
     * Method converts the values of each parameter's data from its primary unit
     * to the alternate or secondary units. Like from English units to Metric units.
     * @param pPrimaryValue The value of a parameter in primary units.
     * @return The value of a parameter after conversion to alternate or secondary units.
     */    
	public String convertToAlternateUnits(String pPrimaryValue)
	{
		double factor = 1.0, addend = 0.0;

		////System.out.println("ParameterMeta:converToAlternateUnits, " + "Value = " + pPrimaryValue);		
		String type = getType();
		if( (type != null) && (type.equals(XMLConstants.sstring)) )
			return pPrimaryValue;
		
		try{
			if(mParameter != null)
			{
				Object factorObj = mParameter.get(XMLConstants.sfactor);
				Object addendObj =  mParameter.get(XMLConstants.saddend);
			
				if(factorObj != null)
					factor =  (new Double(factorObj.toString() )).doubleValue();
			
				if(addendObj != null)
					addend = (new Double(addendObj.toString() )).doubleValue();
			}
			
			double toConvert  = (new Double(pPrimaryValue)).doubleValue();
		
			Double result = new Double( toConvert * factor + addend);
		
			Object returnValue = result;
					
			return convertToProperType(returnValue.toString());
			
		}catch(NumberFormatException e){
			//System.err.println("ParameterMeta:converToAlternatUnits: " + "NumberFormatException on input value " + pPrimaryValue);
		}
		
		return pPrimaryValue; // return old value if something goes wrong
	}
	
	
    /**
     * Method converts the values of each parameter's data from its alternate  unit
     * to the primary units. Like from English units to Metric units.
     * @return The value of a parameter after conversion to primary units.
     * @param pAlternateValue The value of a parameter in alternate units.
     */    

	public String convertToPrimaryUnits(String pAlternateValue)
	{
		double factor = 1.0, addend = 0.0;
		
		String type = getType();
		if( (type != null) && (type.equals(XMLConstants.sstring)) )
			return pAlternateValue;
		
		try{
			if(mParameter != null)
			{
				Object factorObj = mParameter.get(XMLConstants.sfactor);
				Object addendObj =  mParameter.get(XMLConstants.saddend);
			
				if(factorObj != null)
					factor =  (new Double(factorObj.toString() )).doubleValue();
			
				if(addendObj != null)
					addend = (new Double(addendObj.toString() )).doubleValue();
	
			}
		
			double toConvert = (new Double(pAlternateValue)).doubleValue();
		
			Double result = new Double( (toConvert - addend) / factor) ;
		
			Object returnValue = result;
		
			return convertToProperType(returnValue.toString());
		
		}catch(NumberFormatException e){
			//System.err.println("ParameterMeta:convertoToprimaryUnits: " + "NumberFormatException on input value " + pAlternateValue);
		}
		
		return pAlternateValue;
	}
	
    /**
     * This functions converts the value to Integer.toString() or Double.toString()
	 * so as to put the correct value into the parameter. Writing a interger as 
     * double would crash WEPS
     * @param pParamValue The param value to be converted to the proper type
     * @return The converted value represented in string format.
     */    
	public String convertToProperType(String pParamValue)
	{
		try{
			
			NumberFormat format = NumberFormat.getInstance();
			
			String type = getType();
			if(type == null)
				return pParamValue;
			
			if(type.equals(XMLConstants.sstring))
				return pParamValue;
			else if(type.equals(XMLConstants.sint)) 
			{
				format.setParseIntegerOnly(true);
			}
			else
			{	
				format.setParseIntegerOnly(false);
				format.setMaximumFractionDigits(kMaximumFractionDigits);
				////System.out.println("parameterMeta: convertoProperType()" + "Setting max fraction digits to 6 " + pParamValue);
			}	
			
			Number number = format.parse(pParamValue);
			
			return number.toString();
			
			/*			
			Double value = new Double(pParamValue);
			Object returnValue = value;
		
			String type = getType();
			if( (type != null) && (type.equals(XMLConstants.sint)) )
				returnValue = new Integer(result.intValue());
			
			return returnValue.toString();
			*/
			
		}catch(NumberFormatException e){
			//System.err.println("ParameterMeta:convertoToProperType: " + "NumberFormatException on input value " + pParamValue);
		}catch(java.text.ParseException e){
			//System.err.println("ParameterMeta:convertoToProperType: " + "ParseException on input value " + pParamValue);
		}catch(NullPointerException e){
			//System.err.println("ParameterMeta:convertoToProperType: " + "NullPointerException");
		}
		
		return pParamValue;			
	}
	/*
	public String getConversionAddend()
	{
		return (String)mParameter.get(XMLConstants.saddend);
	}
	
	public String getConversionFactor()
	{
		return (String)mParameter.get(XMLConstants.sfactor);
	}
	*/
}
