package ever.workflowRepresentation;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
 * This class is the root for workflow. It contains a Sequence which is the main sequence of the workflow. When an serialization to XML is
 * necessary, the exportToWfTree Method has to be called. 
 * @author Pol Schumacher, Wirtschaftsinformatik, Institut fuer Informatik, Goethe Universitaet Frankfurt
 * 
 */
public class Workflow implements Serializable {
	/**
	 * 
	 */

	private static final long serialVersionUID = 4;

	
	// To store the ThreadLocal information when the thread was destroyed
	// Stores a list of all products used in the workflow
	
	/**
	 * It contains the list of the resources of the resource list in the original process description.
	 * In a cooking domain this would be the ingredient list. 
	 *Conceptional this fields belong to the product. It is used e.g. to test if an extracted product is in the 
	 *resource list or not. Usually this would be done with a static field, which can not be used because of 
	 *multithreading. See ThreadLocal concept for more information.	
	 */	
	public ArrayList<Product> all;

	//	// To store the ThreadLocal information when the thread was destroyed
//	// Stores which products names where used and how often. This makes it
//	// possible to have different HashValues for products with the same name
//	private HashMap<Integer, Integer> usedNames;
	
	/**
	 *Stores the last id which was used for a product. Necessary to generate the ids for the next product.  
	 *Usually this would be done with a static field, which can not be used because of 
	 *multithreading. See ThreadLocal concept for more information.	
	 */	
	private int lastId;

	public static final String base = "http://cake.wi2.uni-trier.de/rdf/cake";
	/**
	 * The root sequence of a workflow.
	 */
	Sequence topLevel;

	/**
	 * Standard workflow constructor
	 */
	public Workflow() {
		
		topLevel = new Sequence(null,null);
//		usedNames=new HashMap<Integer,Integer>();
		all=new ArrayList<Product>();
		lastId=-1;
	}

	/**
	 * Sets a new root sequence.
	 * @param seq The Sequence which is set as new root sequence
	 */
	public void setTopLevel(Sequence seq) {
		topLevel.addWfElement(seq);
	}

	// public void printWorkflow()
	// {
	// String current="";
	//
	// while(current!=null)
	// {
	// current = topLevel.popNext().getName();
	// System.out.println(current);
	// }
	// }

