/*******************************************************************************
 * Copyright (c) 2015, 2016 CNRS, IRIT, IRT AESE (IRT Saint Exupéry).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the LGPL 3.0
 * which accompanies this distribution, and is available at
 * https://www.gnu.org/licenses/lgpl-3.0-standalone.html
 *
 * Contributors:
 *     Faiez Zalila - initial API and implementation and/or initial documentation
 *     Jorge Enrique Gutierrez Zuluaga
 *******************************************************************************/
package com.irtsaintexupery.fiacre.simulator.staticjson.extractor

import com.google.inject.Injector
import com.irtsaintexupery.fiacre.instance.fiacreinstance.BlockInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.DeclarationInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.EnabledStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ExecutableStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ExecutedStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.FireableStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceEnabledStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceExecutableStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceExecutedStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceFireableStatementBlock
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceModel
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ProcessDeclarationInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.VariableDeclarationInstance
import com.irtsaintexupery.fiacre.simulator.core.Utils
import com.irtsaintexupery.fiacre.simulator.msc.msc.EnterStateStep
import com.irtsaintexupery.fiacre.simulator.msc.msc.MSC
import com.irtsaintexupery.fiacre.simulator.msc.msc.SendStep
import com.irtsaintexupery.fiacre.simulator.msc.msc.Step
import com.irtsaintexupery.fiacre.simulator.msc.msc.SynchronisationStep
import fr.irit.fiacre.xtext.fiacre.ArrayExpression
import fr.irit.fiacre.xtext.fiacre.ArrayType
import fr.irit.fiacre.xtext.fiacre.BasicType
import fr.irit.fiacre.xtext.fiacre.BlockStatement
import fr.irit.fiacre.xtext.fiacre.CaseStatement
import fr.irit.fiacre.xtext.fiacre.ConditionalStatement
import fr.irit.fiacre.xtext.fiacre.ConstructionExpression
import fr.irit.fiacre.xtext.fiacre.Expression
import fr.irit.fiacre.xtext.fiacre.ExtendedConditionalStatement
import fr.irit.fiacre.xtext.fiacre.FalseLiteral
import fr.irit.fiacre.xtext.fiacre.FieldExpression
import fr.irit.fiacre.xtext.fiacre.ForeachStatement
import fr.irit.fiacre.xtext.fiacre.IdentifierExpression
import fr.irit.fiacre.xtext.fiacre.IdentifierPattern
import fr.irit.fiacre.xtext.fiacre.Model
import fr.irit.fiacre.xtext.fiacre.NaturalLiteral
import fr.irit.fiacre.xtext.fiacre.PortDeclaration
import fr.irit.fiacre.xtext.fiacre.QueueExpression
import fr.irit.fiacre.xtext.fiacre.QueueType
import fr.irit.fiacre.xtext.fiacre.RangeType
import fr.irit.fiacre.xtext.fiacre.ReceiveStatement
import fr.irit.fiacre.xtext.fiacre.RecordExpression
import fr.irit.fiacre.xtext.fiacre.RecordType
import fr.irit.fiacre.xtext.fiacre.ReferencedType
import fr.irit.fiacre.xtext.fiacre.SelectStatement
import fr.irit.fiacre.xtext.fiacre.SendStatement
import fr.irit.fiacre.xtext.fiacre.Statement
import fr.irit.fiacre.xtext.fiacre.StatementChoice
import fr.irit.fiacre.xtext.fiacre.StatementSequence
import fr.irit.fiacre.xtext.fiacre.TransitionSource
import fr.irit.fiacre.xtext.fiacre.TrueLiteral
import fr.irit.fiacre.xtext.fiacre.Type
import fr.irit.fiacre.xtext.fiacre.TypeDeclaration
import fr.irit.fiacre.xtext.fiacre.UnionTagDeclaration
import fr.irit.fiacre.xtext.fiacre.UnionType
import fr.irit.fiacre.xtext.fiacre.UnlessStatement
import fr.irit.fiacre.xtext.fiacre.ValueExpression
import fr.irit.fiacre.xtext.fiacre.VariablesDeclaration
import fr.irit.fiacre.xtext.fiacre.WhileStatement
import fr.irit.fiacre.xtext.ui.internal.XtextActivator
import fr.laas.libtts.TTSSimulator
import java.util.ArrayList
import java.util.HashMap
import java.util.LinkedHashMap
import java.util.Map
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.jface.text.BadLocationException
import org.eclipse.xtext.resource.EObjectAtOffsetHelper
import org.eclipse.xtext.resource.XtextResource
import org.eclipse.xtext.ui.editor.IURIEditorOpener
import org.eclipse.xtext.ui.editor.XtextEditor
import org.eclipse.xtext.ui.editor.model.XtextDocument
import org.json.simple.JSONArray
import org.json.simple.JSONAware
import org.json.simple.JSONObject

///Jorge Guiterrez 10/08/2016
class DynamicJsonExtractor{
	var XtextResource fiacreResource
	var Resource instanceResource
	var Resource mscResource
	val TTSSimulator tTSSimulator
	var HashMap<BlockInstance, JSONAware> mapping_blockinstance_jsonelement
	var JSONObject fiacreStaticJson
	val HashMap<String, ArrayList<String>> tr_gl_tr_ins = new HashMap<String, ArrayList<String>>();
	val HashMap<String, LinkedHashMap<EObject, String>> tr_in_stmts = new HashMap<String, LinkedHashMap<EObject, String>>();

