/*
 * 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;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.eclipse.bpel.model.messageproperties.MessagepropertiesFactory;
import org.eclipse.bpel.model.messageproperties.Property;
import org.eclipse.emf.common.util.EList;
import org.eclipse.wst.wsdl.Definition;
import org.eclipse.wst.wsdl.WSDLFactory;
import org.eclipse.wst.wsdl.util.WSDLConstants;

import eu.mdd4soa.smm.exception.TransformationException;
import eu.mdd4soa.smm.statik.InterfaceOperation;
import eu.mdd4soa.smm.statik.InterfaceParameter;
import eu.mdd4soa.smm.statik.MessageProperty;
import eu.mdd4soa.smm.statik.MessageType;
import eu.mdd4soa.smm.statik.Participant;
import eu.mdd4soa.smm.statik.SMMType;
import eu.mdd4soa.smm.statik.Service;
import eu.mdd4soa.smm.statik.TypedMultiElement;
import eu.mdd4soa.trans.smm2bpel.impl.util.GPTracker;

public class StaticCorrelationConverter {

	public static Definition convert(GPTracker tracker, Participant participant) throws TransformationException {
		StaticCorrelationConverter scc= new StaticCorrelationConverter(tracker, participant);
		scc.run();
		return scc.getResult();
	}

	private GPTracker fTracker;

	private Participant fParticipant;

	private Map<String, List<TypedMultiElement>> fCorrelationMap;

	private Map<String, SMMType> fPropertyTypeMap;

	private static final WSDLFactory wf= WSDLFactory.eINSTANCE;

	private Definition fDefinition;

	public StaticCorrelationConverter(GPTracker tracker, Participant participant) {
		fTracker= tracker;
		fParticipant= participant;
		fCorrelationMap= new HashMap<String, List<TypedMultiElement>>();
		fPropertyTypeMap= new HashMap<String, SMMType>();
	}

	private Definition getResult() {
		return fDefinition;
	}

	private void run() throws TransformationException {

		EList<Service> partners= fParticipant.getPartners();

		List<InterfaceOperation> all= new ArrayList<InterfaceOperation>();

		for (Service partnerPoint : partners) {
			all.addAll(partnerPoint.getInterface().getImplemented());
			all.addAll(partnerPoint.getInterface().getUsed());
		}

		for (InterfaceOperation op : all) {
			EList<InterfaceParameter> parameters= op.getParameters();

			InterfaceParameter idParameter= null;
			TypedMultiElement idProperty= null;

			for (InterfaceParameter inparams : parameters) {
				String name= inparams.getName();
				if (name.endsWith("Id")) {
					idParameter= inparams;
					idProperty= inparams;
					break;
				} else {
					SMMType type= inparams.getType();
					if (type instanceof MessageType) {
						MessageType msgType= (MessageType) type;
						EList<MessageProperty> properties= msgType.getProperties();
						for (MessageProperty messageProperty : properties) {
							if (messageProperty.getIdentifier().endsWith("Id")) {
								idParameter= inparams;
								idProperty= messageProperty;
							}

						}
					}
				}
			}

			if (idProperty == null)
				throw new TransformationException("Could not find an id in operation " + op);

			if (idProperty.getMax() > 1)
				throw new TransformationException("Cannot use an id field with max > 1: " + idProperty);

			addToMap(idProperty);
			fTracker.registerCorrelationPropertyAlias(idParameter, idProperty);
		}

		// Got a map of all identified ids and their correlation alias
		// properties now.

		fDefinition= wf.createDefinition();

		fDefinition.addNamespace("", WSDLConstants.WSDL_NAMESPACE_URI);
		fDefinition.addNamespace("this", fTracker.getTypeNamespace() + "correlation/");
		fDefinition.addNamespace("xs", WSDLConstants.SCHEMA_FOR_SCHEMA_URI_2001);

		fDefinition.setQName(new QName(fTracker.getTypeNamespace() + "correlation/", "correlation"));
		fDefinition.setTargetNamespace(fTracker.getTypeNamespace() + "correlation/");

		Set<String> keySet= fCorrelationMap.keySet();
		for (String id : keySet) {

			Property correlationProperty= MessagepropertiesFactory.eINSTANCE.createProperty();
			correlationProperty.setName(id);
			correlationProperty.setType(fTracker.getMatchingXSDType(fPropertyTypeMap.get(id)));

			fDefinition.addExtensibilityElement(correlationProperty);

			fTracker.registerCorrelationProperty(id, correlationProperty);
		}
	}

	private void addToMap(TypedMultiElement multiElement) throws TransformationException {

		String name= (multiElement instanceof InterfaceParameter) ? ((InterfaceParameter) multiElement).getName() : ((MessageProperty) multiElement)
				.getIdentifier();

		SMMType existing= fPropertyTypeMap.get(name);
		if (existing == null)
			fPropertyTypeMap.put(name, multiElement.getType());
		else {
			if (!existing.equals(multiElement.getType()))
				throw new TransformationException("Found an id (" + name + ") with different types. Existing is " + existing + " and new is "
						+ multiElement.getType());
		}

		List<TypedMultiElement> list= fCorrelationMap.get(name);
		if (list == null) {
			list= new ArrayList<TypedMultiElement>();
			fCorrelationMap.put(name, list);
		}
		list.add(multiElement);
	}



}