	/**
	 * This method serializes the workflow to xml and writes out to a file. The Cake dataformat is used.
	 * @param location path to output location
	 */
	public void exportWfToTree(String location) {
		XMLOutputFactory factory = XMLOutputFactory.newInstance();
		try {
			XMLStreamWriter writer = factory
					.createXMLStreamWriter(new FileOutputStream(location));
			writer.writeStartDocument();
			writer.writeStartElement("cdop:ObjectPool");

			writer.writeNamespace("rwfl",
					"http://cake.wi2.uni-trier.de/xml/rwfl");
			writer.writeNamespace("nest",
					"http://cake.wi2.uni-trier.de/xml/nest");
			writer.writeNamespace("cdol",
					"http://cake.wi2.uni-trier.de/xml/cdol");
			writer.writeNamespace("cdop",
					"http://cake.wi2.uni-trier.de/xml/cdop");
			writer.writeNamespace("xsi",
					"http://www.w3.org/2001/XMLSchema-instance");

			writer.writeAttribute(
					"xsi:schemaLocation",
					"http://cake.wi2.uni-trier.de/xml/nest nest.xsd "
							+ "http://cake.wi2.uni-trier.de/xml/cdop cdop.xsd http://cake.wi2.uni-trier.de/xml/rwfl "
							+ "rwfl.xsd http://cake.wi2.uni-trier.de/xml/cdol cdol.xsd");
			writer.writeStartElement("rwfl:Workflow");

			writer.writeAttribute("status", "READY");
			// DataObjects
			writer.writeStartElement("rwfl:DataObjects");
			writer.writeCharacters("\n");
			all.add(new SimpleProduct("TESTTEST", 0));
//			HashSet<Product> all = 
//			for (Product prod : all) {
//				try {
//					prod.exportProd2List(writer);
//				} catch (EmptyProductException e) {
//					// TODO Auto-generated catch block
//					System.err.println("Empty Product was in Datastructure.");
//					continue;
//				}
//			}
			writer.writeEndElement();
			// Workflow output
			topLevel.exportWf2Xml(writer);
			writer.writeEndDocument();
			writer.writeEndDocument();
			writer.close();			
			// if(!ExportValidator.validate(location, "cdop.xsd"))
			// {
			// File f = new File(location);
			// if(f.exists())
			// f.delete();
			// }

		} catch (XMLStreamException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	/**
	 * This method serializes the workflow to xml and returns it as String. The Cake dataformat is used.
	 * @return The workflow in an xml string
	 */
	public String exportWfToTree() {
		XMLOutputFactory factory = XMLOutputFactory.newInstance();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			
			XMLStreamWriter writer = factory
					.createXMLStreamWriter(out);
			writer.writeStartDocument();
			writer.writeStartElement("cdop:ObjectPool");

			writer.writeNamespace("rwfl",
					"http://cake.wi2.uni-trier.de/xml/rwfl");
			writer.writeNamespace("nest",
					"http://cake.wi2.uni-trier.de/xml/nest");
			writer.writeNamespace("cdol",
					"http://cake.wi2.uni-trier.de/xml/cdol");
			writer.writeNamespace("cdop",
					"http://cake.wi2.uni-trier.de/xml/cdop");
			writer.writeNamespace("xsi",
					"http://www.w3.org/2001/XMLSchema-instance");

			writer.writeAttribute(
					"xsi:schemaLocation",
					"http://cake.wi2.uni-trier.de/xml/nest nest.xsd "
							+ "http://cake.wi2.uni-trier.de/xml/cdop cdop.xsd http://cake.wi2.uni-trier.de/xml/rwfl "
							+ "rwfl.xsd http://cake.wi2.uni-trier.de/xml/cdol cdol.xsd");
			writer.writeStartElement("rwfl:Workflow");

			writer.writeAttribute("status", "READY");
			// DataObjects
			writer.writeStartElement("rwfl:DataObjects");
			writer.writeCharacters("\n");
			all.add(new SimpleProduct("TESTTEST", 0));
//			HashSet<Product> all = 
			for (Product prod : all) {
				try {
					prod.exportProd2List(writer);
				} catch (EmptyProductException e) {
					// TODO Auto-generated catch block
					System.err.println("Empty Product was in Datastructure.");
					continue;
				}
			}
			writer.writeEndElement();
			// Workflow output
			topLevel.exportWf2Xml(writer);

			writer.writeEndDocument();
			writer.writeEndDocument();
			writer.close();
			return out.toString();
			// if(!ExportValidator.validate(location, "cdop.xsd"))
			// {
			// File f = new File(location);
			// if(f.exists())
			// f.delete();
			// }

		} catch (XMLStreamException e) {		
			e.printStackTrace();
			return null;
		} catch (Exception e) {			
			e.printStackTrace();
			return null;
		}

	}

//	// Export fn for artificial workflow without complex Dataflow handling
//	public void exportWfToTreeArti(String location) {
//		XMLOutputFactory factory = XMLOutputFactory.newInstance();
//		try {
//			XMLStreamWriter writer = factory
//					.createXMLStreamWriter(new FileOutputStream(location));
//			writer.writeStartDocument();
//			writer.writeStartElement("cdop:ObjectPool");
//
//			writer.writeNamespace("rwfl",
//					"http://cake.wi2.uni-trier.de/xml/rwfl");
//			writer.writeNamespace("nest",
//					"http://cake.wi2.uni-trier.de/xml/nest");
//			writer.writeNamespace("cdol",
//					"http://cake.wi2.uni-trier.de/xml/cdol");
//			writer.writeNamespace("cdop",
//					"http://cake.wi2.uni-trier.de/xml/cdop");
//			writer.writeNamespace("xsi",
//					"http://www.w3.org/2001/XMLSchema-instance");
//
//			writer.writeAttribute(
//					"xsi:schemaLocation",
//					"http://cake.wi2.uni-trier.de/xml/nest nest.xsd "
//							+ "http://cake.wi2.uni-trier.de/xml/cdop cdop.xsd http://cake.wi2.uni-trier.de/xml/rwfl "
//							+ "rwfl.xsd http://cake.wi2.uni-trier.de/xml/cdol cdol.xsd");
//			writer.writeStartElement("rwfl:Workflow");
//
//			writer.writeAttribute("status", "READY");
//			// DataObjects
//			writer.writeStartElement("rwfl:DataObjects");
//			writer.writeCharacters("\n");
////			HashSet<Product> all = SimpleProduct.getAllProducts();
//			for (Product prod : all) {
//				try {
//					prod.exportProd2List(writer);
//				} catch (EmptyProductException e) {
//					// TODO Auto-generated catch block
//					System.err.println("Empty Product was in Datastructure.");
//					continue;
//				}
//			}
//			writer.writeEndElement();
//			// Workflow output
//			topLevel.exportWf2XmlArti(writer);
//
//			writer.writeEndDocument();
//			writer.writeEndDocument();
//			writer.close();
//			// if(!ExportValidator.validate(location, "cdop.xsd"))
//			// {
//			// File f = new File(location);
//			// if(f.exists())
//			// f.delete();
//			// }
//
//		} catch (XMLStreamException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (FileNotFoundException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//
//	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((topLevel == null) ? 0 : topLevel.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Workflow other = (Workflow) obj;
		if (topLevel == null) {
			if (other.topLevel != null)
				return false;
		} else if (!topLevel.equals(other.topLevel))
			return false;
		return true;
	}

//	public WorkflowElement popLast() {
//		return topLevel.popLast();
//	}
//
//	public WorkflowElement getLast() {
//		return topLevel.getLast();
//	}