	new(XtextResource _fiacreResource, Resource _instanceResource, TTSSimulator _TTSSimulator, JSONObject _fiacreStaticJson, HashMap<BlockInstance, JSONAware> _mapping, Resource _mscResource) {
		fiacreResource = _fiacreResource
		instanceResource = _instanceResource
		tTSSimulator = _TTSSimulator
		mapping_blockinstance_jsonelement = _mapping
		fiacreStaticJson = _fiacreStaticJson
		mscResource = _mscResource
		extractTransitionsMappings
	}
	def mscFirstStepExtract(){
		var Step step = Utils.createStep
		MSCModel.steps.add(step)
		for (pdi: instanceModel.eAllContents.toList.filter(ProcessDeclarationInstance)){
			var EnterStateStep esst = Utils.createEnterStateStep
			step.microSteps.add(esst)
			esst.stateName = pdi.currentState.name
			esst.site = MSCModel.sites.filter[site|site.name == pdi.flatname].get(0)	
		}
		Utils.saveResource(mscResource)	
	}
	def ArrayList<JSONObject> getallInstanceTransitionsObjects() {
		// Get all Instance transitions from the mapping between block instances and json object
		val ArrayList<JSONObject> allInstanceTransitions = new ArrayList<JSONObject>()
		for (element : ( mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].values.map[value|(value as JSONObject).get("transitions")])) {
			for (jelement : element as JSONArray) {
				allInstanceTransitions.add(jelement as JSONObject)
			}
		}
		allInstanceTransitions
	}

	def extractTransitionsMappings() {
		val Injector injector = XtextActivator.getInstance().getInjector("fr.irit.fiacre.xtext.Fiacre")
		val IURIEditorOpener editorOpener = injector.getInstance(IURIEditorOpener)
		val XtextEditor xtextEditor = editorOpener.open(fiacreResource.URI, true) as XtextEditor
		val XtextDocument xtextDocument = xtextEditor.document as XtextDocument
		val EObjectAtOffsetHelper eObjectAtOffsetHelper = injector.getInstance(EObjectAtOffsetHelper)
		// The following code line is just to show what performed inside mappings
		/* 
		 * It exists two kinds of transitions
		 * Global transitions: which can be find in the json model root
		 * and Instance transitions which can be find in the process instances
		 * So from global transitions, we must find the instance transitions
		 * to obtain, after, the locations for each transition 
		 */
		// Get global transitions from the json model
		val JSONArray global_transitions = fiacreStaticJson.get("gtransitions") as JSONArray

//		// Get all Instance transitions from the mapping between block instances and json object
//		val ArrayList<JSONObject> allInstanceTransitions = new ArrayList<JSONObject>()
//		for (element : ( mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].values.map[value|(value as JSONObject).get("transitions")])) {
//			for (jelement : element as JSONArray) {
//				allInstanceTransitions.add(jelement as JSONObject)
//			}
//		}
		// for each global transition name (a_global_tr_name)
		for (a_global_tr_name : global_transitions.map[agtr|(agtr as JSONObject).get("name")]) {

			// Get the corresponding json object (a_g_tr_jobject) from the global transitions array list
			val JSONObject a_g_tr_jobject = global_transitions.filter[gtr|(gtr as JSONObject).get("name") == a_global_tr_name].get(0) as JSONObject
			// create an array list of instance transition of the current global transition
			val ArrayList<String> in_trs_of_gl = new ArrayList<String>()

			// for each instance transition name, found in the transitions array of the global transition object
			for (a_i_tr_name : a_g_tr_jobject.get("transitions") as JSONArray) {
				// add the instance name in the array list
				in_trs_of_gl.add(a_i_tr_name as String)
				// Get the instance transition object (a_i_tr_jobject)
				val JSONObject a_i_tr_jobject = getallInstanceTransitionsObjects.filter[aatr|(aatr as JSONObject).get("name") == a_i_tr_name].get(0)

				// Get the Process Declaration Instance that contains the instance transition object (a_i_tr_jobject)
				// just to print it and verify 
				// val c_pdi = mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].filter[p1, p2|((p2 as JSONObject).get("transitions") as JSONArray).contains(a_i_tr_jobject)].keySet.get(0);
				// Get locations of each instance transition
				// statements_ins_tr allows to save an eobject (an atomic statement or a transition source)
				// with its first position
				// for example fr.irit.fiacre.xtext.fiacre.impl.TransitionSourceImpl@53c7c937 = 325 3
				val LinkedHashMap<EObject, String> statements_ins_tr = new LinkedHashMap<EObject, String>()

				// for each identified location in the locations table of an instance transition
				for (a_location_jsonobject : (a_i_tr_jobject as JSONObject).get("locations") as JSONArray) {
					// we find the from location
					val JSONObject from_location = (a_location_jsonobject as JSONObject).get("from") as JSONObject
					val long line_from = from_location.get("line") as Long
					val long char_from = from_location.get("char") as Long
					// we calculate the from offset
					val int offset_from = xtextDocument.getOffset(line_from.intValue, char_from.intValue)

					// we find the to location
					val JSONObject to_location = (a_location_jsonobject as JSONObject).get("to") as JSONObject
					val long line_to = to_location.get("line") as Long
					val long char_to = to_location.get("char") as Long
					// we calculate the to offset
					val int offset_to = xtextDocument.getOffset(line_to.intValue, char_to.intValue)
					// from to from offset to the to offset
					for (var int i = offset_from; i <= offset_to; i++) {
						// we calculate the line column position from the offset 
						val int line = xtextDocument.getLineOfOffset(i)
						val int column = (i - xtextDocument.getLineOffset(xtextDocument.getLineOfOffset(i)))

						// we find the corresponding statement
						val statement = eObjectAtOffsetHelper.resolveContainedElementAt(fiacreResource, i).getStatement
						// We insert the statements in the list with its line column location
						/*
						 * We insert only transition source (from)
						 * or atomic statement 
						 * In addition 
						 */
						if((statement instanceof TransitionSource) 
							//&& ! statements_ins_tr.keySet.contains(statement) 
							&& statements_ins_tr.keySet.filter(TransitionSource).size == 0
						)
							statements_ins_tr.put(statement, (line + 1) + " " + (column + 1))

						if(statement.isAtomicStatement)
							// TODO à voir l'histoire des to statement et les loop 
//							if(statement instanceof ToStatement){
//								if(statements_ins_tr.keySet.filter(ToStatement).size == 0)
//									statements_ins_tr.put(statement, (line + 1) + " " + (column + 1))
//							} 
//							else 
							// if(! statements_ins_tr.keySet.contains(statement))
							statements_ins_tr.put(statement, (line + 1) + " " + (column + 1))
					}
				}
				tr_in_stmts.put(a_i_tr_name as String, statements_ins_tr)
			}
			// mapping global transition = instance transitions. en gros, c'est gtransitions de json dynamique
			tr_gl_tr_ins.put(a_global_tr_name as String, in_trs_of_gl)
		}
	}

	def extractValues() {
		val HashMap<JSONObject, DeclarationInstance> variablesMappings = getVariablesMappings
		for (element : Utils.getJsonObject(tTSSimulator.state.getValues).keySet) {
			if(variablesMappings.filter[p1, p2|(p1 as JSONObject).get("flatname") == element].size > 0) {
				val JSONObject jo = variablesMappings.filter[p1, p2|(p1 as JSONObject).get("flatname") == element].keySet.get(0)
				val DeclarationInstance decl = variablesMappings.filter[p1, p2|(p1 as JSONObject).get("flatname") == element].values.get(0)
				val Object value = Utils.getJsonObject(tTSSimulator.state.getValues).get(element)
				val VariableDeclarationInstance vd_i = decl.variableDeclarationInstances.filter[vdi|vdi.variableDeclaration.name == jo.get("sourcename")].get(0)
				val HashMap<Object, Type> mapping_jsonType_fiacreType = getJSONTypeFiacreTypeMapping(jo.get("vartype"), (vd_i.variableDeclaration.eContainer as VariablesDeclaration).type)
				val HashMap<Object, Object> mapping_value_type = getValueTypeMapping(value, jo.get("vartype"))
				vd_i.currentValue = (getFiacreValue(value, mapping_value_type, mapping_jsonType_fiacreType) as ValueExpression)
			}
		}
	}
	/*
	 * This method allows to obtain a mapping between jsontype (from "vartype" of a json variable)
	 * and the fiacre type of this variable.
	 * It consists to do the optimization on fiacre types, in the ecore side, as done in the json side
	 * It can contain something like this
	 * bool = fr.irit.fiacre.xtext.fiacre.impl.BooleanTypeImpl@19569e44
	 * {"interval":{"high":3,"low":0}} = fr.irit.fiacre.xtext.fiacre.impl.RangeTypeImpl@3138f7b9
	 * {"record":{"val":"bool","active_probes":{"interval":{"high":3,"low":0}}}} = fr.irit.fiacre.xtext.fiacre.impl.RecordTypeImpl@1f72ed22
	 */
	def HashMap<Object, Type> getJSONTypeFiacreTypeMapping(Object json_type, Type fiacre_type) { 
		var HashMap<Object, Type> mapping_jsonType_fiacreType = new HashMap<Object, Type>()
		if(fiacre_type instanceof ReferencedType) {
			// If it is a referenced type, we get the the type of the referenced type 
			val referenced_fiacre_type = ((fiacre_type as ReferencedType).type as TypeDeclaration).value
			mapping_jsonType_fiacreType.putAll(getJSONTypeFiacreTypeMapping(json_type, referenced_fiacre_type))
		} else {
			if(fiacre_type instanceof BasicType) {
				mapping_jsonType_fiacreType.put(json_type, fiacre_type)
			} else {
				if(fiacre_type instanceof RangeType) {
					mapping_jsonType_fiacreType.put(json_type, fiacre_type)
				} else {
					if(fiacre_type instanceof QueueType) {
						// get the element type of the queue type, both in json and ecore
						mapping_jsonType_fiacreType.put(json_type, fiacre_type)
						val queue_json_element_type = ((json_type as JSONObject).get("queue") as JSONObject).get("elemtype")
						val queue_fiacre_element_type = (fiacre_type as QueueType).type
						mapping_jsonType_fiacreType.putAll(getJSONTypeFiacreTypeMapping(queue_json_element_type, queue_fiacre_element_type))
					} else {
						if(fiacre_type instanceof ArrayType) {
							// get the element type of the queue type, both in json and ecore
							mapping_jsonType_fiacreType.put(json_type, fiacre_type)
							val array_json_element_type = ((json_type as JSONObject).get("array") as JSONObject).get("elemtype")
							val array_fiacre_element_type = (fiacre_type as ArrayType).type
							mapping_jsonType_fiacreType.putAll(getJSONTypeFiacreTypeMapping(array_json_element_type, array_fiacre_element_type))
						} else {
							if(fiacre_type instanceof UnionType) {
								mapping_jsonType_fiacreType.put(json_type, fiacre_type)
								// get only typed union tags exemple: type xx is union toto | titi of int end
								// So we get only titi type which is int 
								for (json_tag : ((json_type as JSONObject).get("union") as JSONObject).entrySet) {
									if((json_tag.value as JSONArray).size > 0) {
										val tag_fiacre_type = (fiacre_type as UnionType).tags.filter[fs|fs.tags.map[f|f.name].exists[an|an == json_tag.key]].get(0).type
										val tag_json_type = (json_tag.value as JSONArray).get(0)
										mapping_jsonType_fiacreType.putAll(getJSONTypeFiacreTypeMapping(tag_json_type, tag_fiacre_type))
									}
								}
							} else {
								if(fiacre_type instanceof RecordType) {
									mapping_jsonType_fiacreType.put(json_type, fiacre_type)
									// get all fields types 
									for (json_field : ((json_type as JSONObject).get("record") as JSONObject).entrySet) {
										val field_json_type = json_field.value
										val field_fiacre_type = (fiacre_type as RecordType).fields.filter[fs|fs.fields.map[f|f.name].exists[an|an == json_field.key]].get(0).type
										mapping_jsonType_fiacreType.putAll(getJSONTypeFiacreTypeMapping(field_json_type, field_fiacre_type))
									}
								}
							}
						}

					}
				}
			}
		}
		mapping_jsonType_fiacreType
	}

	def Expression getFiacreValue(Object value, HashMap<Object, Object> mapping_value_type, HashMap<Object, Type> mapping_jsonType_fiacreType) {
		if(value instanceof Long) {
			val NaturalLiteral nl = Utils.createNaturalLiteral
			nl.value = (value as Long).intValue
			nl
		} else {
			if(value instanceof String) {
				if((value as String) == "false" || (value as String) == "true") {
					// Fiacre boolean type
					if((value as String) == "false") {
						val FalseLiteral fl = Utils.createFalseLiteral
						fl
					} else {
						val TrueLiteral tl = Utils.createTrueLiteral
						tl
					}
				} else {
					val UnionType union_type = mapping_jsonType_fiacreType.get(mapping_value_type.get(value)) as   UnionType
					// val TypeDeclaration typ_dec = model.declarations.filter(TypeDeclaration).filter[td|td.name == type_name].get(0)
					val UnionTagDeclaration tag = union_type.tags.map[tgs|tgs.tags].flatten.filter[atag|atag.name == value].get(0)
					val IdentifierExpression ide = Utils.createIdentifierExpression
					ide.declaration = tag
					ide
				}
			} else {
				if(value instanceof JSONArray) {
					if((mapping_value_type.get(value) as JSONObject).get("array") != null) {
						val ArrayExpression eae = Utils.createArrayExpression
						for (element : ( value as JSONArray)) {
							eae.values.add(getFiacreValue(element, mapping_value_type, mapping_jsonType_fiacreType))
						}
						eae
					} else {
						if((mapping_value_type.get(value) as JSONObject).get("queue") != null) {
							val QueueExpression qe = Utils.createQueueExpression
							for (element : ( value as JSONArray)) {
								qe.values.add(getFiacreValue(element, mapping_value_type, mapping_jsonType_fiacreType))
							}
							qe
						}
					}
				} else {
					// otherwise JSON Object
					if((mapping_value_type.get(value) as JSONObject).get("record") != null) {
						val RecordExpression re = Utils.createRecordExpression
						// val String type_name = (getFiacreType(mapping_value_type.get(value) as JSONObject) as JSONObject).get("name") as String
						val RecordType record_type = mapping_jsonType_fiacreType.get(mapping_value_type.get(value)) as RecordType //getFiacreType(mapping_value_type.get(value) as JSONObject) as RecordType
						// val TypeDeclaration typ_dec = model.declarations.filter(TypeDeclaration).filter[td|td.name == type_name].get(0)
						for (element : (value as JSONObject).keySet) {
							val FieldExpression fie = Utils.createFieldExpression
							re.fields.add(fie)
							fie.field = record_type.fields.map[fis|fis.fields].flatten.filter[atag|atag.name == element].get(0)
							fie.value = getFiacreValue((value as JSONObject).get(element), mapping_value_type, mapping_jsonType_fiacreType)
						}
						re
					} else {
						// an union element with constructor
						if((mapping_value_type.get(value) as JSONObject).get("union") != null) {
							val ConstructionExpression cstre = Utils.createConstructionExpression
							val UnionType union_type = mapping_jsonType_fiacreType.get(mapping_value_type.get(value)) as UnionType // model.declarations.filter(TypeDeclaration).filter[td|td.name == type_name].get(0)
							val UnionTagDeclaration tag = union_type.tags.map[tgs|tgs.tags].flatten.filter[atag|atag.name == (value as JSONObject).keySet.get(0)].get(0)
							cstre.declaration = tag
							cstre.parameters.add(getFiacreValue((value as JSONObject).values.get(0), mapping_value_type, mapping_jsonType_fiacreType))
							cstre
						}

					}
				}
			}
		}
	}

