/*******************************************************************************
 * 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
 *******************************************************************************/
 /*
 * generated by Xtext 2.10.0
 */
package fr.irit.fiacre.xtext.scoping

import fr.irit.fiacre.xtext.fiacre.ArrayExpression
import fr.irit.fiacre.xtext.fiacre.AssignStatement
import fr.irit.fiacre.xtext.fiacre.CaseStatement
import fr.irit.fiacre.xtext.fiacre.ComponentDeclaration
import fr.irit.fiacre.xtext.fiacre.ComponentInstance
import fr.irit.fiacre.xtext.fiacre.ConstantDeclaration
import fr.irit.fiacre.xtext.fiacre.Declaration
import fr.irit.fiacre.xtext.fiacre.FunctionDeclaration
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.ParameterizedDeclaration
import fr.irit.fiacre.xtext.fiacre.PortSet
import fr.irit.fiacre.xtext.fiacre.ProcessDeclaration
import fr.irit.fiacre.xtext.fiacre.PropertyDeclaration
import fr.irit.fiacre.xtext.fiacre.ReceiveStatement
import fr.irit.fiacre.xtext.fiacre.RecordType
import fr.irit.fiacre.xtext.fiacre.SelectStatement
import fr.irit.fiacre.xtext.fiacre.SendStatement
import fr.irit.fiacre.xtext.fiacre.StateSubject
import fr.irit.fiacre.xtext.fiacre.StatementSequence
import fr.irit.fiacre.xtext.fiacre.TagSubject
import fr.irit.fiacre.xtext.fiacre.TaggedStatement
import fr.irit.fiacre.xtext.fiacre.Type
import fr.irit.fiacre.xtext.fiacre.TypeDeclaration
import fr.irit.fiacre.xtext.fiacre.UnionType
import java.util.ArrayList
import java.util.Collection
import java.util.List
import org.eclipse.emf.common.util.TreeIterator
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EReference
import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.Scopes
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider

/**
 * This class contains custom scoping description.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
 * on how and when to use it.
 * 
 */
class FiacreScopeProvider extends AbstractDeclarativeScopeProvider {
	def IScope scope_StateDeclaration(ProcessDeclaration process, EReference ref) {
		//println("scope_StateDeclaration")
		buildStateDeclaration(process)
	}
/*
 * l'entete de la méthode
 * scope_EClass_Ereference()
 * EClass c'est la classe ou la super-classe
 * (comme IdentifierExpression et ConstructionExpression
 */
//	def IScope scope_ExpressionDeclarationUse(Model process, EReference ref) {
//		println("scope_ExpressionDeclarationUse")
//		buildExpressionDeclarationUse(process)
//	}

//	def IScope scope_ExpressionDeclarationUse(ComponentDeclaration component, EReference ref) {
//		println("scope_ExpressionDeclarationUse")
//		buildExpressionDeclarationUse(component)
//	}
//	def IScope scope_ReferencedType_type(ProcessDeclaration process, EReference ref) {
//		println("scope_ExpressionDeclarationUse")
//		buildExpressionDeclarationUseReferencedType(process)
//	}
	def IScope scope_TypeDeclarationUse(ProcessDeclaration process, EReference ref) {
		//println("scope_TypeDeclarationUse")
		buildTypeDeclarationUse(process)
	}

	def IScope scope_RecordAccessPattern_field(ProcessDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}

	def IScope scope_RecordAccessExpression_field(ProcessDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}
	
	def IScope scope_RecordAccessExpression_field(FunctionDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}
	def IScope scope_RecordAccessExpression_field(PropertyDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}
	
	def IScope scope_FieldExpression_field(ProcessDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}
	
	def IScope scope_FieldExpression_field(FunctionDeclaration process, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(process)
	}
	