	/**
	 * Get the root sequence of a workflow.
	 * @return root sequence
	 */
	public Sequence getTopSequence() {
		return topLevel;
	}

	/**
	 * Sets the root sequence of the workflow.
	 * @param seq
	 */
	public void setTopSequence(Sequence seq) {
		topLevel = seq;
	}
	/**
	 * Is used to add an ingredient aka. a product which was listed in the ressource list of a process description.
	 * @param p 
	 */
	public void addProduct(Product p)
	{
		all.add(p);
	}

	//When a Thread is started or has finished we need to call these methods to change the store position
		/**
		 * This method copies product data from the ThreadLocal to this workflow and resets the ThreadLocal Data.
		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment. 
		 */
		public void moveFromThreadLocalAllProducts()
		{
			all=SimpleProduct.getAllProductsThreadLocal();			
			SimpleProduct.setAllProductsThreadLocal(new ArrayList<Product>());
		}
		/**
		 * Takes the data which is in workflow scoope and copies it to the ThreadLocal element. 
		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment.
		 */
		public void moveToThreadLocalAllProducts()
		{			
			SimpleProduct.setAllProductsThreadLocal(all);
		}
//		/**
//		 * This method copies the used names data from the ThreadLocal to this workflow and resets the ThreadLocal Data.
//		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment. 
//		 */
//		public void moveFromThreadLocalUsedNames()
//		{
//			usedNames=SimpleProduct.getUsedThreadLocal();
//			SimpleProduct.setUsedThreadLocal(new HashMap<Integer,Integer>());
//		}
//		/**
//		 * Takes the data which is in workflow scoope and copies it to the ThreadLocal element. 
//		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment.
//		 */
//		public void moveToThreadLocalUsedNames()
//		{			
//			SimpleProduct.setUsedThreadLocal(usedNames);				
//		}
		/**
		 * This method copies the last id used data from the ThreadLocal to this workflow and resets the ThreadLocal Data.
		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment. 
		 */
		public void moveFromThreadLocalLastId()
		{
			lastId=SimpleProduct.getLastIdThreadLocal();
			SimpleProduct.setLastIdThreadLocal(-1);
		}
		/**
		 * Takes the data which is in workflow scoope and copies it to the ThreadLocal element. 
		 * The ThreadLocal concept allows a static-like behavior in a multithreaded environment.
		 */
		public void moveToThreadLocalLastId()
		{
			SimpleProduct.setLastIdThreadLocal(lastId);
		}

}