//	def Type getFiacreType(JSONObject type) {
//		println("types " + (fiacreStaticJson.get("deftyp") as JSONArray).filter[t|(t as JSONObject).get("tdef").equals(type)])
//		if((fiacreStaticJson.get("deftyp") as JSONArray).filter[t|(t as JSONObject).get("tdef").equals(type)].size == 1) {
//			val String type_name = ((fiacreStaticJson.get("deftyp") as JSONArray).filter[t|(t as JSONObject).get("tdef").equals(type)].get(0) as JSONObject).get("name") as String
//			val TypeDeclaration typ_dec = model.declarations.filter(TypeDeclaration).filter[td|td.name == type_name].get(0)
//			typ_dec.value
//		}
//	}

	def HashMap<Object, Object> getValueTypeMapping(Object value, Object type) {
		var HashMap<Object, Object> mapping_value_type = new HashMap<Object, Object>()
		if(value instanceof Long) {
			mapping_value_type.put(value as Object, type)
		} else {
			if(value instanceof String) {
				mapping_value_type.put(value as Object, type)
			} else {
				if(value instanceof JSONArray) {
					if((type as JSONObject).get("array") != null) {
						mapping_value_type.put(value, type)
						val length = (value as JSONArray).size
						for (var i = 0; i < length; i++) {
							// mapping_value_type.put((value as JSONArray).get(i), ((type as JSONObject).get("array") as JSONObject).get("elemtype"))
							mapping_value_type.putAll(getValueTypeMapping((value as JSONArray).get(i), ((type as JSONObject).get("array") as JSONObject).get("elemtype")))
						}
					} else {
						if((type as JSONObject).get("queue") != null) {
							mapping_value_type.put(value, type)
							val length = (value as JSONArray).size
							for (var i = 0; i < length; i++) {
								// mapping_value_type.put((value as JSONArray).get(i), ((type as JSONObject).get("queue") as JSONObject).get("elemtype"))
								mapping_value_type.putAll(getValueTypeMapping((value as JSONArray).get(i), ((type as JSONObject).get("queue") as JSONObject).get("elemtype")))
							}
						}
					}
				} else {
					// It is a JSONObject so It can be record value or union value
					if((type as JSONObject).get("record") != null) {
						mapping_value_type.put(value, type)
						for (ite : (value as JSONObject).keySet) {
							// mapping_value_type.put((value as JSONObject).get(ite), ((type as JSONObject).get("record") as JSONObject).get(ite))
							mapping_value_type.putAll(getValueTypeMapping((value as JSONObject).get(ite), ((type as JSONObject).get("record") as JSONObject).get(ite)))
						}
					} else {
						mapping_value_type.put(value, type)
						// mapping_value_type.put((value as JSONObject).values.get(0), ((type as JSONObject).get("union") as JSONObject).get((value as JSONObject).keySet.get(0)))
						mapping_value_type.putAll(getValueTypeMapping((value as JSONObject).values.get(0), (((type as JSONObject).get("union") as JSONObject).get((value as JSONObject).keySet.get(0)) as JSONArray).get(0)))

					}
				}
			}
		}
		mapping_value_type
	}

