/*******************************************************************************
 * 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.irtsaintexupery.fiacre.instance.fiacreinstance.BlockInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ComponentDeclarationInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.CompositionInstance
import com.irtsaintexupery.fiacre.instance.fiacreinstance.DeclarationInstance
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.MSC
import com.irtsaintexupery.fiacre.simulator.msc.msc.Site
import fr.irit.fiacre.xtext.fiacre.ComponentDeclaration
import fr.irit.fiacre.xtext.fiacre.CompositeBlock
import fr.irit.fiacre.xtext.fiacre.Composition
import fr.irit.fiacre.xtext.fiacre.InstanceDeclaration
import fr.irit.fiacre.xtext.fiacre.Model
import fr.irit.fiacre.xtext.fiacre.ParameterizedDeclaration
import fr.irit.fiacre.xtext.fiacre.ProcessDeclaration
import java.util.HashMap
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.resource.XtextResource
import org.json.simple.JSONArray
import org.json.simple.JSONAware
import org.json.simple.JSONObject

class StaticJsonExtractor { 
	var XtextResource fiacreResource
	var JSONObject fiacreStaticJson
	var Resource instanceResource
	var Resource mscResource
	var HashMap<BlockInstance, JSONAware> mapping
	// The constructor
	new(XtextResource _fiacreResource, JSONObject _fiacreStaticJson, Resource _instanceResource, Resource _mscResource) {
		fiacreResource = _fiacreResource
		fiacreStaticJson = _fiacreStaticJson
		instanceResource = _instanceResource
		mapping = new HashMap<BlockInstance, JSONAware>()
		mscResource = _mscResource
	}

	// This method extracts static information from the json model 
	// and creates the fiacre instance model
	def extract() {
		extractFiacreModel()
		if (fiacreStaticJson.get("arch_var") instanceof JSONObject){
			extractMainDeclaration(fiacreStaticJson.getJSONObject("arch_var"))	
		}
		else{
			for (element : fiacreStaticJson.get("arch_var") as JSONArray){
				extractMainDeclaration(element as JSONObject)	
			}
		}
		
		Utils.saveResource(instanceResource)
		Utils.saveResource(mscResource)
		}

	def extractFiacreModel() {
		//instanceModel.name = (fiacreModel.root as Declaration).name + "Instance"
		instanceModel.model = fiacreModel
	}

	def extractMainDeclaration(JSONObject jsonob) {
		val String name = jsonob.getJSONString("name")
		if(jsonob.component) {
			val ComponentDeclarationInstance cdi = Utils.createComponentDeclarationInstance
			instanceModel.instance.add(cdi)
			val ComponentDeclaration fcr_component = fiacreModel.declarations.filter[decl|decl.name==jsonob.get("name")].get(0) as ComponentDeclaration
			cdi.component = fcr_component
			cdi.name = fiacreModel.declarations.filter(ComponentDeclaration).filter[cd|cd.name == name].get(0).name
			jsonob.extractVariable(fcr_component, cdi)
			jsonob.getJSONArray("body").extractBody(cdi, fcr_component.body)
			mapping.put(cdi,jsonob)
			cdi.flatname = jsonob.getJSONString("flatname")
		} else {
			val ProcessDeclarationInstance pdi = Utils.createProcessDeclarationInstance
			instanceModel.instance.add(pdi)
			val ProcessDeclaration fcr_process = fiacreModel.declarations.filter[decl|decl.name==jsonob.get("name")].get(0) as ProcessDeclaration
			pdi.process = fcr_process
			pdi.name = fiacreModel.declarations.filter(ProcessDeclaration).filter[cd|cd.name == name].get(0).name
			jsonob.extractVariable(fcr_process, pdi)
			mapping.put(pdi,jsonob)
			pdi.flatname = jsonob.getJSONString("flatname")
			//msc part
			val Site site = Utils.createSite
			site.name = jsonob.getJSONString("flatname")
			MSCModel.sites.add(site)
		}
	}

	def void extractBody(JSONArray json_composition, BlockInstance instance, Composition fiacre_composition) {
		for (element : json_composition) {
			if(element instanceof JSONArray) {
				// It is a composition
				val CompositionInstance ci = Utils.createCompositionInstance
				instance.blockInstances.add(ci)
				ci.composition = ((fiacre_composition.blocks.get(json_composition.indexOf(element))) as CompositeBlock).composition
				element.extractBody(ci, ((((fiacre_composition.blocks.get(json_composition.indexOf(element))) as CompositeBlock).composition)))
			} else {
				if((element as JSONObject).component) {
					// It is a component instance
					val String name = (element as JSONObject).getJSONString("name")
					val ComponentDeclarationInstance cdi = Utils.createComponentDeclarationInstance
					instance.blockInstances.add(cdi)
					cdi.instance = (((fiacre_composition.blocks.get(json_composition.indexOf(element))) as InstanceDeclaration).instance)
					(element as JSONObject).getJSONArray("body").extractBody(cdi,
						((((fiacre_composition.blocks.get(json_composition.indexOf(element))) as InstanceDeclaration).instance.component) as ComponentDeclaration).body)
					cdi.component = fiacreModel.declarations.filter(ComponentDeclaration).filter [ cd |
						cd.name == name
					].get(0)
					cdi.name = (fiacreModel.declarations.filter(ComponentDeclaration).filter [ cd |
						cd.name == name
					].get(0).name)
					(element as JSONObject).extractVariable(fiacreModel.declarations.filter(ComponentDeclaration).filter[cd|cd.name == name].get(0), cdi)
					mapping.put(cdi,element as JSONObject)
					cdi.flatname = (element as JSONObject).getJSONString("flatname")
				} else {
					// It is a process instance
					val String name = (element as JSONObject).getJSONString("name")
					val ProcessDeclarationInstance pdi = Utils.createProcessDeclarationInstance
					pdi.instance = ((fiacre_composition.blocks.get(json_composition.indexOf(element))) as InstanceDeclaration).instance
					instance.blockInstances.add(pdi)
					pdi.process = fiacreModel.declarations.filter(ProcessDeclaration).filter [ cd |
						cd.name == name
					].get(0)
					pdi.name = (fiacreModel.declarations.filter(ProcessDeclaration).filter [ cd |
						cd.name == name
					].get(0).name)
					(element as JSONObject).extractVariable(fiacreModel.declarations.filter(ProcessDeclaration).filter[cd|cd.name == name].get(0), pdi)
					mapping.put(pdi,element as JSONObject)
					pdi.flatname = (element as JSONObject).getJSONString("flatname")
					//msc part
					val Site site = Utils.createSite
					site.name = (element as JSONObject).getJSONString("flatname")
					MSCModel.sites.add(site)
				}
			}
		}
	}

	def boolean isComponent(JSONObject jsonob) {
		if(jsonob.containsKey("body"))
			return true
		else
			return false
	}

	def extractVariable(JSONObject jsonob, ParameterizedDeclaration comp_decl, DeclarationInstance cdi) {
		for (jvar : jsonob.getJSONArray("vars")) {
			val String var_name = (jvar as JSONObject).getJSONString("sourcename")

			if(var_name.isVariableDeclaration(comp_decl)) {
				val VariableDeclarationInstance vi = Utils.createVariableDeclarationInstance
				cdi.variableDeclarationInstances.add(vi)
				vi.variableDeclaration = comp_decl.variables.map[vars|vars.variables].flatten.filter [ avar |
					avar.name == (jvar as JSONObject).getJSONString("sourcename")
				].get(0)
			}
		// else
		// vi.referenceDeclaration = comp_decl.parameters.map[vars|vars.parameters].flatten.filter[avar |avar.name == (jvar as JSONObject).getJSONString("sourcename")] .get(0)
		}
	}

	def boolean isVariableDeclaration(String name, ParameterizedDeclaration declaration) {
		if(declaration.variables.map[vars|vars.variables].flatten.map[avar|avar.name].filter [ aname |
			aname == name
		].size > 0)
			return true
		else
			return false
	}

	def getJSONObject(JSONObject jsonOb, String key) {
		jsonOb.get(key) as JSONObject
	}

	def getJSONString(JSONObject jsonOb, String key) {
		jsonOb.get(key) as String
	}

	def getJSONArray(JSONObject jsonOb, String key) {
		jsonOb.get(key) as JSONArray
	}

	def Model getFiacreModel() {
		return fiacreResource.contents.get(0) as Model
	}
	def MSC getMSCModel() {
		return mscResource.contents.get(0) as MSC
	}
	def InstanceModel getInstanceModel() {
		return instanceResource.contents.get(0) as InstanceModel
	}
	def HashMap<BlockInstance, JSONAware> getMapping(){
		return mapping
	}
}