/*******************************************************************************
 * 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.validation

import fr.irit.fiacre.xtext.fiacre.AssignAnyStatement
import fr.irit.fiacre.xtext.fiacre.BlockStatement
import fr.irit.fiacre.xtext.fiacre.FiacrePackage
import fr.irit.fiacre.xtext.fiacre.FunctionDeclaration
import fr.irit.fiacre.xtext.fiacre.LoopStatement
import fr.irit.fiacre.xtext.fiacre.OnStatement
import fr.irit.fiacre.xtext.fiacre.ParametersDeclaration
import fr.irit.fiacre.xtext.fiacre.ReturnStatement
import fr.irit.fiacre.xtext.fiacre.SelectStatement
import fr.irit.fiacre.xtext.fiacre.TaggedStatement
import fr.irit.fiacre.xtext.fiacre.WaitStatement
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.validation.Check

/**
 * This class contains custom validation rules. 
 *
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation
 */
class FiacreValidator extends AbstractFiacreValidator {
	
  public static val INVALID_NAME = 'invalidName'
//
	@Check
	def checkNotReadAttributeInParametersDeclarationOfFunctionDeclaration(ParametersDeclaration parameters) {
		if (parameters.read) {
			//println("read econtainer"+ eContainer(parameters, Declaration))
			//println(eContainer(parameters, FunctionDeclaration))
			if(eContainer(parameters, FunctionDeclaration) != null){
				error('Not allowed in this scope', 
					FiacrePackage.eINSTANCE.parametersDeclaration_Read)
					
			}
		}
	}
	
	@Check
	def checkNotWriteAttributeInParametersDeclarationOfFunctionDeclaration(ParametersDeclaration parameters) {
		if (parameters.write) {
			//println("read econtainer"+ eContainer(parameters, Declaration))
			//println(eContainer(parameters, FunctionDeclaration))
			if(eContainer(parameters, FunctionDeclaration) != null){
				error('Not allowed in this scope', 
					FiacrePackage.eINSTANCE.parametersDeclaration_Write)
					
			}
		}
	}
	
	@Check
	def checkFunctionBodyStatement(FunctionDeclaration function) {
	 if (!(function.statement instanceof BlockStatement || function.statement instanceof ReturnStatement)){
	 	error('a function statement must be either a return statement or a block statement', 
					FiacrePackage.eINSTANCE.functionDeclaration_Statement)
	 }
	 
	 // not allowed statements in function
	  /*
	   * TaggedStatement
	   * AssignAnyStatement
	   * SelectStatement
	   * ToStatement 
	   * LoopStatement 
	   * IdentifierPattern
	   * SendStatement
	   * ReceiveStatement
	   * WaitStatement 
	   * OnStatement
	   */
	 
	}
	// TaggedStatement
	@Check
	def checkNoTaggedStatementInFunctionDeclaration(TaggedStatement taggedStatement) {
			if(eContainer(taggedStatement, FunctionDeclaration) != null){
				//println(taggedStatement)
				messageAcceptor.acceptError("A tag is not allowed in a function",taggedStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	
	//AssignAnyStatement
	@Check
	def checkNoAssignAnyStatementInFunctionDeclaration(AssignAnyStatement assignAnyStatement) {
		//println("messageAcceptor " + messageAcceptor);
			if(eContainer(assignAnyStatement, FunctionDeclaration) != null){
				messageAcceptor.acceptError("Assigning any value not allowed in a function ",assignAnyStatement,null,INSIGNIFICANT_INDEX, null)
			}
	}
	
	//SelectStatement
	@Check
	def checkNoSelectStatementInFunctionDeclaration(SelectStatement selectStatement) {

			if(eContainer(selectStatement, FunctionDeclaration) != null){
				messageAcceptor.acceptError("Non-deterministic choices are not allowed in a function",selectStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	//ToStatement
	/*
	 * It is checked automatically using the scopeProvider mechanism.
	 * A To statement requires referring to a state declared in the concerned declaration, 
	 * but in a function, states can't be declared. So, user can't write To statements
	 * Idem to:
	 * IdentifierPattern
	 * SendStatement
	 * ReceiveStatement
	 */
	 
	 //LoopStatement
	 @Check
	def checkNoLoopStatementInFunctionDeclaration(LoopStatement loopStatement) {

			if(eContainer(loopStatement, FunctionDeclaration) != null){
				messageAcceptor.acceptError("Loop statement is not allowed in a function",loopStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	
	//WaitStatement
	 @Check
	def checkNoWaitStatementInFunctionDeclaration(WaitStatement waitStatement) {

			if(eContainer(waitStatement, FunctionDeclaration) != null){
				messageAcceptor.acceptError("Wait statement is not allowed in a function",waitStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	
	//OnStatement
	 @Check
	def checkNoOnStatementInFunctionDeclaration(OnStatement onStatement) {

			if(eContainer(onStatement, FunctionDeclaration) != null){
				messageAcceptor.acceptError("Guards are not allowed in a function",onStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	
	//ReturnStatement
	@Check
	def checkNoReturnStatementOutsideFunctionDeclaration(ReturnStatement returnStatement) {
			if(eContainer(returnStatement, FunctionDeclaration) == null){
				messageAcceptor.acceptError("A return statement is only allowed in a function",returnStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	
	//BlockStatement
	@Check
	def checkNoBlockStatementInFunctionDeclaration(BlockStatement blockStatement) {
			if(eContainer(blockStatement, FunctionDeclaration) == null){
				messageAcceptor.acceptError("A block statement (begin ... end) is only allowed in a function",blockStatement,null,INSIGNIFICANT_INDEX, null)	
			}
	}
	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
	}
}
