package edu.uga.cs.lsdis.meteors.wadls;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.wadls.Application;
import javax.wadls.ModelReference;
import javax.wadls.Params;
import javax.wadls.WADLSException;
import javax.wadls.extensions.schema.Schema;
import javax.xml.namespace.QName;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;

import edu.uga.cs.lsdis.meteors.wadls.util.SchemaUtils;

/**
 * This class represents the &lt;types&gt; section of a WSDL document.
 *
 * @author Zixin Wu (wuzixin@uga.edu)
 * @author Matthew J. Duftler (duftler@us.ibm.com)
 */
public class ParamsImpl implements Params
{
	protected List<ModelReference> modelRefs = null;
	protected Application app = null;		//WSDLS Definition
	protected Element docEl = null;
	protected List extElements = new Vector();
	protected Map allSchemas = null;
	
	public static final long serialVersionUID = 1;
	
	public ParamsImpl(Application app){
		this.app = app;
	}
	
	public List getTopLevelSchemas(){
		return SchemaUtils.getSchemas(this.extElements);		
	}
	
	/**
	 * Get the DOM elements of all the schemas in this Types.
	 * @return A list of DOM elements of all the schemas in this Types.
	 */
	public Map getSchemas(){
		return this.allSchemas;
	}
	
	public void setSchemas(Map allSchemas){
		this.allSchemas = allSchemas;
	}
	
	

	public void addModelReference (ModelReference modelReference){
		if (this.modelRefs == null)
			modelRefs= new ArrayList<ModelReference> ();
		modelRefs.add(modelReference);
	}
	
	
	/**
	 * Get the modelReference of this operation.
	 *
	 * @return the modelReference value
	 */
	
	public ModelReference getModelReference(){
		if(modelRefs == null)
			return null;
		return modelRefs.get(0);
	}
	/**
	 * Get the DOM element of the first schema in this Types.
	 * @return The DOM element of the first schema in this Types.
	 */
	public Schema getFirstSchema(){
		return SchemaUtils.getFirstSchema(this.extElements);
	}
	public List<ModelReference> getModelReferences() {
		return modelRefs;
	}
	public void setModelReferences(List<ModelReference> refs) {
		modelRefs = refs;		
	}
	/**
	 * Get the XSD simpleType with the given name.
	 * @param name The QName of the type
	 * @return A DOM Element representing the simpleType
	 * @throws WADLSException 
	 */
	
	public Element getXSDElement(String xpath) throws WADLSException{
		Element returnElt = null;
		Map schemas = this.getSchemas();
		Iterator it = schemas.values().iterator();
		while(it.hasNext()){
			Schema schema = (Schema)(it.next());
			Element schemaEle = schema.getElement();
			returnElt = this.getXSDElement(schemaEle, xpath);
			if (returnElt != null)
				return returnElt;
		}
		return null;
	}
	
	/**
	 * Get the modelReference on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @return The modelReference
	 */
	public ModelReference getModelReference(Element startElement, String path, Application app) throws WADLSException, URISyntaxException{
		List<ModelReference> mrefs = getModelReferences(startElement, path, app);
		return mrefs.get(0);
	}
	