//	def getType(JSONObject vartype) {
//		(fiacreStaticJson.get("deftyp") as JSONArray).filter[atype|((atype as JSONObject).get("tdef") as JSONObject).equals(vartype) == true]
//	}

	def HashMap<JSONObject, DeclarationInstance> getVariablesMappings() {
		/*
		 * Ce mapping permet d'identifier le couple suivant:
		 * - une variable sous forme d'un object json 
		 * - La DeclarationInstance correspondante
		 * Il y a deux sources de redondance à résoudre
		 * D'abord une variable est instancié dans chacune des instances
		 * Ceci est resolu à travers la flatname qui contient le numéro d'instance 
		 * Le deuxième niveau de redondance est les paramètres qui portent les mêmes noms
		 * des variables. Ceci est résolu par le fait de vérifier s'il y a une variable instance qui
		 * a le même nom dans la declaration instance
		 */
		var HashMap<JSONObject, DeclarationInstance> map_var_job_cdi = new HashMap<JSONObject, DeclarationInstance>()
		val ArrayList<JSONObject> fiacreVariablesAsJsonObjects = new ArrayList<JSONObject>
		// Get all json objects of vars from the mappings
		// TODO why just flatten doesn't work
		for (element : ( mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof DeclarationInstance].values.map[value|(value as JSONObject).get("vars")])) {
			for (jelement : element as JSONArray) {
				fiacreVariablesAsJsonObjects.add(jelement as JSONObject)
			}
		}
		for (json_object_var : fiacreVariablesAsJsonObjects) {
			var ArrayList<DeclarationInstance> decls = new ArrayList<DeclarationInstance>
			for (decl : mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof DeclarationInstance].filter[p1, p2|((p2 as JSONObject).get("vars") as JSONArray).contains(json_object_var)].
				keySet) {
				decls.add(decl as DeclarationInstance)
			}
			if(decls.filter[decl|decl.variableDeclarationInstances.map[vdi|vdi.variableDeclaration].exists[v|v.name == json_object_var.get("sourcename")]].size > 0) {
				map_var_job_cdi.put(json_object_var, decls.filter[decl|decl.variableDeclarationInstances.map[vdi|vdi.variableDeclaration].exists[v|v.name == json_object_var.get("sourcename")]].get(0))
			}
		}
		map_var_job_cdi
	}

	def extractStates() {
		val ArrayList<JSONObject> allJsonStates = new ArrayList<JSONObject>

		// Get all json variables objects from the mappings
		// TODO why just flatten doesn't work
		for (element : ( mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].values.map[value|(value as JSONObject).get("states")])) {
			for (jelement : element as JSONArray) {
				allJsonStates.add(jelement as JSONObject)
			}
		}
		//msc

		
		for (place : tTSSimulator.currentPlaces) {
			// cette condition permet d'éliminer les états qui ne sont pas dans le modèle Fiacre
			// et qui sont ajoutés par le compilateur
			if(allJsonStates.filter[a|a.get("flatname") == place].size > 0) {
				// json_object_state is the concerned json object corresponding 
				// to the "place" name
				val JSONObject json_object_state = allJsonStates.filter[a|a.get("flatname") == place].get(0)
				// we find the c_pdi key whose the "states" json array of the corresponding value element 
				// contains the json_object_state
				val c_pdi = mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].filter [ p1, p2 |
					((p2 as JSONObject).get("states") as JSONArray).contains(json_object_state)
				].keySet.get(0);
				// c_pdi is the concerned process declaration instance where the state must be updated.
				// According to the c_pdi and json_object_state, we update the currentState reference of c_pdi
				(c_pdi as ProcessDeclarationInstance).currentState = (c_pdi as ProcessDeclarationInstance).process.states.filter[state|state.name == json_object_state.get("sourcename")].get(0)
			}
		}
	}

	def extractExecutedStatements(String executed_tr_name) {
		clearAllExecutedStatements
		val ExecutedStatementBlock esb = extractExecutedStatementsBlock(executed_tr_name)
		// frac compiler adds some intermediate transitions 
		// that don't exist in the static json description,
		// like in mouse_property example (__presentAfter__1_1_t1 transition)
		// Therefore for this global transition, it doesn't exist instance transitions
		// and the mapping doesn't exist for this transition
		// So, using this conditional statement, we eliminate this case 
		if(tr_gl_tr_ins.filter[p1, p2|p1 == executed_tr_name].values.size > 0) {
			// for each instance transition (instance_tr) of the global transition (tr_name)
			for (instance_tr : tr_gl_tr_ins.filter[p1, p2|p1 == executed_tr_name].values.get(0)) // add the created instance statement block in the current instanceStatementBlocks  
			{
				esb.instanceStatementBlocks.add(extractInstanceExecutedStatements(instance_tr))
			}
		}
		//msc
		//if it is sychronisation
		if(esb.instanceStatementBlocks.map[isb|isb.statements].flatten.exists[s|s instanceof IdentifierPattern && (s as IdentifierPattern).declaration instanceof PortDeclaration]){
			var Step step = Utils.createStep
			MSCModel.steps.add(step)
			var SynchronisationStep st = Utils.createSynchronisationStep
			step.microSteps.add(st)
			for (pdi : esb.instanceStatementBlocks.map[isb|isb.eContainer]){
				st.sites.add(MSCModel.sites.filter[site|site.name == (pdi as ProcessDeclarationInstance).flatname].get(0))
			}
			//
			var Step step2 = Utils.createStep
			MSCModel.steps.add(step2)
			for (pdi : esb.instanceStatementBlocks.map[isb|isb.eContainer]){
				var EnterStateStep esst = Utils.createEnterStateStep
				esst.stateName= (pdi as ProcessDeclarationInstance).currentState.name
				esst.site =MSCModel.sites.filter[site|site.name == (pdi as ProcessDeclarationInstance).flatname].get(0)
				step2.microSteps.add(esst)
			}
		}
		else{
			if((esb.instanceStatementBlocks.map[isb|isb.statements].flatten.exists[s|s instanceof SendStatement])){
				var Step step = Utils.createStep
				MSCModel.steps.add(step)
				var SendStep st = Utils.createSendStep
				step.microSteps.add(st)
			for (ss : esb.instanceStatementBlocks.map[isb|isb.statements].flatten.filter(SendStatement)){
				st.senderSites.add(MSCModel.sites.filter[site|site.name == (esb.instanceStatementBlocks.filter[isb|isb.statements.contains(ss)].get(0).eContainer as ProcessDeclarationInstance).flatname].get(0))
			}
			for (ss : esb.instanceStatementBlocks.map[isb|isb.statements].flatten.filter(ReceiveStatement)){
				st.receiverSites.add(MSCModel.sites.filter[site|site.name == (esb.instanceStatementBlocks.filter[isb|isb.statements.contains(ss)].get(0).eContainer as ProcessDeclarationInstance).flatname].get(0))
			}
			//
			var Step step2 = Utils.createStep
			MSCModel.steps.add(step2)
			for (pdi : esb.instanceStatementBlocks.map[isb|isb.eContainer]){
				var EnterStateStep esst = Utils.createEnterStateStep
				esst.stateName= (pdi as ProcessDeclarationInstance).currentState.name
				esst.site =MSCModel.sites.filter[site|site.name == (pdi as ProcessDeclarationInstance).flatname].get(0)
				step2.microSteps.add(esst)
			}
			}
			else{
				var Step step = Utils.createStep
				MSCModel.steps.add(step)
				for (pdi : esb.instanceStatementBlocks.map[isb|isb.eContainer]){
					var EnterStateStep esst= Utils.createEnterStateStep
					esst.site = MSCModel.sites.filter[site|site.name == (pdi as ProcessDeclarationInstance).flatname].get(0)
					esst.stateName = (pdi as ProcessDeclarationInstance).currentState.name
					step.microSteps.add(esst)
				}
			}
		}
		Utils.saveResource(mscResource)	
	}

	def extractInstanceExecutedStatements(String instance_tr_name) {
		// gel the instance transition json object whose the name is equal to instance_tr_name 
		val JSONObject a_i_tr_jobject = getallInstanceTransitionsObjects.filter[aatr|(aatr as JSONObject).get("name") == instance_tr_name].get(0)
		// Get the Process Declaration Instance that contains the instance transition object (a_i_tr_jobject)
		val ProcessDeclarationInstance c_pdi = mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].filter [ p1, p2 |
			((p2 as JSONObject).get("transitions") as JSONArray).contains(a_i_tr_jobject)
		].keySet.get(0) as ProcessDeclarationInstance;
		// the following if then else allows to forbid duplicate instance statements blocks
		// If the process declaration instance contains a block which have the same name
		if(c_pdi.instanceStatementBlocks.filter(InstanceExecutedStatementBlock).filter[ifst|ifst.name == instance_tr_name].size > 0) {
			// so, return this found block (this block will not be added again because the list doesn't accept duplicates) 
			c_pdi.instanceStatementBlocks.filter(InstanceExecutedStatementBlock).filter[ifst|ifst.name == instance_tr_name].get(0)
		} else {
			// there is no block with this name, so create a new block
			val InstanceExecutedStatementBlock block = Utils.createInstanceExecutedStatementBlock
			block.name = instance_tr_name
			// get the LinkedHashMap that contains the statements of this instance transition
			for (element : tr_in_stmts.filter[p1, p2|p1 == instance_tr_name].values.get(0).keySet) {
				// and for each element 
				// add it in the appropriate location
				if(element instanceof TransitionSource) {
					block.transitionSource = element
				} else {
					block.statements.add(element as Statement)
				}
			}
			c_pdi.instanceStatementBlocks.add(block)
			block
		}
	}

	def clearAllExecutableStatements() {
		// delete all executable block statements in the instance model
		instanceModel.statementBlocks.removeAll(instanceModel.statementBlocks.filter(ExecutableStatementBlock))
		// delete all instance executable block statements in different process declaration instance
		instanceModel.eAllContents.toList.filter(ProcessDeclarationInstance).forEach[pdi|pdi.instanceStatementBlocks.removeAll(pdi.instanceStatementBlocks.filter(InstanceExecutableStatementBlock))]
	}

	def clearAllExecutedStatements() {
		// delete all executable block statements in the instance model
		instanceModel.statementBlocks.removeAll(instanceModel.statementBlocks.filter(ExecutedStatementBlock))
		// delete all instance executable block statements in different process declaration instance
		instanceModel.eAllContents.toList.filter(ProcessDeclarationInstance).forEach[pdi|pdi.instanceStatementBlocks.removeAll(pdi.instanceStatementBlocks.filter(InstanceExecutedStatementBlock))]
	}

	def ExecutedStatementBlock extractExecutedStatementsBlock(String transition_name) {
		val ExecutedStatementBlock esb = Utils.createExecutedStatementBlock
		esb.name = transition_name
		instanceModel.statementBlocks.add(esb)
		esb
	}

	def extractExecutableStatements() {
		clearAllExecutableStatements
		// this for statement allows to iterate in a map 
		for (Map.Entry<String, String> entry : tTSSimulator.enabledTransitions.entrySet()) {
			val String tr_name = entry.getKey()
			val String tr_interval = entry.getValue()
			// for each (tr_name, tr_interval), we create an EnabledStatementBlock
			val EnabledStatementBlock esb = extractEnabledStatements(tr_name, tr_interval)
			// frac compiler adds some intermediate transitions 
			// that don't exist in the static json description,
			// like in mouse_property example (__presentAfter__1_1_t1 transition)
			// Therefore for this global transition, it doesn't exist instance transitions
			// and the mapping doesn't exist for this transition
			// So, using this conditional statement, we eliminate this case 
			if(tr_gl_tr_ins.filter[p1, p2|p1 == tr_name].values.size > 0) {
				// for each instance transition (instance_tr) of the global transition (tr_name)
				for (instance_tr : tr_gl_tr_ins.filter[p1, p2|p1 == tr_name].values.get(0)) {
					// add the created instance statement block in the current instanceStatementBlocks  	 				
					esb.instanceStatementBlocks.add(extractInstanceEnabledStatements(instance_tr))
				}
			}
		}
		// Idem for fireable
		for (Map.Entry<String, String> entry : tTSSimulator.fireableTransitions.entrySet()) {
			val String tr_name = entry.getKey()
			val String tr_interval = entry.getValue()
			val FireableStatementBlock fsb = extractFireableStatements(tr_name, tr_interval)
			// frac compiler adds some intermediate transitions 
			// that don't exist in the static json description,
			// like in mouse_property example (__presentAfter__1_1_t1 transition)
			// Therefore for this global transition, it doesn't exist instance transitions
			// and the mapping doesn't exist for this transition
			// So, using this conditional statement, we eliminate this case 
			if(tr_gl_tr_ins.filter[p1, p2|p1 == tr_name].values.size > 0) {
				for (instance_tr : tr_gl_tr_ins.filter[p1, p2|p1 == tr_name].values.get(0)) {
					fsb.instanceStatementBlocks.add(extractInstanceFireableStatements(instance_tr))
				}
			}
		}
	}

	def extractInstanceFireableStatements(String instance_tr_name) {
		// gel the instance transition json object whose the name is equal to instance_tr_name 
		val JSONObject a_i_tr_jobject = getallInstanceTransitionsObjects.filter[aatr|(aatr as JSONObject).get("name") == instance_tr_name].get(0)
		// Get the Process Declaration Instance that contains the instance transition object (a_i_tr_jobject)
		val ProcessDeclarationInstance c_pdi = mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].filter [ p1, p2 |
			((p2 as JSONObject).get("transitions") as JSONArray).contains(a_i_tr_jobject)
		].keySet.get(0) as ProcessDeclarationInstance;
		// the following if then else allows to forbid duplicate instance statements blocks
		// If the process declaration instance contains a block which have the same name
		if(c_pdi.instanceStatementBlocks.filter(InstanceFireableStatementBlock).filter[ifst|ifst.name == instance_tr_name].size > 0) {
			// so, return this found block (this block will not be added again because the list doesn't accept duplicates) 
			c_pdi.instanceStatementBlocks.filter(InstanceFireableStatementBlock).filter[ifst|ifst.name == instance_tr_name].get(0)
		} else {
			// there is no block with this name, so create a new block
			val InstanceFireableStatementBlock block = Utils.createInstanceFireableStatementBlock
			block.name = instance_tr_name
			// get the LinkedHashMap that contains the statements of this instance transition
			for (element : tr_in_stmts.filter[p1, p2|p1 == instance_tr_name].values.get(0).keySet) {
				// and for each element 
				// add it in the appropriate location
				if(element instanceof TransitionSource) {
					block.transitionSource = element
				} else {
					block.statements.add(element as Statement)
				}
			}
			c_pdi.instanceStatementBlocks.add(block)
			block
		}
	}

	// Idem 
	def extractInstanceEnabledStatements(String instance_tr_name) {
		// Get the Process Declaration Instance that contains the instance transition object (a_i_tr_jobject)
		val JSONObject a_i_tr_jobject = getallInstanceTransitionsObjects.filter[aatr|(aatr as JSONObject).get("name") == instance_tr_name].get(0)
		val ProcessDeclarationInstance c_pdi = mapping_blockinstance_jsonelement.filter[p1, p2|p1 instanceof ProcessDeclarationInstance].filter [ p1, p2 |
			((p2 as JSONObject).get("transitions") as JSONArray).contains(a_i_tr_jobject)
		].keySet.get(0) as ProcessDeclarationInstance;
		if(c_pdi.instanceStatementBlocks.filter(InstanceEnabledStatementBlock).filter[ifst|ifst.name == instance_tr_name].size > 0) {
			c_pdi.instanceStatementBlocks.filter(InstanceEnabledStatementBlock).filter[ifst|ifst.name == instance_tr_name].get(0)
		} else {
			val InstanceEnabledStatementBlock block = Utils.createInstanceEnabledStatementBlock
			block.name = instance_tr_name
			for (element : tr_in_stmts.filter[p1, p2|p1 == instance_tr_name].values.get(0).keySet) {
				if(element instanceof TransitionSource) {
					block.transitionSource = element
				} else {
					block.statements.add(element as Statement)
				}
			}
			c_pdi.instanceStatementBlocks.add(block)
			block

		}
	}

	def EnabledStatementBlock extractEnabledStatements(String transition_name, String interval) {
		val EnabledStatementBlock esb = Utils.createEnabledStatementBlock
		esb.name = transition_name
		esb.date = interval
		instanceModel.statementBlocks.add(esb)
		esb
	}

	def FireableStatementBlock extractFireableStatements(String transition_name, String interval) {
		val FireableStatementBlock fsb = Utils.createFireableStatementBlock
		fsb.name = transition_name
		fsb.date = interval
		instanceModel.statementBlocks.add(fsb)
		fsb
	}

	def int getOffset(XtextDocument xtextDocument, int line, int character) {
		var int offset = character
		for (var i = 0; i < line; i++) {
			try {
				offset += xtextDocument.getLineLength(i)
			} catch(BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace
			}
		}
		return offset
	}

	def EObject eContainer(EObject root, Class<?> type) {
		var EObject current = root
		while(current != null) {
			if(type.isInstance(current)) {
				return current
			} else {
				current = current.eContainer
			}
		}
		return null
	}

