/*
 * This file has been developed at the University of Munich, Chair for Programming & Software Engineering.
 * 
 * This file is licensed under the Eclipse Public License (EPL) 1.0
 * 
 */
package eu.mdd4soa.trans.smm2bpel.impl.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.eclipse.bpel.model.messageproperties.Property;
import org.eclipse.bpel.model.partnerlinktype.PartnerLinkType;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.Operation;
import org.eclipse.wst.wsdl.util.WSDLConstants;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDTypeDefinition;

import eu.mdd4soa.smm.behaviour.ServiceActivity;
import eu.mdd4soa.smm.exception.TransformationException;
import eu.mdd4soa.smm.statik.BooleanType;
import eu.mdd4soa.smm.statik.DateType;
import eu.mdd4soa.smm.statik.IntegerType;
import eu.mdd4soa.smm.statik.InterfaceOperation;
import eu.mdd4soa.smm.statik.InterfaceParameter;
import eu.mdd4soa.smm.statik.PrimitiveType;
import eu.mdd4soa.smm.statik.RealType;
import eu.mdd4soa.smm.statik.SMMType;
import eu.mdd4soa.smm.statik.Service;
import eu.mdd4soa.smm.statik.StringType;
import eu.mdd4soa.smm.statik.TypedMultiElement;

public class GPTracker {

	public static final String NS_XSD_PREFIX= "xs";

	public static final String NS_XSD_URI= WSDLConstants.SCHEMA_FOR_SCHEMA_URI_2001;

	private String fProcessURI;

	private XSDSchema fSchema;

	public static final String XSD_STRING= "string";

	public static final String XSD_INTEGER= "integer";

	public static final String XSD_DOUBLE= "decimal";

	public static final String XSD_BOOLEAN= "boolean";

	public static final String XSD_DATE= "date";

	private static Set<String> primitiveXsdTypes= new HashSet<String>();
	{
		primitiveXsdTypes.add(XSD_BOOLEAN);
		primitiveXsdTypes.add(XSD_DOUBLE);
		primitiveXsdTypes.add(XSD_INTEGER);
		primitiveXsdTypes.add(XSD_STRING);
		primitiveXsdTypes.add(XSD_DATE);
	}

	private Map<Service, PartnerLinkType> fServicePLTS;

	private Map<InterfaceOperation, Message> fInMessageMap;

	private Map<InterfaceOperation, Message> fOutMessageMap;

	private Map<InterfaceOperation, Operation> fOperationMap;

	private Map<InterfaceParameter, TypedMultiElement> fCorrelationAliasMap;

	private Map<String, Property> fCorrelationMap;

	private Map<Message, String> fCorrelationMessageMap;


	public GPTracker(String pName) {
		fProcessURI= "http://www.mdd4soa.eu/generated/" + pName + "/";
		fServicePLTS= new HashMap<Service, PartnerLinkType>();

		fInMessageMap= new HashMap<InterfaceOperation, Message>();
		fOutMessageMap= new HashMap<InterfaceOperation, Message>();

		fOperationMap= new HashMap<InterfaceOperation, Operation>();

		// TODO this can probably be simplified.
		fCorrelationAliasMap= new HashMap<InterfaceParameter, TypedMultiElement>();
		fCorrelationMap= new HashMap<String, Property>();
		fCorrelationMessageMap= new HashMap<Message, String>();
	}

	/**
	 * The URI of the types.
	 * 
	 * @return
	 */
	public String getTypeNamespace() {
		return fProcessURI;
	}

	private String getProcessPrefix() {
		return "this";
	}

	public static boolean isPrimitiveType(QName xsdType) {
		return primitiveXsdTypes.contains(xsdType.getLocalPart());
	}

	public void registerSchema(XSDSchema schema) {
		fSchema= schema;
	}

	public XSDTypeDefinition getMatchingXSDType(SMMType type) throws TransformationException {
		QName qname= getMatchingXSDTypeQName(type);
		return fSchema.resolveTypeDefinition(qname.getNamespaceURI(), qname.getLocalPart());
	}

	public QName getMatchingXSDTypeQName(SMMType theType) throws TransformationException {

		if (theType == null)
			throw new TransformationException("Null Type given to getXSDNameForType.");

		// use default XSD primitive types for primitive types
		if (theType instanceof PrimitiveType) {
			String simpleName= null;
			if (theType instanceof StringType) {
				simpleName= XSD_STRING;
			} else if (theType instanceof BooleanType) {
				simpleName= XSD_BOOLEAN;
			} else if (theType instanceof IntegerType) {
				simpleName= XSD_INTEGER;
			} else if (theType instanceof RealType) {
				simpleName= XSD_DOUBLE;
			} else if (theType instanceof DateType) {
				simpleName= XSD_DATE;
			} else {
				throw new TransformationException("InteractionType of " + theType + " is an unknown primitive element.");
			}
			return new QName(NS_XSD_URI, simpleName, NS_XSD_PREFIX);
		}

		// no primitive type.
		return new QName(getTypeNamespace(), theType.getName(), getProcessPrefix());
	}

	public String getPartnerNamespace(String name) {
		return "http://www.mdd4soa.eu/generated/partner/" + name + "/";
	}

	public String getProcessNamespace(ServiceActivity behaviour) {
		return "http://www.mdd4soa.eu/generated/process/" + behaviour.getName() + "/";
	}


	public void setServicePLTForPartner(Service partnerPoint, PartnerLinkType plt) {
		fServicePLTS.put(partnerPoint, plt);
	}

	public PartnerLinkType getServicePLT(Service partnerPoint) {
		return fServicePLTS.get(partnerPoint);
	}

	public void registerMessage(InterfaceOperation op, Message msg, WSDLMessageType in) {
		switch (in) {
			case IN:
				fInMessageMap.put(op, msg);
				break;
			case OUT:
				fOutMessageMap.put(op, msg);
				break;
		}
	}

	public Message getMessage(InterfaceOperation op, WSDLMessageType msgType) {
		switch (msgType) {
			case IN:
				return fInMessageMap.get(op);
			case OUT:
				return fOutMessageMap.get(op);
		}
		return null;
	}

	public void registerOperation(InterfaceOperation op, Operation wsdlOp) {
		fOperationMap.put(op, wsdlOp);
	}

	public Operation getOperation(InterfaceOperation operation) {
		return fOperationMap.get(operation);
	}

	public void registerCorrelationPropertyAlias(InterfaceParameter idParameter, TypedMultiElement idProperty) {
		fCorrelationAliasMap.put(idParameter, idProperty);
	}

	public void registerCorrelationProperty(String id, Property correlationProperty) {
		fCorrelationMap.put(id, correlationProperty);
	}

	public TypedMultiElement getCorrelationPropertyAlias(InterfaceParameter interfaceParameter) {
		return fCorrelationAliasMap.get(interfaceParameter);
	}

	public Property getCorrelationProperty(String name) {
		return fCorrelationMap.get(name);
	}

	public Set<String> getCorrelationSets() {
		return fCorrelationMap.keySet();
	}

	public void registerCorrelationMessage(Message msg, String name) {
		fCorrelationMessageMap.put(msg, name);
	}

	public String getCorrelationSet(Message messageType) {
		return fCorrelationMessageMap.get(messageType);
	}

}
