/**
  *
  *                      OOAS Compiler
  *
  *       Copyright 2015, AIT Austrian Institute of Technology.
  * This code is based on the C# Version of the OOAS Compiler, which is
  * copyright 2015 by the Institute of Software Technology, Graz University
  * of Technology with portions copyright by the AIT Austrian Institute of
  * Technology. All rights reserved.
  *
  * SEE THE "LICENSE" FILE FOR THE TERMS UNDER WHICH THIS FILE IS PROVIDED.
  *
  * If you modify the file please update the list of contributors below to in-
  * clude your name. Please also stick to the coding convention of using TABs
  * to do the basic (block-level) indentation and spaces for anything after
  * that. (Enable the display of special chars and it should be pretty obvious
  * what this means.) Also, remove all trailing whitespace.
  *
  * Contributors:
  *               Willibald Krenn (AIT)
  *               Stephan Zimmerer (AIT)
  *               Markus Demetz (AIT)
  *               Christoph Czurda (AIT)
  *
  */


package org.momut.ooas.visitors;

import org.momut.ooas.ast.AstNodeTypeEnum;
import org.momut.ooas.ast.IAst;
import org.momut.ooas.ast.expressions.Expression;
import org.momut.ooas.ast.expressions.ObjectConstructor;
import org.momut.ooas.ast.expressions.UnaryOperator;
import org.momut.ooas.ast.identifiers.AttributeIdentifier;
import org.momut.ooas.ast.identifiers.ExpressionVariableIdentifier;
import org.momut.ooas.ast.identifiers.Identifier;
import org.momut.ooas.ast.identifiers.LocalVariableIdentifier;
import org.momut.ooas.ast.identifiers.ParameterIdentifier;
import org.momut.ooas.ast.types.FunctionType;
import org.momut.ooas.ast.types.ListType;
import org.momut.ooas.ast.types.MapType;
import org.momut.ooas.ast.types.OpaqueType;
import org.momut.ooas.ast.types.TupleType;
import org.momut.ooas.ast.types.TypeKind;
import org.momut.ooas.ast.types.Type;
import org.momut.ooas.parser.ParserState;
import org.momut.ooas.utils.exceptions.NotImplementedException;

/// <summary>
/// Replaces all Opaque types (types we could not resolve in the first run of the parser)
/// with the resolved ones. Note: The actual resolving has already been done, i.e., the
/// resolvedType member is already set to the correct value. Now we just want to get an
/// AST that does not contain any OpaqueTypeNodes at all.
/// </summary>
public final class OoaReplaceOpaqueVisitor extends OoaCompleteAstTraversalVisitor
{

	private void ReplaceOpaque(IAst parent)
	{
		switch (parent.nodeType())
		{
		case type:
			ReplaceOpaqueInType((Type)parent);
			break;
		case identifier:
			ReplaceOpaqueInIdentifier((Identifier)parent);
			break;
		case expression:
			ReplaceOpaqueInExpression((Expression)parent);
			break;
		default:
			throw new NotImplementedException();
		}
	}


	private void ReplaceOpaqueInExpression(Expression expression)
	{
		switch (expression.kind())
		{
		case ObjectConstr:
			ObjectConstructor c = (ObjectConstructor)expression;
			c.SetType(((OpaqueType)c.type()).resolvedType());
			break;
		case Cast:
			UnaryOperator unop = (UnaryOperator)expression;
			unop.SetType(((OpaqueType)unop.type()).resolvedType());
			break;
		default:
			throw new NotImplementedException();
		}
	}


	private void ReplaceOpaqueInIdentifier(Identifier identifier)
	{
		switch (identifier.kind())
		{
		case AttributeIdentifier:
			AttributeIdentifier id = (AttributeIdentifier)identifier;
			assert(id.type() instanceof OpaqueType); // that's why we are here
			id.SetType(((OpaqueType)id.type()).resolvedType());
			break;
		case ExpressionVariableIdentifier:
			ExpressionVariableIdentifier exprvar = (ExpressionVariableIdentifier)identifier;
			assert(exprvar.type() instanceof OpaqueType);
			exprvar.SetType(((OpaqueType)exprvar.type()).resolvedType());
			break;
		case LocalVariableIdentifier:
			LocalVariableIdentifier var = (LocalVariableIdentifier)identifier;
			assert(var.type() instanceof OpaqueType);
			var.SetType(((OpaqueType)var.type()).resolvedType());
			break;
		case ParameterIdentifier:
			ParameterIdentifier param = (ParameterIdentifier)identifier;
			assert(param.type() instanceof OpaqueType);
			param.SetType(((OpaqueType)identifier.type()).resolvedType());
			break;
		default:
			throw new NotImplementedException();
		}
	}

	private void ReplaceOpaqueInType(Type ulyssesType)
	{
		switch (ulyssesType.kind())
		{
		case FunctionType:
			FunctionType func = (FunctionType)ulyssesType;
			if (func.returnType() instanceof OpaqueType)
				func.SetReturnType(((OpaqueType)func.returnType()).resolvedType());

			for (int cntr = 0; cntr < func.parameter().size(); cntr ++)
			{
				Type t = func.parameter().get(cntr);
				if (t.kind() == TypeKind.OpaqueType)
					func.parameter().set(cntr, ((OpaqueType)t).resolvedType());
			}
			break;
		case ListType:
			ListType alist = (ListType)ulyssesType;
			if (alist.innerType().kind() == TypeKind.OpaqueType)
				alist.SetInnerType(((OpaqueType)alist.innerType()).resolvedType());
			break;
		case MapType:
			MapType aMap = (MapType)ulyssesType;
			if (aMap.fromType().kind() == TypeKind.OpaqueType)
				aMap.SetFromType(((OpaqueType)aMap.fromType()).resolvedType());
			if (aMap.toType().kind() == TypeKind.OpaqueType)
				aMap.SetToType(((OpaqueType)aMap.toType()).resolvedType());
			break;
			/*case TypeKind.OoActionSystemType:
               // handeled by the other cases...
                break;*/
			case TupleType:
				TupleType aTuple = (TupleType)ulyssesType;
				for (int cntr = 0; cntr < aTuple.innerTypes().size(); cntr ++) {
					Type t = aTuple.innerTypes().get(cntr);
					if (t.kind() == TypeKind.OpaqueType)
						aTuple.innerTypes().set(cntr, ((OpaqueType)t).resolvedType());
				}
				break;
			default:
				throw new NotImplementedException();
		}
	}

	@Override
	protected void VisitAstElement(IAst subElement, IAst parent)
	{
		super.VisitAstElement(subElement, parent);

		/*see whether the subElement is an OpaqueType, we need
          to replace.*/
		if ((subElement != null)
				&& (subElement.nodeType() == AstNodeTypeEnum.type)
				&& (((Type)subElement).kind() == TypeKind.OpaqueType))
			ReplaceOpaque(parent);
	}

	public OoaReplaceOpaqueVisitor(ParserState aState)
	{
		super(aState);
	}
}
