import stext;
import sexec;
import ecore;
import sgraph;
import types;

extension org::yakindu::sct::generator::cpp::templates::Naming;

toCppCode(Void void) :
	"/*toCppCode() called with NULL element. Polymorphic resolver could not match callable method!*/";
String toCppCode(Void o, String statechartReference) : 
	""; //polymorphic placeholder (abstract rule)

String toCppCode(Expression statement) : 
	null; //polymorphic placeholder (abstract rule)
 
String toCppCode(Statement statement) : 
	null; //polymorphic placeholder (abstract rule)

String toCppCode(Literal lit) :
   "Unknown Literal Type" ;

String toCppCode(BoolLiteral bLit) :
   (bLit.value == true) ? "bool_true" : "bool_false"; 

String toCppCode(IntLiteral iLit) :
	iLit.value.toString();
 
String toCppCode(RealLiteral rLit) :
	rLit.value.toString();

String toCppCode(HexLiteral hLit) :
	hLit.value.toString();

String toCppCode(PrimitiveValueExpression primValue) :
	primValue.value.toCppCode();

/* Assignment */
String toCppCode(AssignmentExpression assignment) :
  switch (assignment.operator) {
  case (AssignmentOperator::assign) : "set_" + assignment.varRef.name() + "(" + assignment.expression.toCppCode() + ");"  
  case (AssignmentOperator::multAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() * (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::divAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() / (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::modAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() % (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::addAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() + (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::subAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() - (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::leftShiftAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() << ("+ assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::rightShiftAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() >> (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::andAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() & (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::xorAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() ^ (" + assignment.expression.toCppCode() + "));"
  case (AssignmentOperator::orAssign) : "set_" + assignment.varRef.name() + "(get_"+ assignment.varRef.name()+ "() ^ (" + assignment.expression.toCppCode() + "));"
  default : ""
  };
		
/* EventRaising */

String toCppCode(EventRaisingExpression eventRaising) :
	((eventRaising.value == null)?"":
	"((" + eventRaising.event.name() + "*) handle->" + eventRaising.getInterfaceName() + "." + eventRaising.event.name() + ")->value = " 
	+ eventRaising.value.toCppCode() + ";") + 
	" statemachine_cy_setEvent(&handle->base, handle->" + eventRaising.getInterfaceName() + "." + eventRaising.event.name() + "); ";
//  "{ _Event* ev = eventPool_createEvent(handle->base.eventPool, ev_"+eventRaising.event.name+"); if (ev) { " + eventRaising.addValue() + "statemachine_cy_setEvent(&handle->base, ev); } }";

String getInterfaceName(EventRaisingExpression event) :
	"interface" + (( ((InterfaceScope)event.event.getEvent().eContainer).name == null)?"":((InterfaceScope)event.event.getEvent().eContainer).name.toFirstUpper());

EventDefinition getEvent(Expression e) : null;
EventDefinition getEvent(ecore::EObject e) : null;
EventDefinition getEvent(FeatureCall this) : feature.getEvent();
EventDefinition getEvent(ElementReferenceExpression this) : reference.getEvent();
EventDefinition getEvent(EventDefinition this) : this;

String addValue(EventRaisingExpression event) :
  ( (event.value == null)?"":("((" +event.event.name() + "*)ev)->value = " + event.value.toCppCode() + "; ") );



/* Logical Expressions */
String toCppCode(LogicalOrExpression expression) :
  	expression.leftOperand.toCppCode() + " || " + expression.rightOperand.toCppCode();
  	
String toCppCode(LogicalAndExpression expression) :
  	expression.leftOperand.toCppCode() + " && " + expression.rightOperand.toCppCode();

String toCppCode(LogicalNotExpression expression) :
  	" ^" + expression.operand.toCppCode();

String toCppCode(LogicalRelationExpression expression) :
   expression.leftOperand.toCppCode() + getOperator(expression.operator) + expression.rightOperand.toCppCode();
    
String toCppCode(BitwiseAndExpression expression) :
  	expression.leftOperand.toCppCode() + " & " + expression.rightOperand.toCppCode();

String toCppCode(BitwiseOrExpression expression) :
  	expression.leftOperand.toCppCode() + " | " + expression.rightOperand.toCppCode();

String toCppCode(BitwiseXorExpression expression) :
  	expression.leftOperand.toCppCode() + " ^ " + expression.rightOperand.toCppCode();

String toCppCode(NumericalAddSubtractExpression expression) :
  	expression.leftOperand.toCppCode() + getOperator(expression.operator) + expression.rightOperand.toCppCode();
  	
String toCppCode(NumericalMultiplyDivideExpression expression) :
  	expression.leftOperand.toCppCode() + getOperator(expression.operator) + expression.rightOperand.toCppCode();

// is this just relevant for events?
String toCppCode(ElementReferenceExpression ev) :
    " eventSet.check( ev_" + ev.reference.name().toLowerCase() + ") ";

String getOperator(AdditiveOperator operator) : 
    switch(operator) {
    	case(AdditiveOperator::plus) : " + "
    	case(AdditiveOperator::minus) : " - "
    	default : ""
    };

String getOperator(MultiplicativeOperator operator) : 
    switch(operator) {
    	case(MultiplicativeOperator::mul) : " * "
    	case(MultiplicativeOperator::div) : " / "
    	case(MultiplicativeOperator::mod) : " % "
    	default : ""
    };

String getOperator(RelationalOperator operator) :
	switch (operator) {
	  case (RelationalOperator::greater) : " > "
	  case (RelationalOperator::greaterEqual) : " >= "
	  case (RelationalOperator::smaller) : " < "
	  case (RelationalOperator::smallerEqual) : " <= "
	  case (RelationalOperator::equals) : " == "
	  case (RelationalOperator::notEquals) : " != "
	  default : ""
	};

String eventTypeToString(Type type) : 
  switch (type.name) {
  case null : "void"
  case "void" : "void"
  case "real" : "real"
  case "integer" : "integer"
  case "boolean" : "boolean"
  case "string"  : "strng"
  default : "unknownType"
  };
  
Set[EventDefinition] getInEvents(InterfaceScope interface) : 
	interface.declarations.typeSelect(EventDefinition).select(e|e.direction == Direction::IN);

Set[EventDefinition] getOutEvents(InterfaceScope interface) : 
	interface.declarations.typeSelect(EventDefinition).select(e|e.direction == Direction::OUT);

Set[EventDefinition] getLocalEvents(InterfaceScope interface) : 
	interface.declarations.typeSelect(EventDefinition).select(e|e.direction == Direction::LOCAL);
 
/* Timer handling */

 
	