//	def EObject eContainerStatement(EObject root) {
//		var EObject current = root
//		while(current != null) {
//			if(StatementSequence.isInstance(current.eContainer())) {
//				return current
//			} else {
//				current = current.eContainer
//			}
//		}
//		return null;
//	}
	def EObject getStatement(EObject root) {
		var EObject current = root
		while(current != null) {
			if(!(isAtomicStatement(current.eContainer))) {
				return current
			} else {
				current = current.eContainer
			}
		}
		return null
	}

	def boolean isAtomicStatement(EObject root) {
		if(eContainer(root, Statement) != null) {
			if(UnlessStatement.isInstance(root) || StatementChoice.isInstance(root) || StatementSequence.isInstance(root) || ConditionalStatement.isInstance(root) ||
				ExtendedConditionalStatement.isInstance(root) || SelectStatement.isInstance(root) || WhileStatement.isInstance(root) || ForeachStatement.isInstance(root) ||
				CaseStatement.isInstance(root) || BlockStatement.isInstance(root)) {
				return false
			} else {
				return true
			}

		} else {
			return false
		}
	}

	def InstanceModel getInstanceModel() {
		return instanceResource.contents.get(0) as InstanceModel
	}
	def MSC getMSCModel() {
		return mscResource.contents.get(0) as MSC
	}
	
	def Model getModel() {
		return fiacreResource.contents.get(0) as Model
	}

	def saveInstanceResource() {
		Utils.saveResource(instanceResource)
	}
	def saveMscResource() {
		Utils.saveResource(mscResource)
	}

	def Resource getInstanceResource() {
		instanceResource
	}

	// this method just to print to detect errors
	def println_elements(XtextResource xtextResource, XtextDocument xtextDocument) {
		for (var i = 0; i < xtextDocument.getNumberOfLines(); i++) {
			try {
				print("Line " + i + " Line length " + xtextDocument.getLineLength(i))
				println(" Line " + i + " LineOffset " + xtextDocument.getLineOffset(i))
			} catch(BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		for (var i = 0; i < xtextDocument.getLength(); i++) {
			val Injector injector = XtextActivator.getInstance().getInjector("fr.irit.fiacre.xtext.Fiacre")
			val EObjectAtOffsetHelper eObjectAtOffsetHelper = injector.getInstance(EObjectAtOffsetHelper)
			try {
				val EObject object = eObjectAtOffsetHelper.resolveElementAt(xtextResource, i)
				val EObject object2 = eObjectAtOffsetHelper.resolveContainedElementAt(xtextResource, i)
				val EObject object3 = eObjectAtOffsetHelper.resolveCrossReferencedElementAt(xtextResource, i)
				val int line = xtextDocument.getLineOfOffset(i)
				val int column = (i - xtextDocument.getLineOffset(xtextDocument.getLineOfOffset(i)))
				if(i != xtextDocument.getOffset(line, column)) {
					println("error")
				}
				println("----- ")
				print("o " + i + " offset " + xtextDocument.getOffset(line, column) + " line of offset " + xtextDocument.getLineOfOffset(i))
				print(" (l " + line + " ")
				print("c " + column + ") ")
				print("(el = " + (line + 1) + ", ")
				println("ec = " + (column + 1) + ") ")
				println("object " + object)
				println("cross " + object3)
				println("contained " + object2)
				println("containerStatement " + getStatement(object2))

			} catch(BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	def gettr_gl_tr_ins(){
		tr_gl_tr_ins
	}
}