/**
 *    Copyright 2011 Peter Murray-Rust et. al.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.xmlcml.cml.element;

import java.util.List;

import nu.xom.Element;
import nu.xom.Nodes;

import org.xmlcml.cml.attribute.DictRefAttribute;
import org.xmlcml.cml.base.CMLConstants;
import org.xmlcml.cml.base.CMLElement;
import org.xmlcml.cml.base.CMLElements;
import org.xmlcml.cml.interfacex.HasDataType;
import org.xmlcml.cml.interfacex.HasDictRef;

/**
 * user-modifiable class supporting parameter. * autogenerated from schema use
 * as a shell which can be edited
 *
 */
public class CMLParameter extends AbstractParameter implements HasDictRef {

	/** namespaced element name.*/
	public final static String NS = C_E+TAG;

	private HasDataType child;
	
    /**
     * constructor.
     */
    public CMLParameter() {
    }

    /**
     * constructor.
     *
     * @param old
     */
    public CMLParameter(CMLParameter old) {
        super((AbstractParameter) old);

    }

    /**
     * copy node .
     *
     * @return Node
     */
    public Element copy() {
        return new CMLParameter(this);

    }

    /**
     * create new instance in context of parent, overridable by subclasses.
     *
     * @param parent
     *            parent of element to be constructed (ignored by default)
     * @return CMLParameter
     */
    public CMLElement makeElementInContext(Element parent) {
        return new CMLParameter();

    }
    
        /** null
    	    * @return String
    	    */
    public String getDictRef() {
        DictRefAttribute att = (DictRefAttribute) this.getDictRefAttribute();
        if (att == null) {
            return null;
        }
        return att.getString();
    }
    
    /** gets descendant parameter elements.
     * may either thave the dictRef on the parameter element or on 
     * its child.
     * properties are normalized
     * @param parent
     * @param dictRef
     * @return parameterList containing references (normally 1 or 0 entries)
     */ 
    public static CMLParameterList getParameterList(CMLElement parent, String dictRef) {
    	CMLParameterList parameterList = new CMLParameterList();
    	Nodes nodes = parent.query("./cml:parameter", CMLConstants.CML_XPATH);
    	for (int i = 0; i < nodes.size(); i++ ) {
    		CMLParameter parameter = (CMLParameter) nodes.get(i);
    		parameter.canonicalize();
    		if (dictRef.equals(parameter.getAttributeValue("dictRef"))) {
	    		parameterList.addParameter(parameter);
    		}
    	}
    	return parameterList;
    }

    /** gets single parameter.
     * if zero or many properties with gievn dictRef returns null
     * @param parent
     * @param dictRef
     * @return parameter or null
     */
    public static CMLParameter getParameter(CMLElement parent, String dictRef) {
    	CMLParameterList parameterList = CMLParameter.getParameterList(parent, dictRef);
    	CMLElements<CMLParameter> properties = parameterList.getParameterElements();
    	CMLParameter parameter = null;
    	if (properties.size() == 1) {
    		parameter = properties.get(0);
    	}
    	return parameter;
    }

    // ------------------------ analogous to CMLProperty -------------------
    /** makes sure property has the structure:
     * {@code <property @title @dictRef><scalar @dataType @units>...}
     * if zero or many children (scalar, array, matrix) no-op
     *
     */
    public void canonicalize() {
    	CMLProperty.staticCanonicalize(this);
    }
    
	/**
	 * @return units on child
	 */
	public String getUnits() {
		return CMLProperty.getStaticUnits(this);
	}
	
	/**
	 * gets real value of scalar child
	 * 
	 * @return the value (NaN if not set)
	 */
	public double getDouble() {
		return CMLProperty.getStaticDouble(this);
	}

	/**
	 * gets String value. dataType must be XSD_STRING.
	 * 
	 * @return the value (null if not set)
	 */
	public String getString() {
		return CMLProperty.getStaticString(this);
	}
	

	/**
	 * gets int value. dataType must be XSD_INTEGER.
	 * 
	 * @return the value
	 * @throws RuntimeException
	 *             if different type
	 */
	public int getInt() {
		return CMLProperty.getStaticInt(this);
	}

    /** get array elements.
     * recalculates each time so best cached for frequent use
     * @return elements as String
     */
    public List<String> getStringValues() {
		return CMLProperty.getStaticStringValues(this);
	}
    
    /**
     * gets values of element;
     * 
     * @return integer values
     */
    public int[] getInts() {
		return CMLProperty.getStaticInts(this);
	}

    /**
     * gets values of element;
     * 
     * @return double values
     */
    public double[] getDoubles() {
		return CMLProperty.getStaticDoubles(this);
	}
	
	/**
	 * requires exactly one child of type scalar array matrix
	 * @return the child
	 */
	public HasDataType getChild() {
		if (child == null) {
			child = CMLProperty.getStaticChild(this);
		}
		return child;
	}
	
	/**
	 * @param child the child to set
	 */
	public void setChild(HasDataType child) {
		this.child = child;
	}


	/** gets dataType
	 * @return dataType as string
	 */
	public String getDataType() {
		return CMLProperty.getStaticDataType(this);
	}

}
