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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.apache.log4j.Logger;
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 a crop. It holds information ABOUT the parameters of a particular
 * crop as obtained from crop_defn.xml, crop_display.xml, crop_lang.xml.
 */
public class CropMeta extends ObjectMeta {

    private final static Logger logger = Logger.getLogger(CropMeta.class);

    Map<String, ParameterMeta> mParameters; // holds all parameter Metas. Node: Crops does'bnt have nay actions, only parameters
    List<String> mParameterNames; //List of parameter names
    ArrayList<String> decomp; //holds all of the specifically decomp parameters.

    Map<String, String> cropDisCatAndVal;
    Map<String, List<String>> cropCatAndParams;
    List<String> CategoryNamesVec;
    List<String> catParamsVec;

    /**
     * Constructor that defines the objects/datastructure that stores the meta
     * data for each crop object declared in the crop_defn.xml, crop_display.xml,
     * crop_lang.xml
     */
    public CropMeta() {
        mParameters = new Hashtable<String, ParameterMeta>();
        mParameterNames = new Vector<String>();
        decomp = new ArrayList<String>();
        cropDisCatAndVal = new Hashtable<String, String>();
        cropCatAndParams = new Hashtable<String, List<String>>();
        CategoryNamesVec = new Vector<String>();
        catParamsVec = new Vector<String>();
    }

    /**
     * Constructor that defines the objects/datastructure that stores the meta
     * data for each crop object with name pObjectName declared in the 
     * crop_defn.xml, crop_display.xml, crop_lang.xml
     * @param pObjectName The name of the cropObject whose meta data needs to be
     * stored here in this object
     */
    public CropMeta(String pObjectName) {
        this();
        mObjectName = pObjectName;
    }

    /**
     * Initialization of the crop meta data happens here. Even though crops are part
     * of the operations' data the Meta's are made independent for simplicity sake.
     * @param pNode The node which needs to be initialized with the data from the 
     * particular crop file containing the data.
     */
    @Override
    public void initialize(Node pNode) {
        DocumentTraversal traversable;
        Node node;

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

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

        node = walker.firstChild(); // the node is one of the action (operationdefn) or paramdefn (crop_defn) from 

        while (node != null) {
            String nodeName = node.getNodeName();
            if (mParameters == null) {
                mParameters = new Hashtable<>();
            }

            if (nodeName.equals(XMLConstants.sparamdefn)) //e.g. cropdefnxml has not actions
            {
                /* Note: Crop object are part of operation objects. Buyt just for simplicity, the Metas are made independent.
                 * The crop data objects though are part of Operation data objects
                 */
                ParameterMeta parameterMeta = new ParameterMeta();
                parameterMeta.initialize(node);
                String paramname = parameterMeta.get(XMLConstants.sparamname);
                mParameters.put(paramname, parameterMeta); // parameters are stored according to their names
                mParameterNames.add(paramname);
                Node child = node.getLastChild();
                while(child != null)
                {
                    String decompQ = child.getNodeName();
                    if(decompQ.equals(XMLConstants.sdecomp))
                    {
                        decomp.add(paramname);
                        break;
                    }
                    child = child.getPreviousSibling();
                }
            }
            if (nodeName.equals(XMLConstants.scategoryname)) //e.g. crop_display.xml have these elements
            {
                 //System.out.println("Crop_display.XML : Category Name found in crop_display.xml file");

            }

            node = walker.nextSibling();
        } // end while( node != null )
    }

