// =================================================================
//	Performs finalization on this node.
//
//	TODO: Move this into semant, we only have it in here because
//		  if we do a checkAccess in semant we will get a null
//		  reference exception if we are still assinging this
//		  nodes parents (in the case of implicit boxing)
//
// =================================================================
CASTNode* CNewExpressionASTNode::Finalize(CSemanter* semanter)
{
	// Grab arguments.
	std::vector<CDataType*> argument_datatypes;
	for (auto iter = ArgumentExpressions.begin(); iter != ArgumentExpressions.end(); iter++)
	{
		CExpressionBaseASTNode* node = dynamic_cast<CExpressionBaseASTNode*>(*iter);
		argument_datatypes.push_back(node->ExpressionResultType);
	}

	// Create new object.
	if (dynamic_cast<CArrayDataType*>(DataType) == NULL)
	{		
		// Check class is valid.
		CClassASTNode* classNode = DataType->GetClass(semanter);

		// Check we can find a constructor.
		CClassMemberASTNode* node = classNode->FindClassMethod(semanter, classNode->Identifier, argument_datatypes, false);
		if (node == NULL)
		{
			semanter->GetContext()->FatalError(CStringHelper::FormatString("Could not find suitable constructor to instantiate class '%s'.", DataType->ToString().c_str()), Token);
		}

		// Now to do the actual finalization - checking if access is valid!
		node->CheckAccess(semanter, this);
	}

	return this;
}
// =================================================================
//	Performs semantic analysis on this node.
// =================================================================
CASTNode* CMethodCallExpressionASTNode::Semant(CSemanter* semanter)
{ 
	SEMANT_TRACE("CMethodCallExpressionASTNode");

	// Only semant once.
	if (Semanted == true)
	{
		return this;
	}
	Semanted = true;

	// Get expression representations.
	CExpressionBaseASTNode* left_hand_expr	 = dynamic_cast<CExpressionBaseASTNode*>(LeftValue);
	CExpressionBaseASTNode* right_hand_expr  = dynamic_cast<CExpressionBaseASTNode*>(RightValue);

	// Semant left hand node.
	LeftValue  = ReplaceChild(LeftValue,   LeftValue->Semant(semanter));
	
	// Make sure we can access class.
	CClassASTNode* accessClass = left_hand_expr->ExpressionResultType->GetClass(semanter);
	if (accessClass == NULL)
	{
		semanter->GetContext()->FatalError(CStringHelper::FormatString("Invalid use of scoping operator."), Token);		
	}

	// Check we can access this class from here.
	accessClass->CheckAccess(semanter, this);

	// NOTE: Do not r-value semant identifier, we want to process that ourselves.
	CIdentifierExpressionASTNode* identNode = dynamic_cast<CIdentifierExpressionASTNode*>(RightValue);

	// Semant arguments.
	std::vector<CDataType*> argument_types;
	std::string argument_types_string;
	for (std::vector<CASTNode*>::iterator iter = ArgumentExpressions.begin(); iter < ArgumentExpressions.end(); iter++)
	{
		CExpressionBaseASTNode* node = dynamic_cast<CExpressionBaseASTNode*>((*iter)->Semant(semanter));
		argument_types.push_back(node->ExpressionResultType);

		if (iter != ArgumentExpressions.begin())
		{
			argument_types_string += ", ";
		}
		argument_types_string += node->ExpressionResultType->ToString();

		(*iter) = node;
	}

	// Make sure the identifier represents a valid field.
	CClassMemberASTNode* declaration = accessClass->FindClassMethod(semanter, identNode->Token.Literal, argument_types, false, NULL, this);
	if (declaration == NULL)
	{
		semanter->GetContext()->FatalError(CStringHelper::FormatString("Undefined method '%s(%s)' in class '%s'.", identNode->Token.Literal.c_str(), argument_types_string.c_str(), accessClass->ToString().c_str()), Token);		
	}

// UPDATE: Abstract method calling is fine. Remember we won't be able to instantiate classes that do not override all abstract methods.
//	if (declaration->IsAbstract == true)
//	{
//		semanter->GetContext()->FatalError(CStringHelper::FormatString("Cannot call method '%s(%s)' in class '%s', method is abstract.", identNode->Token.Literal.c_str(), argument_types_string.c_str(), accessClass->ToString().c_str()), Token);		
//	}
	
	ResolvedDeclaration = declaration;

	// Check we can access this field from here.
	declaration->CheckAccess(semanter, this);

	// HACK: This is really hackish and needs fixing!
	if (dynamic_cast<CThisExpressionASTNode*>(LeftValue) != NULL &&
		declaration->IsStatic == true)
	{		
		LeftValue = ReplaceChild(LeftValue, new CClassRefExpressionASTNode(NULL, Token));
		LeftValue->Token.Literal = declaration->FindClassScope(semanter)->Identifier;
		LeftValue->Semant(semanter);

		left_hand_expr	 = dynamic_cast<CExpressionBaseASTNode*>(LeftValue);
	}

	// Add default arguments if we do not have enough args to call.
	if (declaration->Arguments.size() > ArgumentExpressions.size())
	{
		for (unsigned int i = ArgumentExpressions.size(); i < declaration->Arguments.size() ; i++)
		{
			CASTNode* expr = declaration->Arguments.at(i)->AssignmentExpression->Clone(semanter);
			AddChild(expr);
			ArgumentExpressions.push_back(expr);

			expr->Semant(semanter);
		}
	}
	
	// Cast all arguments to correct data types.
	int index = 0;
	for (std::vector<CASTNode*>::iterator iter = ArgumentExpressions.begin(); iter != ArgumentExpressions.end(); iter++)
	{
		CDataType* dataType = declaration->Arguments.at(index++)->Type;

		CExpressionBaseASTNode* subnode = dynamic_cast<CExpressionBaseASTNode*>(*iter);
		subnode = dynamic_cast<CExpressionBaseASTNode*>(ReplaceChild(subnode, subnode->CastTo(semanter, dataType, Token)));
		(*iter) = subnode;
	}

	// If we are a class reference, we can only access static fields.
	bool isClassReference = (dynamic_cast<CClassReferenceDataType*>(left_hand_expr->ExpressionResultType) != NULL);
	if (isClassReference == true)
	{
		if (declaration->IsStatic == false)
		{
			semanter->GetContext()->FatalError(CStringHelper::FormatString("Cannot access instance method '%s' through class reference '%s'.", declaration->Identifier.c_str(), accessClass->ToString().c_str()), Token);	
		}
	}

	// If this is a constructor we are calling, make sure we are in a constructors scope, or its illegal!
	else
	{
		CClassMemberASTNode* methodScope = FindClassMethodScope(semanter);

		if (methodScope == NULL ||
			methodScope->IsConstructor == false)
		{
			if (declaration->IsConstructor == true)
			{
				semanter->GetContext()->FatalError("Calling constructors manually is only valid inside another constructors scope.", Token);	
			}
		}
	}

	// Resulting type is always our right hand type.
	ExpressionResultType = declaration->ReturnType;

	return this;
}
// =================================================================
//	Check for duplicate identifier.
// =================================================================
CClassMemberASTNode* CClassASTNode::FindClassMethod(CSemanter*					semanter, 
													std::string					identifier, 
													std::vector<CDataType*>		arguments, 
													bool						explicit_arguments,
													CASTNode*					ignoreNode, 
													CASTNode*					referenceNode)
{
	// Make sure this class is semanted.
	if (!Semanted)
	{
		Semant(semanter);
	}

	// Find all possible methods with the name.
	std::vector<CClassMemberASTNode*> nodes;

	CClassASTNode* scope = this;
	while (scope != NULL)
	{
		if (scope->Body != NULL)
		{
			for (auto iter = scope->Body->Children.begin(); iter != scope->Body->Children.end(); iter++)
			{
				CClassMemberASTNode* member = dynamic_cast<CClassMemberASTNode*>(*iter);
				if (member				!= NULL &&
					member->MemberType	== MemberType::Method && 
					member->Identifier	== identifier &&
					member				!= ignoreNode &&
					arguments.size()	<= member->Arguments.size())
				{

					// Has one of the other members overridcen this method already?
					bool alreadyExists = false;
					for (auto iter2 = nodes.begin(); iter2 != nodes.end(); iter2++)
					{
						CClassMemberASTNode* member2 = *iter2;
						if (member->Identifier == member2->Identifier &&
							member->Arguments.size() == member2->Arguments.size() &&
							member->IsVirtual == true && member2->IsOverride == true)
						{
							bool argsSame = true;

							for (unsigned int i = 0; i < member->Arguments.size(); i++)
							{
								CVariableStatementASTNode* arg = member->Arguments.at(i);
								CVariableStatementASTNode* arg2 = member2->Arguments.at(i);
								if (!arg->Type->IsEqualTo(semanter, arg2->Type))
								{
									argsSame = false;
									break;
								}
							}

							if (argsSame == true)
							{
								alreadyExists = true;
								break;
							}
						}
					}

					if (alreadyExists == false)
					{
						member->Semant(semanter);
						nodes.push_back(member);
					}
				}
			}
		}
		scope = scope->SuperClass;
	}

	// Try and find amatch!
	CClassMemberASTNode* match			= NULL;
	bool				 isExactMatch	= false;
	std::string			 errorMessage	= "";

	// Look for valid nodes.		
	for (auto iter = nodes.begin(); iter != nodes.end(); iter++)
	{
		CClassMemberASTNode* member = *iter;

		bool exact		= true;
		bool possible	= true;

		for (unsigned int i = 0; i < member->Arguments.size(); i++)
		{
			CVariableStatementASTNode* arg = member->Arguments.at(i);

			if (arguments.size() > member->Arguments.size())
			{
				continue;
			}

			if (i < arguments.size())
			{
				if (arguments.at(i)->IsEqualTo(semanter, arg->Type))
				{
					continue;
				}
				exact = false;

				if (!explicit_arguments && CCastExpressionASTNode::IsValidCast(semanter, arguments.at(i), arg->Type, false))// arguments.at(i)->CanCastTo(semanter, arg->Type))
				{
					continue;
				}
			}
			else if (arg->AssignmentExpression != NULL)
			{
				exact = false;
				if (!explicit_arguments)
				{
					continue;
				}
			}

			possible = false;
			break;
		}

		if (!possible)
		{
			continue;
		}

		if (exact == true)
		{
			if (isExactMatch == true)
			{
				semanter->GetContext()->FatalError(CStringHelper::FormatString("Found ambiguous reference to method of class '%s'. Reference could mean either '%s' or '%s'.", Identifier.c_str(), match->ToString().c_str(), member->ToString().c_str()),
													referenceNode == NULL ? Token : referenceNode->Token);
			}
			else
			{
				errorMessage	= "";
				match			= member;
				isExactMatch	= true;
			}
		}
		else
		{
			if (!isExactMatch)
			{
				if (match != NULL)
				{
					errorMessage = CStringHelper::FormatString("Found ambiguous reference to method of class '%s'. Reference could mean either '%s' or '%s'.", Identifier.c_str(), match->ToString().c_str(), member->ToString().c_str());
				}
				else
				{
					match = member;
				}
			}
		}
	}

	// Return?
	if (!isExactMatch)
	{
		if (errorMessage != "")
		{
			semanter->GetContext()->FatalError(errorMessage, referenceNode == NULL ? Token : referenceNode->Token);
		}
		if (explicit_arguments == true)
		{
			return NULL;
		}
	}

	// No match available? :S
	if (match == NULL)
	{
		return NULL;
	}

	// Return matched class.
	return match;
}