/*
 * 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.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDCompositor;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDForm;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSchema;

import eu.mdd4soa.smm.exception.TransformationException;
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.trans.smm2bpel.impl.util.GPTracker;

/**
 * 
 * The static data converter creates one XSD complex type definition for each
 * messageType in the UML4SOA model. The properties of the message types are
 * converted to elements which are placed in an xsd:sequence. While a sequence
 * is not strictly necessary, it does not matter as there is no sequence in UML.
 * The other possibility would have been xsd:all, but elements in an xsd:all
 * cannot occur more than once (maxoccurs must be 0 or 1).
 * 
 * Each property has a type as well which is another one of the generated XSD
 * complex types or an XSD primitive type. There is no inheritance, as the SMM
 * does not contain the means to express it.
 * 
 * An example for a generated data type: <code>
 * <xs:complexType name="Thesis">
 *  <xs:sequence>
 * 	    <xs:element name="id" type="xs:string"/>
 *      <xs:element name="title" type="xs:string"/>
 *      <xs:element name="student" type="this:Student"/>
 *      <xs:element maxOccurs="unbounded" minOccurs="0" name="documents" type="this:Document"/>
 *      <xs:element name="description" type="xs:string"/>
 *      <xs:element name="grade" type="this:Grade"/>
 *    </xs:sequence>
 *  </xs:complexType>
 * </code>
 * 
 * @author Phil
 * 
 */
public class StaticDataConverter {

	public static XSDSchema convert(GPTracker tracker, Participant participant) throws TransformationException {
		StaticDataConverter c= new StaticDataConverter(tracker, participant);
		c.execute();
		return c.getResult();
	}

	private static XSDFactory xf= XSDFactory.eINSTANCE;

	private XSDSchema fSchema;

	private Participant fParticipant;

	private GPTracker fTracker;

	public StaticDataConverter(GPTracker tracker, Participant participant) {
		fParticipant= participant;
		fTracker= tracker;
		fSchema= xf.createXSDSchema();
	}

	private void execute() throws TransformationException {

		fSchema.setElementFormDefault(XSDForm.QUALIFIED_LITERAL);
		fSchema.getQNamePrefixToNamespaceMap().put(GPTracker.NS_XSD_PREFIX, GPTracker.NS_XSD_URI);
		fSchema.setSchemaForSchemaQNamePrefix(GPTracker.NS_XSD_PREFIX);

		fSchema.setTargetNamespace(fTracker.getTypeNamespace());
		fSchema.getQNamePrefixToNamespaceMap().put("this", fTracker.getTypeNamespace());

		fTracker.registerSchema(fSchema);

		Map<MessageType, XSDModelGroup> typeMap= new HashMap<MessageType, XSDModelGroup>();

		EList<SMMType> types= fParticipant.getTypes();
		for (SMMType ismType : types) {
			if (ismType instanceof MessageType) {
				typeMap.put((MessageType) ismType, addTypeToXSD(ismType));
			}
		}

		for (SMMType ismType : types) {
			if (ismType instanceof MessageType) {
				MessageType msgType= (MessageType) ismType;
				EList<MessageProperty> properties= msgType.getProperties();
				for (MessageProperty messageProperty : properties) {
					XSDModelGroup typeOfProperty= typeMap.get(msgType);
					if (typeOfProperty == null)
						throw new TransformationException("Referenced type of attribute " + messageProperty + " of type " + msgType
								+ " was not found.");
					addAttribute(typeOfProperty, messageProperty);
				}
			}
		}
	}

	private XSDModelGroup addTypeToXSD(SMMType ismType) {
		MessageType msgType= (MessageType) ismType;

		XSDComplexTypeDefinition complexType= xf.createXSDComplexTypeDefinition();
		complexType.setTargetNamespace(fSchema.getTargetNamespace());
		fSchema.getContents().add(complexType);
		complexType.setName(msgType.getName());

		XSDParticle particle= XSDFactory.eINSTANCE.createXSDParticle();
		complexType.setContent(particle);

		XSDModelGroup modelGroup= XSDFactory.eINSTANCE.createXSDModelGroup();
		modelGroup.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
		particle.setContent(modelGroup);

		return modelGroup;
	}

	private XSDSchema getResult() {
		return fSchema;
	}

	private XSDParticle addAttribute(XSDModelGroup mg, MessageProperty messageProperty) throws TransformationException {

		XSDElementDeclaration element= XSDFactory.eINSTANCE.createXSDElementDeclaration();
		element.setName(messageProperty.getIdentifier());
		element.setTypeDefinition(fTracker.getMatchingXSDType(messageProperty.getType()));

		XSDParticle particle= XSDFactory.eINSTANCE.createXSDParticle();
		particle.setContent(element);
		mg.getContents().add(particle);

		// Default is "1"

		if (messageProperty.getMax() != 1)
			particle.setMaxOccurs(messageProperty.getMax());

		// Default is "1", but set to 0. 1 means that the element MUST BE
		// PRESENT IN THE XML, which is not really the intention here. In
		// particular,
		// Axis fails in calls when a <thesis><student/><thesis> is given and no
		// inner elements for student are present although they are minoccurs=1.
		particle.setMinOccurs(0);
		if (messageProperty.getMin() > 1)
			particle.setMinOccurs(messageProperty.getMin());

		return particle;
	}



}