	def IScope scope_FieldExpression_field(ComponentDeclaration component, EReference ref) {
		//println("scope_RecordFieldDeclarationUse")
		buildRecordFieldDeclarationUse(component)
	}

//	def IScope scope_ConstructionExpression_declaration(ComponentDeclaration component, EReference ref) {
//		println("scope_RecordFieldDeclarationUse")
//		build_ConstructionExpression_declaration(component)
//	}
//	def IScope build_ConstructionExpression_declaration(ComponentDeclaration declaration) {
//		val Collection<EObject> tags = new ArrayList<EObject>()
//		val Model model = eContainer(declaration, Model) as Model
//		println("model " + model)
//		for (d : model.declarations) {
//			if (d instanceof TypeDeclaration) {
//				val Type t = (d as TypeDeclaration).value
//				if (t instanceof UnionType) {
//					for (ut : (t as UnionType).tags) {
//						tags.addAll(ut.tags)
//					}
//				}
//			}
//		}
//		return Scopes.scopeFor(tags)
//	}
//	def IScope scope_ConstructionExpression_declaration(ConstructionExpression id, EReference ref) {
//		buildExpressionDeclarationUseInProcessDeclaration(id)
//	}
	def IScope scope_IdentifierExpression_declaration(IdentifierExpression id, EReference ref) {
		if (id.eContainer instanceof ComponentInstance) {
			//println("first if")
			if (((id.eContainer as ComponentInstance)).ports.contains(id)) {
				//println("second if")
				buildPortDeclarations(id)
			} else {
				if (((id.eContainer as ComponentInstance)).parameters.contains(id)) {
					//println("second if")
					buildParametersDeclarations(id)
				}
			}
		} else {
			if (id.eContainer instanceof ArrayExpression) {
				if (((id.eContainer as ArrayExpression).eContainer as ComponentInstance).ports.contains(
					id.eContainer)) {
					buildPortDeclarations(id)
				}
			} else {
				if (id.eContainer instanceof PortSet) {
					buildPortDeclarations(id)
				} else {
					buildExpressionDeclarationUseInProcessDeclaration(id)
				}

			}
		}
	}

	def IScope buildExpressionDeclarationUseInProcessDeclaration(IdentifierExpression expression) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		//println("econtainer" + eContainer(expression, Declaration))
		switch eContainer(expression, Declaration) {
			ProcessDeclaration: {
				val ProcessDeclaration process = eContainer(expression, ProcessDeclaration) as ProcessDeclaration
				for (pards : process.parameters) {
					tags.addAll(pards.parameters)
				}
				for (vards : process.variables) {
					tags.addAll(vards.variables)
				}
				val Model model = eContainer(expression, Model) as Model
				for (dec : model.declarations) {
					if (dec instanceof ConstantDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof FunctionDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof TypeDeclaration) {
						val Type t = (dec as TypeDeclaration).value
						if (t instanceof UnionType) {
							for (ts : (t as UnionType).tags) {
								tags.addAll(ts.tags)
							}
						}
					}
				}
			}
			ComponentDeclaration: {
				val Model model = eContainer(expression, Model) as Model
				val ComponentDeclaration component = eContainer(expression, ComponentDeclaration) as ComponentDeclaration
				for (pards : component.parameters) {
					tags.addAll(pards.parameters)
				}
				for (vards : component.variables) {
					tags.addAll(vards.variables)
				}
				for (dec : model.declarations) {
					if (dec instanceof ConstantDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof FunctionDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof TypeDeclaration) {
						val Type t = (dec as TypeDeclaration).value
						if (t instanceof UnionType) {
							for (ts : (t as UnionType).tags) {
								tags.addAll(ts.tags)
							}
						}
					}
				}
				
			}
			TypeDeclaration: {
				val Model model = eContainer(expression, Model) as Model
				for (dec : model.declarations) {
					if (dec instanceof ConstantDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof TypeDeclaration) {
						val Type t = (dec as TypeDeclaration).value
						if (t instanceof UnionType) {
							for (ts : (t as UnionType).tags) {
								tags.addAll(ts.tags)
							}
						}
					}
				}
				}
			// TODO à modifier en suivant le path
			PropertyDeclaration :{
				val Model model = eContainer(expression, Model) as Model
				for (dec : model.declarations) {
					if (dec instanceof ConstantDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof FunctionDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof TypeDeclaration) {
						val Type t = (dec as TypeDeclaration).value
						if (t instanceof UnionType) {
							for (ts : (t as UnionType).tags) {
								tags.addAll(ts.tags)
							}
						}
					}
				}
				for (process : model.declarations.filter(ProcessDeclaration)){
					for (vards: process.variables){
						tags.addAll(vards.variables)
					}
					for (vards: process.parameters){
						tags.addAll(vards.parameters)
					}
				}
			}
			FunctionDeclaration:{
				val Model model = eContainer(expression, Model) as Model
				val FunctionDeclaration function = eContainer(expression, FunctionDeclaration) as FunctionDeclaration
				for (dec : model.declarations) {
					if (dec instanceof ConstantDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof FunctionDeclaration) {
						tags.add(dec)
					}
					if (dec instanceof TypeDeclaration) {
						val Type t = (dec as TypeDeclaration).value
						if (t instanceof UnionType) {
							for (ts : (t as UnionType).tags) {
								tags.addAll(ts.tags)
							}
						}
					}
				}
				for (parameters : function.parameters) {
					tags.addAll(parameters.parameters)
					//println("read"+parameters.read)
					//println("write"+parameters.write)
				}
				for (params : function.variables){
					tags.addAll(params.variables)
				}
			}
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildParametersDeclarations(IdentifierExpression expression) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val Model model = eContainer(expression, Model) as Model
		val ComponentDeclaration component = eContainer(expression, ComponentDeclaration) as ComponentDeclaration
				for (pards : component.parameters) {
					tags.addAll(pards.parameters)
				}
				for (vards : component.variables) {
					tags.addAll(vards.variables)
		}
		for (d : model.declarations) {
			if (d instanceof TypeDeclaration) {
				val Type t = (d as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ut : (t as UnionType).tags) {
						tags.addAll(ut.tags)
					}
				}
			}
			if (d instanceof ConstantDeclaration) {
				tags.add(d)
			}
			if (d instanceof FunctionDeclaration) {
				tags.add(d)
			}
		}