	/**
	 * Set the modelReference on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @param modelReference The desired modelReference
	 */
	public void addModelReference(Element startElement, String path, ModelReference modelReference) throws WADLSException{
		Element el = getXSDEle(startElement, path);
		
		if (modelReference != null){
			String strModelReference = modelReference.value();
			Attr attr = el.getAttributeNodeNS(
					Constants.NS_URI_WADLS, Constants.ATTR_MODELREF);
			if(attr == null) {
				attr = el.getOwnerDocument().createAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_MODELREF);
				attr.setPrefix(Constants.PREFIX_WSDLS);
				el.setAttributeNodeNS(attr);
			}
			String value = attr.getValue();
			if(value != null) {
				value += " " + strModelReference;
			} else {
				value = strModelReference;
			}
			attr.setValue(value);
			modelReference.setParent(el);
		}
		else
			el.removeAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_MODELREF);
	}
	
	/**
	 * Get the liftingSchemaMapping on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @return The schemaMapping value
	 */
	public String getLiftingSchemaMapping(Element startElement, String path) throws WADLSException{
		Element el = getXSDEle(startElement, path);
		if (el == null)
			return null;
		String attrSchemaMapping = el.getAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LIFTINGSCHEMAMAPPING);
		if (attrSchemaMapping == "")
			return null;
		return attrSchemaMapping;
	}
	
	/**
	 * Get the loweringSchemaMapping on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @return The schemaMapping value
	 */
	public String getLoweringSchemaMapping(Element startElement, String path) throws WADLSException{
		Element el = getXSDEle(startElement, path);
		if (el == null)
			return null;
		String attrSchemaMapping = el.getAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LOWERINGSCHEMAMAPPING);
		if (attrSchemaMapping == "")
			return null;
		return attrSchemaMapping;
	}
	
	/**
	 * Set the liftingSchemaMapping on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @param schemaMapping The desired schemaMapping
	 */
	public void setLiftingSchemaMapping(Element startElement, String path, String schemaMapping) throws WADLSException{
		Element el = getXSDEle(startElement, path);
		if (schemaMapping != null){
			Attr attr = el.getOwnerDocument().createAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LIFTINGSCHEMAMAPPING);
			attr.setPrefix(Constants.PREFIX_WSDLS);
			attr.setValue(schemaMapping);
			el.setAttributeNodeNS(attr);
		}
		else
			el.removeAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LIFTINGSCHEMAMAPPING);
	}
	
	/**
	 * Set the loweringSchemaMapping on the element located by the given path.
	 * @param startElement The starting element of the path
	 * @param path
	 * @param schemaMapping The desired schemaMapping
	 */
	public void setLoweringSchemaMapping(Element startElement, String path, String schemaMapping) throws WADLSException{
		Element el = getXSDEle(startElement, path);
		if (schemaMapping != null){
			Attr attr = el.getOwnerDocument().createAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LOWERINGSCHEMAMAPPING);
			attr.setPrefix(Constants.PREFIX_WSDLS);
			attr.setValue(schemaMapping);
			el.setAttributeNodeNS(attr);
		}
		else
			el.removeAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_LOWERINGSCHEMAMAPPING);
	}
	
	private Element getXSDEle(Element startElement, String path) throws WADLSException{
		if (path == null || path == "")
			return startElement;
		String eleName = startElement.getLocalName();
		if (eleName.equals("element")){					//search the target from <element ...
			return SchemaUtils.findXSDEleOnEle(startElement, path, this);
		}
		else if (eleName.equals("complexType")){			//search the target from <complexType ...
			return SchemaUtils.findXSDEleOnComplexType(startElement, path, this);
		}
		else{												//error
			WADLSException wsdlsExc = new WADLSException(WADLSException.PATH_ERROR, 
			"simpleType cannot has path");
			throw wsdlsExc;
		}
	}
	
	/**
	 * Return an XML Schema Element by locating it from the XSD Element startElement and using the path.
	 * @param startElement
	 * @param path
	 * @return an XML Schema Element 
	 * @throws WADLSException
	 */
	public Element getXSDElement(Element startElement, String path) throws WADLSException{
		return SchemaUtils.findXSDEleOnEle(startElement, path, this);
	}
	
	/**
	 * Return a list of XSD elements contained in the ComplexType startElement, search in only 1 level depth. 
	 * @param startXSDElement
	 * @return A list of XSD elements contained in the startElement. If no XSD element is contained, the list has 0 element.
	 * @throws WADLSException
	 */
	public List getXSDElementsInComplexType(Element startXSDElement) throws WADLSException{
		return SchemaUtils.listXSDElesInComplexType(startXSDElement);
	}
	
	/**
	 * Return a list of XSD elements contained in the startElement, search in only 1 level depth. 
	 * @param startXSDComplexType
	 * @return A list of XSD elements contained in the startElement. If no XSD element is contained, the list has 0 element.
	 * @throws WADLSException
	 */
	public List getXSDElementsInElement(Element startXSDComplexType) throws WADLSException{
		return SchemaUtils.listXSDElesInEle(startXSDComplexType, this);
	}
	
	

	/**
	 * Set the documentation element for this document. This dependency
	 * on org.w3c.dom.Element should eventually be removed when a more
	 * appropriate way of representing this information is employed.
	 *
	 * @param docEl the documentation element
	 */
	public void setDocumentationElement(Element docEl)
	{
		this.docEl = docEl;
	}
	
	/**
	 * Get the documentation element. This dependency on org.w3c.dom.Element
	 * should eventually be removed when a more appropriate way of
	 * representing this information is employed.
	 *
	 * @return the documentation element
	 */
	public Element getDocumentationElement()
	{
		return docEl;
	}
	
	/**
	 * Add an extensibility element.
	 *
	 * @param extElement the extensibility element to be added
	 */
	
	
	/**
	 * Get all the extensibility elements defined here.
	 */
	public List getExtensibilityElements()
	{
		return extElements;
	}
	
	public String toString()
	{
		StringBuffer strBuf = new StringBuffer();
		
		strBuf.append("Types:");
		
		if (extElements != null)
		{
			Iterator extIterator = extElements.iterator();
			
			while (extIterator.hasNext())
			{
				strBuf.append("\n" + extIterator.next());
			}
		}
		
		return strBuf.toString();
	}
	
	public List<ModelReference> getModelReferences(Element startElement, String path, Application app) throws WADLSException, URISyntaxException {
		Element el = getXSDEle(startElement, path);
		if (el == null)
			return null;
		String attrModelReference = el.getAttributeNS(Constants.NS_URI_WADLS, Constants.ATTR_MODELREF);
		if (attrModelReference.equals(""))
			return null;
		List<ModelReference> mrefs = ModelReferenceImpl.getModelReferences(attrModelReference, app);
		if(mrefs.size() == 0)
			return null;
		return mrefs;
	}
	public void setModelReferences(Element startElement, String path, List<ModelReference> refs) throws WADLSException {
		for(ModelReference ref : refs) {
			addModelReference(startElement, path, ref);
		}
	}
}