    /**
     * This method allows the updation of crop meta data stored in the corresponding
     * data structure
     * @param pNode The crop node whose meta data is being updated from the crop_dlg screen
     * depending on its category or the OperationDlg screens which allows modification and
     * updation of data for analysis purpose.
     * 
     */
    @Override
    public void update(Node pNode) {
        DocumentTraversal traversable;
        Node nodeChild;

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

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

        nodeChild = walker.firstChild(); // The node is one of the  or paramlang (croplang) 
        // or categoryname (cropdisplay)

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

            if ((nodeName.equals(XMLConstants.sparamlang)) || (nodeName.equals(XMLConstants.sparamdisplay))) {
                Node langORdisplayNode = nodeChild;

                Node paramnameNode = walker.firstChild();
                String paramname = XMLDoc.getTextData(paramnameNode);

                ParameterMeta parameterMeta = mParameters.get(paramname);
                if (parameterMeta != null) {
                    parameterMeta.update(langORdisplayNode);
                } else {
                    logger.warn("Prameter " + paramname + " is missing in croplang/display");
                }

                walker.parentNode(); // back to paramlang/ paramdisplay level

            } else if (nodeName.equals(XMLConstants.scategory)) //from crop_display.xml
            {
                populateCropDisplayDataStructure(nodeChild);
                updateCropDisplay(nodeChild);

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

    /**
     * This methods populates the data structures involved in pulling the data
     * for various tabs shown in crop dialog screen from the crop file taken 
     * from the soil database.
     * @param pNode The crop node whose data is being populated.
     */
    public void populateCropDisplayDataStructure(Node pNode) {
        DocumentTraversal traversable;
        Node nodeChild;

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

        TreeWalker walker = traversable.createTreeWalker(pNode, NodeFilter.SHOW_ALL, null, false);
        nodeChild = walker.firstChild(); // The node is one of the categoryname or paramdisplay

        while (nodeChild != null) {
            String categoryName = "";
            String categoryChildName = nodeChild.getNodeName();

            if (categoryChildName.equals(XMLConstants.scategoryname)) //for cropdefn
            {
                categoryName = XMLDoc.getTextData(nodeChild);
                if (categoryName != null && !categoryName.equals("")) {
                    CategoryNamesVec.add(categoryName);
                    String catDisplayVal = getCategoryDisplayValue(walker);
                    cropDisCatAndVal.put(categoryName, catDisplayVal);
                                    //System.out.println("========populateCropDisplayDataStructure ===== Category Name : === " + categoryName + " === Category Display Value is : == " + catDisplayVal );

                    //catParamsVec = getCatParamsVec(walker);
                    catParamsVec = getCatParamsVec(nodeChild);
                    if (catParamsVec.isEmpty()) {
                        //System.out.println("=============================populateCropDisplayDataStructure VECTOR catParamsVec is EMPTY ==========================");
                    }
                    cropCatAndParams.put(categoryName, catParamsVec);
                }

//                                //System.out.println("CropMeta: updateCropDisplay(), " + "Category Name: "  + categoryName  );
                //categoryName = walker.firstChild().getNodeValue();
                //walker.parentNode(); //back to category name level
                walker.parentNode();
            }
            /*else if( categoryChildName.equals(XMLConstants.sparamdisplay) )  
             {
             Node displayNode = nodeChild;
			
             Node paramnameNode = walker.firstChild();
             String paramname = XMLDoc.getTextData(paramnameNode);
                                
             // Manmohan has added on 5th May, 2004
             Node dispAttrNode = walker.nextSibling();
             String dispAttr = XMLDoc.getTextData(dispAttrNode);
                                
             Node valAttrNode = walker.nextSibling();
             String valAttr = XMLDoc.getTextData(valAttrNode);
                                
             //System.out.println("CropMeta:updateCropDisplay(), " + "Parameter:- "  + paramname + " Display Attribute :- " +  dispAttr + " Value Attribute :- " + valAttr );
				
             ParameterMeta parameterMeta = (ParameterMeta)mParameters.get(paramname);
             if(parameterMeta != null)
             {
             parameterMeta.update(displayNode);
             parameterMeta.setDisplayCategory(categoryName);
             }
             else
             {
             System.err.println("Parameter " + paramname + " is missing in parameterMeta"+ " @CropMeta:updateCropDisplay()");
             }
             walker.parentNode();
             }*/

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

    /**
     * Getter method that gets the value of the category that will be displayed
     * when the tabs in cropDlg are populated category-wise.
     * @param w Tree of nodes that contain the data in a categorized form where the
     * category display value will be cross checked sequentially.
     * @return The display value for the category
     */
    public String getCategoryDisplayValue(TreeWalker w) {

        String display = "true";
        Node n = w.nextSibling();
        String categoryDisplayVal = "";

        do {
            categoryDisplayVal = n.getNodeName();
            // //System.out.println(" CropMeta : getCategoryDisplayValue() Ln 223 : Value of categoryDisplayVal is : "+ categoryDisplayVal );
            if (categoryDisplayVal.equals(XMLConstants.scatDisplayValue)) {
                display = n.getNodeValue();
                break;
            } else {
                display = "true";

            }

            n = w.nextSibling();

        } while (n != null);

        ////System.out.println(" CropMeta : getCategoryDisplayValue() Ln 260 : Value of DISPLAY : "+ display );
        return display;
    }

    //public Vector getCatParamsVec(TreeWalker w){
    /**
     * Getter method that returns the vector containing parameters in a category
     * of crops
     * @param n1 - The Node whose soil parameters are to be found out under a particular
     * category tag. For e:g, can be found out from crop_display.xml file.
     * @return The vector containing all the crop parameters falling under
     * the mentioned category.
     */
    public List<String> getCatParamsVec(Node n1) {
        List<String> paramList = new Vector<String>();
        //Node n = w.nextSibling();
        //String name = n1.getNodeName();
        String paramdisplay = "";

        while (n1 != null) {
            paramdisplay = n1.getNodeName(); //XMLDoc.getTextData(n1);
            ////System.out.println("================== getCatParamsVec() : paramdisplay value is: " + paramdisplay + "======================");
            if (paramdisplay.equals(XMLConstants.sparamdisplay)) {
                n1 = n1.getFirstChild();
                String nodename = XMLDoc.getTextData(n1);
                ////System.out.println("================== getCatParamsVec() : Adding Node Name to paramList " + nodename + "======================");
                paramList.add(nodename);
                n1 = n1.getParentNode();
            }
            // n = w.nextSibling();
            n1 = n1.getNextSibling();
        }
        if (paramList.isEmpty()) {
            //System.out.println("================== getCatParamsVec() : paramList Vector is EMPTY ======================");
            return null;
        } else {
            return paramList;
        }
    }

    /**
     * This is a getter method that returns the crop categories and parameters.
     * @return The hashtable containing the crop's categories and corresponding 
     * parameters associated with that category.
     */
    public Map<String, List<String>> getCropCatAndParams() {
        if (cropCatAndParams != null) {
            return Collections.unmodifiableMap(cropCatAndParams);
        } else {
            return Collections.emptyMap();
        }
    }

    /**
     * This is a getter method that returns the category names contained in each
     * category under which the parameternames are specified.
     * @return The Vector containing these category names
     */
    public List<String> getCategoryNamesVec() {

        return CategoryNamesVec;
    }

    /**
     * This method updates the crop display screen with the most recent data
     * that was fed in its various text fields or options updated in its choice
     * lists returns the category names contained in each category under which
     * the parameternames are specified.
     * @param pNode The crop node parameters whose data got modified in the crop
     * dialog screen.
     */
    public void updateCropDisplay(Node pNode) {
        DocumentTraversal traversable;
        Node nodeChild;

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

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

        nodeChild = walker.firstChild(); // The node is one of the categoryname or paramdisplay

        while (nodeChild != null) {
            String categoryName = "";
            String categoryChildName = nodeChild.getNodeName();

            logger.debug("Nodechild Name: " + categoryChildName);
            if (categoryChildName.equals(XMLConstants.scategoryname)) //for cropdefn
            {
                categoryName = XMLDoc.getTextData(nodeChild);
                logger.debug("Category Name: " + categoryName);
				//categoryName = walker.firstChild().getNodeValue();
                //walker.parentNode(); //back to category name level
            } else if (categoryChildName.equals(XMLConstants.sparamdisplay)) {
                Node displayNode = nodeChild;

                Node paramnameNode = walker.firstChild();
                String paramname = XMLDoc.getTextData(paramnameNode);

                // Manmohan has added on 5th May, 2004
                Node dispAttrNode = walker.nextSibling();
                String dispAttr = XMLDoc.getTextData(dispAttrNode);

                Node valAttrNode = walker.nextSibling();
                String valAttr = XMLDoc.getTextData(valAttrNode);

                logger.debug("Parameter:- " + paramname + " Display Attribute :- " + dispAttr + " Value Attribute :- " + valAttr);

                ParameterMeta parameterMeta = mParameters.get(paramname);
                if (parameterMeta != null) {
                    parameterMeta.update(displayNode);
                    parameterMeta.setDisplayCategory(categoryName);
                } else {
                    logger.warn("Parameter " + paramname + " is missing in parameterMeta.");
                }

                walker.parentNode();
            }

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

    /**
     * Getter method that returns the name of the object the crop is associated
     * with.
     * @return The name of the object
     */
    public String getObjectName() {
        return mObjectName;
    }

    /* This function is just for completion sake as the Meta class has this function.
     * CropMeta does'nt have any actions
     */
    /**
     * Getter method that returns the Meta objects for an action which are
     * visible in an operation_defn, operation_display, operation_lang, XML
     * files.
     * @param pIdentity Identity of the action object whose meta data is being
     * requested. The identity object is classified by CODE and ID
     * part.
     *
     * @return Returns the ActionMeta object that contains the meta data such
     * as parameter names, types, units that data is in etc. Also the
     * actionname could be retrieved which is again a part of that
     * meta data.
     */
    @Override
    public ActionMeta getActionMeta(Identity pIdentity) {
        return null;
    }

    /**
     * This is a getter method that fetches the vector containing the parameter
     * names for the crop.
     * @return The vector containing the parameter names.
     */
    public List<String> getParameterNames() {
        return mParameterNames;
    }
    
    /**
     * This is a getter method that fetches the vector containing only the names
     * of the decomposition parameters.
     * @return 
     */
    public List<String> getDecompNames()
    {
        return decomp;
    }

    /**
     * This method fetches the ParameterMeta objects associated with a crop
     * having a parameter with name pParamName
     * @param pParamName The name of the parameter whose meta data is being requested.
     *
     * @return The object that consists the meta data for that parameter.
     */
    public ParameterMeta getParameterMeta(String pParamName) {
        ParameterMeta paramMeta = mParameters.get(pParamName);
        return paramMeta;
    }

    /**
     * This method fetches the values held by parameter meta object in a crop 
     * meta object having parameter name pParamName and value name as pValueName
     * @param pParamName Name of the parameter whose value value is sought
     * @param pValueName The name of the value parameter whose data is being 
     * requested
     * @return The value part of the value parameter.
     */
    @Override
    public String getValue(String pParamName, String pValueName) {
        if (pParamName.equals(XMLConstants.sobjectname)) {
            return mObjectName;
        }

        String value = null;

        ParameterMeta paramMeta = mParameters.get(pParamName);
        if (paramMeta != null) {
            value = paramMeta.get(pValueName);
        } else {
            logger.info("There is no parameter meta for parameter " + pParamName);
        }

        return value;
    }

}