		return Scopes.scopeFor(tags)
	}

	def IScope scope_IdentifierPattern_declaration(IdentifierPattern id, EReference ref) {
		switch id.eContainer {
			AssignStatement: {
				//println("assign")
				if ((id.eContainer as AssignStatement).patterns.contains(id)) {
					//println("assign2")
					buildIdentifierPatternParametersAndVariables(id)
				}

			}
			SendStatement: {
				if ((id.eContainer as SendStatement).port == id) {
					buildIdentifierPatternPorts(id)
				}
			}
			ReceiveStatement: {
				if ((id.eContainer as ReceiveStatement).port == id) {
					buildIdentifierPatternPorts(id)
				} else {
					if ((id.eContainer as ReceiveStatement).patterns.contains(id)) {
						buildIdentifierPatternParametersAndVariables(id)
					}
				}
			}
			StatementSequence: {
				buildIdentifierPatternPorts(id)
			}
			CaseStatement: {
				//println("case statement")
				buildIdentifierPatternForCaseStatement(id)
			}
		}
	}

	def IScope buildIdentifierPatternForCaseStatement(IdentifierPattern pattern) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val Model model = eContainer(pattern, Model) as Model
		for (d : model.declarations) {
			if (d instanceof TypeDeclaration) {
				val Type t = (d as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ut : (t as UnionType).tags) {
						tags.addAll(ut.tags)
					}
				}
			}
		}
		switch eContainer(pattern, Declaration){
			ProcessDeclaration:{
				val ProcessDeclaration process = eContainer(pattern, ProcessDeclaration) as ProcessDeclaration
				for (pds : process.ports) {
					tags.addAll(pds.ports)
				}				
			}
			FunctionDeclaration:{
				val FunctionDeclaration function = eContainer(pattern, FunctionDeclaration) as FunctionDeclaration
				for (parameters : function.parameters) {
					tags.addAll(parameters.parameters)
					//println("read"+parameters.read)
					//println("write"+parameters.write)
				}
				for (vars : function.variables) {
					tags.addAll(vars.variables)
				}		
			}
		}
		//println("tags"+tags)
		return Scopes.scopeFor(tags)
	}

	def buildIdentifierPatternPorts(IdentifierPattern pattern) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val ProcessDeclaration process_decl = eContainer(pattern, ProcessDeclaration) as ProcessDeclaration
		for (port_d : process_decl.ports) {
			//println("Scope PortDeclaration ProcessDeclaration")
			tags.addAll(port_d.ports)
		}
		for (port_d : process_decl.localPorts) {
			//println("Scope LocalPortDeclaration ProcessDeclaration")
			tags.addAll(port_d.ports)
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildIdentifierPatternParametersAndVariables(IdentifierPattern id) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val ProcessDeclaration process_decl = eContainer(id, ProcessDeclaration) as ProcessDeclaration
		for (psd : process_decl.parameters) {
			for (pard : psd.parameters) {
				tags.add(pard)
			}
		}
		for (vsd : process_decl.variables) {
			for (vard : vsd.variables) {
				tags.add(vard)
			}
		}
		val Model model = process_decl.eContainer as Model
		for (d : model.declarations) {
			if (d instanceof TypeDeclaration) {
				val Type t = (d as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ut : (t as UnionType).tags) {
						tags.addAll(ut.tags)
					}
				}
			}
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildPortDeclarations(IdentifierExpression id) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val ParameterizedDeclaration pard = eContainer(id, ParameterizedDeclaration) as ParameterizedDeclaration
		//println(pard)
		if (pard instanceof ComponentDeclaration) {
			for (eports : (pard as ComponentDeclaration).localPorts) {
				for (port : eports.ports) {
					tags.add(port)
				}
			}
			for (eports : (pard as ComponentDeclaration).ports) {
				for (port : eports.ports) {
					tags.add(port)
				}
			}
		} else {
			for (eports : (pard as ProcessDeclaration).ports) {
				for (port : eports.ports) {
					tags.add(port)
				}
			}
		}
		return Scopes.scopeFor(tags)
	}

	def static 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 IScope buildRecordFieldDeclarationUse(Declaration process) {
		val Collection<EObject> tags = new ArrayList<EObject>()
//		for (dec : process.generics) {
//			tags.add(dec)
//		}

		val Model model = process.eContainer as Model
//		for (d : model.getDeclarations()) {
//			if (d instanceof TypeDeclaration) {
//				val Type t = (d as TypeDeclaration).value
//				if (t instanceof RecordType) {
//					val RecordType u = t as RecordType
//					for (ts : u.fields) {
//						for (s : ts.fields) {
//							//println(s.getName() + " record ")
//						}
//						tags.addAll(ts.getFields())
//					}
//				}
//			}
//		}
		for (rt : model.eAllContents.toList.filter(RecordType))
		{
			for (ts : rt.fields) {
						for (s : ts.fields) {
							//println(s.getName() + " record ")
						}
						tags.addAll(ts.getFields())
					}
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildRecordFieldDeclarationUse(ComponentDeclaration component) {
		val Collection<EObject> tags = new ArrayList<EObject>()
//		for (dec : component.generics) {
//			tags.add(dec)
//		}

		val Model model = component.eContainer as Model
		for (d : model.getDeclarations()) {
			if (d instanceof TypeDeclaration) {
				val Type t = (d as TypeDeclaration).value
				if (t instanceof RecordType) {
					val RecordType u = t as RecordType
					for (ts : u.fields) {
						for (s : ts.fields) {
							//println(s.getName() + " record ")
						}
						tags.addAll(ts.getFields())
					}
				}
			}
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildPatternDeclarationUse(ProcessDeclaration pdec) {
		val Collection<EObject> tags = new ArrayList<EObject>()

		for (vd : pdec.variables) {
			//println("Scope VariableDeclaration ProcessDeclaration")
			tags.addAll(vd.variables)
		}
		for (port_d : pdec.ports) {
			//println("Scope PortDeclaration ProcessDeclaration")
			tags.addAll(port_d.ports)
		}
		for (port_d : pdec.localPorts) {
			//println("Scope LocalPortDeclaration ProcessDeclaration")
			tags.addAll(port_d.ports)
		}
//		for (VariablesDeclaration vd : pdec.getVariables()) {
//			System.out
//					.println("Scope VariableDeclaration in ProcessDeclaration");
//			for (VariableDeclaration s : vd.getVariables()) {
//				System.out.print(s.getName() + " ");
//			}
//			tags.addAll(vd.getVariables());
//		}
		for (vd : pdec.parameters) {
			//println("Scope ParameterDeclaration in ProcessDeclaration")
			tags.addAll(vd.parameters)
		}

		val Model model = pdec.eContainer as Model
		for (d : model.declarations) {
			if (d instanceof TypeDeclaration) {
				val Type t = (d as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ut : (t as UnionType).tags) {
						tags.addAll(ut.tags)
					}
				}
			}
		}
		return Scopes.scopeFor(tags)
	}

	def static List<EObject> eAllSelectStatement(EObject source) {
		val TreeIterator<EObject> contentIterator = source.eAllContents()
		val List<EObject> result = new ArrayList<EObject>()

		while (contentIterator.hasNext) {
			val EObject next = contentIterator.next
			if (next instanceof SelectStatement) {
				result.add(next)
			}
		}
		return result
	}

	def IScope buildStateDeclaration(ProcessDeclaration process) {
		return Scopes.scopeFor(process.getStates())
	}

	def IScope buildExpressionDeclarationUse(Model model) {
		val Collection<EObject> tags = new ArrayList<EObject>()
//		val Model model = d.eContainer as Model
		for (dec : model.declarations) {
			if (dec instanceof ConstantDeclaration) {
				tags.add(dec)
			}
			if (dec instanceof TypeDeclaration) {
				val Type t = (dec as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ts : (t as UnionType).tags) {
						tags.addAll(ts.tags)
					}
				}
			}
//			for (gd : d.generics) {
//				tags.add(gd)
//			}
		}

//		val ProcessDeclaration pdec = d as ProcessDeclaration
//		for (vd : pdec.parameters) {
//			println("Scope ParameterDeclaration in ProcessDeclaration")
//			tags.addAll(vd.parameters)
//		}
//		for (vd : pdec.variables) {
//			tags.addAll(vd.variables)
//		}
//
//		val List<EObject> e = eAllSelectStatement(pdec)
//		for (EObject ss : e) {
//			if ((ss as SelectStatement).index != null) {
//				tags.add((ss as SelectStatement).index)
//			}
//		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildExpressionDeclarationUse(ComponentDeclaration d) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val Model model = d.eContainer as Model
		for (dec : model.declarations) {
			if (dec instanceof ConstantDeclaration) {
				tags.add(dec)
			}
			if (dec instanceof TypeDeclaration) {
				val Type t = (dec as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ts : (t as UnionType).tags) {
						tags.addAll(ts.tags)
					}
				}
			}
//			for (gd : d.generics) {
//				tags.add(gd)
//			}
		}

		val ProcessDeclaration pdec = d as ProcessDeclaration
		for (vd : pdec.parameters) {
			//println("Scope ParameterDeclaration in ProcessDeclaration")
			tags.addAll(vd.parameters)
		}
		for (vd : pdec.variables) {
			tags.addAll(vd.variables)
		}

		val List<EObject> e = eAllSelectStatement(pdec)
		for (EObject ss : e) {
			if ((ss as SelectStatement).index != null) {
				tags.add((ss as SelectStatement).index)
			}
		}
		return Scopes.scopeFor(tags)
	}

	def IScope buildExpressionDeclarationUseReferencedType(ProcessDeclaration d) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val Model model = d.eContainer as Model
		for (dec : model.declarations) {
			if (dec instanceof TypeDeclaration) {
				val Type t = (dec as TypeDeclaration).value
				if (t instanceof UnionType) {
					for (ts : (t as UnionType).tags) {
						tags.addAll(ts.tags)
					}
				}
			}
		}

		return Scopes.scopeFor(tags)
	}

	def IScope buildTypeDeclarationUse(ProcessDeclaration p) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		val Model model = p.eContainer as Model
		for (dec : model.declarations) {
			if (dec instanceof TypeDeclaration) {
				tags.add(dec)
			}
		}

		return Scopes.scopeFor(tags)
	}

	def IScope scope_StateSubject_state(StateSubject ss, EReference ref) {
		//println("scope_StateDeclaration")
		buildStateSubject_state(ss)
	}

	def IScope buildStateSubject_state(StateSubject subject) {
		val Collection<EObject> tags = new ArrayList<EObject>()
		//println("statesubject " + subject.eContainer)
		val model = eContainer(subject, Model) as Model
		for (process : model.declarations.filter(ProcessDeclaration)) {
			tags.addAll(process.states)
		}
		return Scopes.scopeFor(tags)
	}
	def IScope scope_TagSubject_tag(TagSubject tags, EReference ref) {
		val model = eContainer(tags, Model) as Model
		return Scopes.scopeFor(model.eAllContents.toList.filter(TaggedStatement))
	}
		

}
