/*
   AngelCode Scripting Library
   Copyright (c) 2003-2009 Andreas Jonsson

   This software is provided 'as-is', without any express or implied 
   warranty. In no event will the authors be held liable for any 
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any 
   purpose, including commercial applications, and to alter it and 
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you 
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product 
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and 
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source 
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/


//
// as_builder.cpp
//
// This is the class that manages the compilation of the scripts
//


#include "as_config.h"
#include "as_builder.h"
#include "as_parser.h"
#include "as_compiler.h"
#include "as_tokendef.h"
#include "as_string_util.h"
#include "as_outputbuffer.h"
#include "as_texts.h"
#include "as_scriptstruct.h"

BEGIN_AS_NAMESPACE

asCBuilder::asCBuilder(asCScriptEngine *engine, asCModule *module)
{
	this->engine = engine;
	this->module = module;
}

asCBuilder::~asCBuilder()
{
	asUINT n;

	// Free all functions
	for( n = 0; n < functions.GetLength(); n++ )
	{
		if( functions[n] )
		{
			if( functions[n]->node ) 
			{
				functions[n]->node->Destroy(engine);
			}

			asDELETE(functions[n],sFunctionDescription);
		}

		functions[n] = 0;
	}

	// Free all global variables
	for( n = 0; n < globVariables.GetLength(); n++ )
	{
		if( globVariables[n] )
		{
			if( globVariables[n]->node )
			{
				globVariables[n]->node->Destroy(engine);
			}

			asDELETE(globVariables[n],sGlobalVariableDescription);
			globVariables[n] = 0;
		}
	}

	// Free all the loaded files
	for( n = 0; n < scripts.GetLength(); n++ )
	{
		if( scripts[n] )
		{
			asDELETE(scripts[n],asCScriptCode);
		}

		scripts[n] = 0;
	}

	// Free all class declarations
	for( n = 0; n < classDeclarations.GetLength(); n++ )
	{
		if( classDeclarations[n] )
		{
			if( classDeclarations[n]->node )
			{
				classDeclarations[n]->node->Destroy(engine);
			}

			asDELETE(classDeclarations[n],sClassDeclaration);
			classDeclarations[n] = 0;
		}
	}

	for( n = 0; n < interfaceDeclarations.GetLength(); n++ )
	{
		if( interfaceDeclarations[n] )
		{
			if( interfaceDeclarations[n]->node )
			{
				interfaceDeclarations[n]->node->Destroy(engine);
			}

			asDELETE(interfaceDeclarations[n],sClassDeclaration);
			interfaceDeclarations[n] = 0;
		}
	}

	for( n = 0; n < namedTypeDeclarations.GetLength(); n++ )
	{
		if( namedTypeDeclarations[n] )
		{
			if( namedTypeDeclarations[n]->node )
			{
				namedTypeDeclarations[n]->node->Destroy(engine);
			}

			asDELETE(namedTypeDeclarations[n],sClassDeclaration);
			namedTypeDeclarations[n] = 0;
		}
	}
}

int asCBuilder::AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy)
{
	asCScriptCode *script = asNEW(asCScriptCode);
	script->SetCode(name, code, codeLength, makeCopy);
	script->lineOffset = lineOffset;
	script->idx = sectionIdx;
	scripts.PushLast(script);

	return 0;
}

int asCBuilder::Build()
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	ParseScripts();
	CompileClasses();
	CompileGlobalVariables();
	CompileFunctions();

	if( numErrors > 0 )
		return asERROR;

	return asSUCCESS;
}

int asCBuilder::BuildString(const char *string, asCContext *ctx)
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	// Add the string to the script code
	asCScriptCode *script = asNEW(asCScriptCode);
	script->SetCode(TXT_EXECUTESTRING, string, true);
	script->lineOffset = -1; // Compensate for "void ExecuteString() {\n"
	scripts.PushLast(script);

	// Parse the string
	asCParser parser(this);
	if( parser.ParseScript(scripts[0]) >= 0 )
	{
		// Find the function
		asCScriptNode *node = parser.GetScriptNode();
		node = node->firstChild;
		if( node->nodeType == snFunction )
		{
			node->DisconnectParent();

			sFunctionDescription *func = asNEW(sFunctionDescription);
			functions.PushLast(func);

			func->script = scripts[0];
			func->node = node;
			func->name = "";
		}
		else
		{
			// An error occurred
			asASSERT(false);
		}
	}

	if( numErrors == 0 )
	{
		// Compile the function
		asCCompiler compiler(engine);
		asCScriptFunction *execfunc = asNEW(asCScriptFunction)(engine,module);
		if( compiler.CompileFunction(this, functions[0]->script, functions[0]->node, execfunc) >= 0 )
		{
			execfunc->id = asFUNC_STRING;

			ctx->SetExecuteStringFunction(execfunc);
		}
		else
		{
			asDELETE(execfunc,asCScriptFunction);
		}
	}

	if( numErrors > 0 )
		return asERROR;

	return asSUCCESS;
}

void asCBuilder::ParseScripts()
{
	asCArray<asCParser*> parsers((int)scripts.GetLength());
	
	// Parse all the files as if they were one
	asUINT n = 0;
	for( n = 0; n < scripts.GetLength(); n++ )
	{
		asCParser *parser = asNEW(asCParser)(this);
		parsers.PushLast(parser);

		// Parse the script file
		parser->ParseScript(scripts[n]);
	}

	if( numErrors == 0 )
	{
		// Find all type declarations
		for( n = 0; n < scripts.GetLength(); n++ )
		{
			asCScriptNode *node = parsers[n]->GetScriptNode();

			// Find structure definitions first
			node = node->firstChild;
			while( node )
			{
				asCScriptNode *next = node->next;
				if( node->nodeType == snClass )
				{
					node->DisconnectParent();
					RegisterClass(node, scripts[n]);
				}
				else if( node->nodeType == snInterface )
				{
					node->DisconnectParent();
					RegisterInterface(node, scripts[n]);
				}
				//	Handle enumeration
				else if( node->nodeType == snEnum )
				{
					node->DisconnectParent();
					RegisterEnum(node, scripts[n]);
				}
				//	Handle typedef
				else if( node->nodeType == snTypedef )
				{
					node->DisconnectParent();
					RegisterTypedef(node, scripts[n]);
				}

				node = next;
			}
		}

		// Register script methods found in the interfaces
		for( n = 0; n < interfaceDeclarations.GetLength(); n++ )
		{
			sClassDeclaration *decl = interfaceDeclarations[n];

			asCScriptNode *node = decl->node->firstChild->next;
			while( node )
			{
				asCScriptNode *next = node->next;
				if( node->nodeType == snFunction )
				{
					node->DisconnectParent();
					RegisterScriptFunction(module->GetNextFunctionId(), node, decl->script, decl->objType, true);
				}
				
				node = next;
			}
		}

		// Now the interfaces have been completely established, now we need to determine if
		// the same interface has already been registered before, and if so reuse the interface id.
		module->ResolveInterfaceIds();

		// Register script methods found in the structures
		for( n = 0; n < classDeclarations.GetLength(); n++ )
		{
			sClassDeclaration *decl = classDeclarations[n];

			asCScriptNode *node = decl->node->firstChild->next;

			// Skip list of classes and interfaces
			while( node && node->nodeType == snIdentifier )
				node = node->next;

			while( node )
			{
				asCScriptNode *next = node->next;
				if( node->nodeType == snFunction )
				{
					node->DisconnectParent();
					RegisterScriptFunction(module->GetNextFunctionId(), node, decl->script, decl->objType);
				}
				
				node = next;
			}

			// Make sure the default factory & constructor exists for classes
			if( decl->objType->beh.construct == engine->scriptTypeBehaviours.beh.construct )
			{
				AddDefaultConstructor(decl->objType, decl->script);
			}
		}

		// Find other global nodes
		for( n = 0; n < scripts.GetLength(); n++ )
		{
			// Find other global nodes
			asCScriptNode *node = parsers[n]->GetScriptNode();
			node = node->firstChild;
			while( node )
			{
				asCScriptNode *next = node->next;
				node->DisconnectParent();

				if( node->nodeType == snFunction )
				{
					RegisterScriptFunction(module->GetNextFunctionId(), node, scripts[n]);
				}
				else if( node->nodeType == snGlobalVar )
				{
					RegisterGlobalVar(node, scripts[n]);
				}
				else if( node->nodeType == snImport )
				{
					RegisterImportedFunction(module->GetNextImportedFunctionId(), node, scripts[n]);
				}
				else
				{
					// Unused script node
					int r, c;
					scripts[n]->ConvertPosToRowCol(node->tokenPos, &r, &c);

					WriteWarning(scripts[n]->name.AddressOf(), TXT_UNUSED_SCRIPT_NODE, r, c);

					node->Destroy(engine);
				}

				node = next;
			}
		}
	}

	for( n = 0; n < parsers.GetLength(); n++ )
	{
		asDELETE(parsers[n],asCParser);
	}
}

void asCBuilder::CompileFunctions()
{
	// Compile each function
	for( asUINT n = 0; n < functions.GetLength(); n++ )
	{
		if( functions[n] == 0 ) continue;

		asCCompiler compiler(engine);

		int r, c;
		functions[n]->script->ConvertPosToRowCol(functions[n]->node->tokenPos, &r, &c);
		asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
		asCString str = func->GetDeclarationStr();
		str.Format(TXT_COMPILING_s, str.AddressOf());
		WriteInfo(functions[n]->script->name.AddressOf(), str.AddressOf(), r, c, true);

		compiler.CompileFunction(this, functions[n]->script, functions[n]->node, func);

		preMessage.isSet = false;
	}
}

int asCBuilder::ParseDataType(const char *datatype, asCDataType *result)
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	asCScriptCode source;
	source.SetCode("", datatype, true);

	asCParser parser(this);
	int r = parser.ParseDataType(&source);
	if( r < 0 )
		return asINVALID_TYPE;

	// Get data type and property name
	asCScriptNode *dataType = parser.GetScriptNode()->firstChild;

	*result = CreateDataTypeFromNode(dataType, &source);

	if( numErrors > 0 )
		return asINVALID_TYPE;

	return asSUCCESS;
}

int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &name, asCDataType &type)
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	if( dt )
	{
		// Verify that the object type exist
		if( dt->GetObjectType() == 0 )
			return asINVALID_OBJECT;
	}

	// Check property declaration and type
	asCScriptCode source;
	source.SetCode(TXT_PROPERTY, decl, true);

	asCParser parser(this);
	int r = parser.ParsePropertyDeclaration(&source);
	if( r < 0 )
		return asINVALID_DECLARATION;

	// Get data type and property name
	asCScriptNode *dataType = parser.GetScriptNode()->firstChild;

	asCScriptNode *nameNode = dataType->next;

	type = CreateDataTypeFromNode(dataType, &source);
	name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength);

	// Verify property name
	if( dt )
	{
		if( CheckNameConflictMember(*dt, name.AddressOf(), nameNode, &source) < 0 )
			return asNAME_TAKEN;
	}
	else
	{
		if( CheckNameConflict(name.AddressOf(), nameNode, &source) < 0 )
			return asNAME_TAKEN;
	}

	if( numErrors > 0 )
		return asINVALID_DECLARATION;

	return asSUCCESS;
}

asCObjectProperty *asCBuilder::GetObjectProperty(asCDataType &obj, const char *prop)
{
	asASSERT(obj.GetObjectType() != 0);

	// TODO: Only search in config groups to which the module has access
	// TODO: optimize: Improve linear search
	asCArray<asCObjectProperty *> &props = obj.GetObjectType()->properties;
	for( asUINT n = 0; n < props.GetLength(); n++ )
		if( props[n]->name == prop )
			return props[n];

	return 0;
}

asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue)
{
	asUINT n;

	if( isCompiled ) *isCompiled = true;
	if( isPureConstant ) *isPureConstant = false;

	// TODO: optimize: Improve linear search
	// Check application registered properties
	asCArray<asCGlobalProperty *> *props = &(engine->globalProps);
	for( n = 0; n < props->GetLength(); ++n )
		if( (*props)[n] && (*props)[n]->name == prop )
		{
			if( module )
			{
				// Find the config group for the global property
				asCConfigGroup *group = engine->FindConfigGroupForGlobalVar((*props)[n]->index);
				if( !group || group->HasModuleAccess(module->name.AddressOf()) )
					return (*props)[n];
			}
			else
			{
				// We're not compiling a module right now, so it must be a registered global property
				return (*props)[n];
			}
		}

	// TODO: optimize: Improve linear search
	// Check properties being compiled now
	asCArray<sGlobalVariableDescription *> *gvars = &globVariables;
	for( n = 0; n < gvars->GetLength(); ++n )
	{
		if( (*gvars)[n] && (*gvars)[n]->name == prop )
		{
			if( isCompiled ) *isCompiled = (*gvars)[n]->isCompiled;

			if( isPureConstant ) *isPureConstant = (*gvars)[n]->isPureConstant;
			if( constantValue  ) *constantValue  = (*gvars)[n]->constantValue;

			return (*gvars)[n]->property;
		}
	}

	// TODO: optimize: Improve linear search
	// Check previously compiled global variables
	if( module )
	{
		props = &module->scriptGlobals;
		for( n = 0; n < props->GetLength(); ++n )
			if( (*props)[n]->name == prop )
				return (*props)[n];
	}

	return 0;
}

int asCBuilder::ParseFunctionDeclaration(const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray<bool> *paramAutoHandles, bool *returnAutoHandle)
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	asCScriptCode source;
	source.SetCode(TXT_SYSTEM_FUNCTION, decl, true);

	asCParser parser(this);

	int r = parser.ParseFunctionDefinition(&source);
	if( r < 0 )
		return asINVALID_DECLARATION;

	asCScriptNode *node = parser.GetScriptNode();

	// Find name
	asCScriptNode *n = node->firstChild->next->next;
	func->name.Assign(&source.code[n->tokenPos], n->tokenLength);

	// Initialize a script function object for registration
	bool autoHandle;

	// Scoped reference types are allowed to use handle when returned from application functions
	func->returnType = CreateDataTypeFromNode(node->firstChild, &source, true);
	func->returnType = ModifyDataTypeFromNode(func->returnType, node->firstChild->next, &source, 0, &autoHandle);
	if( autoHandle && (!func->returnType.IsObjectHandle() || func->returnType.IsReference()) )
			return asINVALID_DECLARATION;			
	if( returnAutoHandle ) *returnAutoHandle = autoHandle;

	// Reference types cannot be returned by value from system functions
	if( isSystemFunction &&
		(func->returnType.GetObjectType() && 
		 (func->returnType.GetObjectType()->flags & asOBJ_REF)) && 
        !(func->returnType.IsReference() ||
		  func->returnType.IsObjectHandle()) )
		return asINVALID_DECLARATION;

	// Count number of parameters
	int paramCount = 0;
	n = n->next->firstChild;
	while( n )
	{
		paramCount++;
		n = n->next->next;
		if( n && n->nodeType == snIdentifier )
			n = n->next;
	}

	// Preallocate memory
	func->parameterTypes.Allocate(paramCount, false);
	func->inOutFlags.Allocate(paramCount, false);
	if( paramAutoHandles ) paramAutoHandles->Allocate(paramCount, false);

	n = node->firstChild->next->next->next->firstChild;
	while( n )
	{
		int inOutFlags;
		asCDataType type = CreateDataTypeFromNode(n, &source);
		type = ModifyDataTypeFromNode(type, n->next, &source, &inOutFlags, &autoHandle);
		
		// Reference types cannot be passed by value to system functions
		if( isSystemFunction &&
			(type.GetObjectType() && 
		     (type.GetObjectType()->flags & asOBJ_REF)) && 
			!(type.IsReference() ||
			  type.IsObjectHandle()) )
			return asINVALID_DECLARATION;

		// Store the parameter type
		func->parameterTypes.PushLast(type);
		func->inOutFlags.PushLast(inOutFlags);

		// Don't permit void parameters
		if( type.GetTokenType() == ttVoid )
			return asINVALID_DECLARATION;

		if( autoHandle && (!type.IsObjectHandle() || type.IsReference()) )
			return asINVALID_DECLARATION;			

		if( paramAutoHandles ) paramAutoHandles->PushLast(autoHandle);

		// Make sure that var type parameters are references
		if( type.GetTokenType() == ttQuestion &&
			!type.IsReference() )
			return asINVALID_DECLARATION;

		// Move to next parameter
		n = n->next->next;
		if( n && n->nodeType == snIdentifier )
			n = n->next;
	}

	// Set the read-only flag if const is declared after parameter list
	if( node->lastChild->nodeType == snUndefined && node->lastChild->tokenType == ttConst )
		func->isReadOnly = true;
	else 
		func->isReadOnly = false;

	if( numErrors > 0 || numWarnings > 0 )
		return asINVALID_DECLARATION;

	return 0;
}

int asCBuilder::ParseVariableDeclaration(const char *decl, asCObjectProperty *var)
{
	numErrors = 0;
	numWarnings = 0;
	preMessage.isSet = false;

	asCScriptCode source;
	source.SetCode(TXT_VARIABLE_DECL, decl, true);

	asCParser parser(this);

	int r = parser.ParsePropertyDeclaration(&source);
	if( r < 0 )
		return asINVALID_DECLARATION;

	asCScriptNode *node = parser.GetScriptNode();

	// Find name
	asCScriptNode *n = node->firstChild->next;
	var->name.Assign(&source.code[n->tokenPos], n->tokenLength);

	// Initialize a script variable object for registration
	var->type = CreateDataTypeFromNode(node->firstChild, &source);

	if( numErrors > 0 || numWarnings > 0 )
		return asINVALID_DECLARATION;

	return 0;
}

int asCBuilder::CheckNameConflictMember(asCDataType &dt, const char *name, asCScriptNode *node, asCScriptCode *code)
{
	// It's not necessary to check against object types

	// Check against other members
	asCObjectType *t = dt.GetObjectType();

	// TODO: optimize: Improve linear search
	asCArray<asCObjectProperty *> &props = t->properties;
	for( asUINT n = 0; n < props.GetLength(); n++ )
	{
		if( props[n]->name == name )
		{
			if( code )
			{
				int r, c;
				code->ConvertPosToRowCol(node->tokenPos, &r, &c);

				asCString str;
				str.Format(TXT_NAME_CONFLICT_s_OBJ_PROPERTY, name);
				WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
			}

			return -1;
		}
	}

	// TODO: Property names must be checked against method names

	return 0;
}

int asCBuilder::CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code)
{
	// TODO: Must verify object types in all config groups, whether the module has access or not
	// Check against object types
	if( engine->GetObjectType(name) != 0 )
	{
		if( code )
		{
			int r, c;
			code->ConvertPosToRowCol(node->tokenPos, &r, &c);

			asCString str;
			str.Format(TXT_NAME_CONFLICT_s_EXTENDED_TYPE, name);
			WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
		}

		return -1;
	}

	// TODO: Must verify global properties in all config groups, whether the module has access or not
	// Check against global properties
	asCGlobalProperty *prop = GetGlobalProperty(name, 0, 0, 0);
	if( prop )
	{
		if( code )
		{
			int r, c;
			code->ConvertPosToRowCol(node->tokenPos, &r, &c);

			asCString str;
			str.Format(TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY, name);

			WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
		}

		return -1;
	}

	// TODO: Property names must be checked against function names

	// Check against class types
	asUINT n;
	for( n = 0; n < classDeclarations.GetLength(); n++ )
	{
		if( classDeclarations[n]->name == name )
		{
			if( code )
			{
				int r, c;
				code->ConvertPosToRowCol(node->tokenPos, &r, &c);

				asCString str;
				str.Format(TXT_NAME_CONFLICT_s_STRUCT, name);

				WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
			}

			return -1;
		}
	}

	// Check against named types
	for( n = 0; n < namedTypeDeclarations.GetLength(); n++ )
	{
		if( namedTypeDeclarations[n]->name == name )
		{
			if( code )
			{
				int r, c;
				code->ConvertPosToRowCol(node->tokenPos, &r, &c);

				asCString str;

				// TODO: Need a TXT constant
				str.Format("Name conflict. '%s' is a named type (FIXME!).", name);

				WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
			}

			return -1;
		}
	}

	return 0;
}


int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file)
{
	// What data type is it?
	asCDataType type = CreateDataTypeFromNode(node->firstChild, file);

	if( !type.CanBeInstanciated() )
	{
		asCString str;
		// TODO: Change to "'type' cannot be declared as variable"
		str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf());
		
		int r, c;
		file->ConvertPosToRowCol(node->tokenPos, &r, &c);
		
		WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
	}

	asCScriptNode *n = node->firstChild->next;

	while( n )
	{
		// Verify that the name isn't taken
		GETSTRING(name, &file->code[n->tokenPos], n->tokenLength);
		CheckNameConflict(name.AddressOf(), n, file);

		// Register the global variable
		sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription);
		globVariables.PushLast(gvar);

		gvar->script      = file;
		gvar->name        = name;
		gvar->isCompiled  = false;
		gvar->datatype    = type;
		gvar->isEnumValue = false;

		// TODO: Give error message if wrong
		asASSERT(!gvar->datatype.IsReference());

		gvar->node = 0;
		if( n->next && 
			(n->next->nodeType == snAssignment ||
			 n->next->nodeType == snArgList    || 
			 n->next->nodeType == snInitList     ) )
		{
			gvar->node = n->next;
			n->next->DisconnectParent();
		}

		gvar->property = module->AllocateGlobalProperty(name.AddressOf(), gvar->datatype);
		gvar->index    = gvar->property->index;

		n = n->next;
	}

	node->Destroy(engine);

	return 0;
}

int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file)
{
	asCScriptNode *n = node->firstChild;
	GETSTRING(name, &file->code[n->tokenPos], n->tokenLength);
	
	int r, c;
	file->ConvertPosToRowCol(n->tokenPos, &r, &c);

	CheckNameConflict(name.AddressOf(), n, file);

	sClassDeclaration *decl = asNEW(sClassDeclaration);
	classDeclarations.PushLast(decl);
	decl->name = name;
	decl->script = file;
	decl->validState = 0;
	decl->node = node;

	asCObjectType *st = asNEW(asCObjectType)(engine);
	st->arrayType = 0;
	st->flags = asOBJ_REF | asOBJ_SCRIPT_STRUCT;

	if( node->tokenType == ttHandle )
		st->flags |= asOBJ_IMPLICIT_HANDLE;

	st->size = sizeof(asCScriptStruct);
	st->name = name;
	st->tokenType = ttIdentifier;
	module->classTypes.PushLast(st);
	engine->classTypes.PushLast(st);
	st->AddRef();
	decl->objType = st;

	// Use the default script class behaviours
	st->beh.factory = engine->scriptTypeBehaviours.beh.factory;
	st->beh.factories = engine->scriptTypeBehaviours.beh.factories;
	st->beh.construct = engine->scriptTypeBehaviours.beh.construct;
	st->beh.constructors = engine->scriptTypeBehaviours.beh.constructors;
	st->beh.addref = engine->scriptTypeBehaviours.beh.addref;
	st->beh.release = engine->scriptTypeBehaviours.beh.release;
	st->beh.copy = engine->scriptTypeBehaviours.beh.copy;
	st->beh.gcGetRefCount = engine->scriptTypeBehaviours.beh.gcGetRefCount;
	st->beh.gcSetFlag = engine->scriptTypeBehaviours.beh.gcSetFlag;
	st->beh.gcGetFlag = engine->scriptTypeBehaviours.beh.gcGetFlag;
	st->beh.gcEnumReferences = engine->scriptTypeBehaviours.beh.gcEnumReferences;
	st->beh.gcReleaseAllReferences = engine->scriptTypeBehaviours.beh.gcReleaseAllReferences;
	st->beh.operators.PushLast(ttAssignment);
	st->beh.operators.PushLast(st->beh.copy);

	return 0;
}

int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file)
{
	asCScriptNode *n = node->firstChild;
	GETSTRING(name, &file->code[n->tokenPos], n->tokenLength);

	int r, c;
	file->ConvertPosToRowCol(n->tokenPos, &r, &c);

	CheckNameConflict(name.AddressOf(), n, file);

	sClassDeclaration *decl = asNEW(sClassDeclaration);
	interfaceDeclarations.PushLast(decl);
	decl->name       = name;
	decl->script     = file;
	decl->validState = 0;
	decl->node       = node;

	// Register the object type for the interface
	asCObjectType *st = asNEW(asCObjectType)(engine);
	st->arrayType = 0;
	st->flags = asOBJ_REF | asOBJ_SCRIPT_STRUCT;
	st->size = 0; // Cannot be instanciated
	st->name = name;
	st->tokenType = ttIdentifier;
	module->classTypes.PushLast(st);
	engine->classTypes.PushLast(st);
	st->AddRef();
	decl->objType = st;

	// Use the default script class behaviours
	st->beh.construct = 0;
	st->beh.addref = engine->scriptTypeBehaviours.beh.addref;
	st->beh.release = engine->scriptTypeBehaviours.beh.release;
	st->beh.copy = 0; 

	return 0;
}

void asCBuilder::CompileGlobalVariables()
{
	bool compileSucceeded = true;

	asCByteCode finalInit(engine);

	// Store state of compilation (errors, warning, output)
	int currNumErrors = numErrors;
	int currNumWarnings = numWarnings;

	// Backup the original message stream
	bool                       msgCallback     = engine->msgCallback;
	asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc;
	void                      *msgCallbackObj  = engine->msgCallbackObj;

	// Set the new temporary message stream
	asCOutputBuffer outBuffer;
	engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL);	

	asCOutputBuffer finalOutput;

	while( compileSucceeded )
	{
		compileSucceeded = false;

		int accumErrors = 0;
		int accumWarnings = 0;

		// Restore state of compilation
		finalOutput.Clear();
		for( asUINT n = 0; n < globVariables.GetLength(); n++ )
		{
			asCByteCode init(engine);
			numWarnings = 0;
			numErrors = 0;
			outBuffer.Clear();

			sGlobalVariableDescription *gvar = globVariables[n];
			if( gvar->isCompiled )
				continue;

			if( gvar->node )
			{
				int r, c;
				gvar->script->ConvertPosToRowCol(gvar->node->tokenPos, &r, &c);
				asCString str = gvar->datatype.Format();
				str += " " + gvar->name;
				str.Format(TXT_COMPILING_s, str.AddressOf());
				WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), r, c, true);
			}

			if( gvar->isEnumValue ) 
			{
				int r;
				if( gvar->node )
				{
					asCCompiler comp(engine);

					// Temporarily switch the type of the variable to int so it can be compiled properly
					asCDataType saveType;
					saveType = gvar->datatype;
					gvar->datatype = asCDataType::CreatePrimitive(ttInt, true);
					r = comp.CompileGlobalVariable(this, gvar->script, gvar->node, gvar);
					gvar->datatype = saveType;
				}
				else 
				{
					r = 0;

					// When there is no assignment the value is the last + 1
					int enumVal = 0;
					if( n > 0 )
					{
						sGlobalVariableDescription *gvar2 = globVariables[n-1];
						if( gvar2->datatype == gvar->datatype )
						{
							// The integer value is stored in the lower bytes
							enumVal = (*(int*)&gvar2->constantValue) + 1;

							if( !gvar2->isCompiled )
							{
								// TODO: Need to get the correct script position
								int row, col;
								gvar->script->ConvertPosToRowCol(0, &row, &col);

								asCString str = gvar->datatype.Format();
								str += " " + gvar->name;
								str.Format(TXT_COMPILING_s, str.AddressOf());
								WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), row, col, true);

								str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, gvar2->name.AddressOf());
								WriteError(gvar->script->name.AddressOf(), str.AddressOf(), row, col);
								r = -1;
							}
						}
					}

					// The integer value is stored in the lower bytes
					*(int*)&gvar->constantValue = enumVal;
				}

				if( r >= 0 )
				{
					// Set the value as compiled
					gvar->isCompiled = true;
					compileSucceeded = true;
				}
			}
			else
			{
				// Compile the global variable
				asCCompiler comp(engine);
				int r = comp.CompileGlobalVariable(this, gvar->script, gvar->node, gvar);
				if( r >= 0 )
				{
					// Compilation succeeded
					gvar->isCompiled = true;
					compileSucceeded = true;

					init.AddCode(&comp.byteCode);
				}
			}

			if( gvar->isCompiled )
			{
				// Add warnings for this constant to the total build
				if( numWarnings )
				{
					currNumWarnings += numWarnings;
					if( msgCallback )
						outBuffer.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj);
				}

				// Add compiled byte code to the final init and exit functions
				finalInit.AddCode(&init);
			}
			else
			{
				// Add output to final output
				finalOutput.Append(outBuffer);
				accumErrors += numErrors;
				accumWarnings += numWarnings;
			}

			preMessage.isSet = false;
		}

		if( !compileSucceeded )
		{
			// Add errors and warnings to total build
			currNumWarnings += accumWarnings;
			currNumErrors += accumErrors;
			if( msgCallback )
				finalOutput.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj);
		}
	}

	// Restore states
	engine->msgCallback     = msgCallback;
	engine->msgCallbackFunc = msgCallbackFunc;
	engine->msgCallbackObj  = msgCallbackObj;

	numWarnings = currNumWarnings;
	numErrors   = currNumErrors;

	// Register init code and clean up code
	finalInit.Ret(0);

	finalInit.Finalize();

	int id = engine->GetNextScriptFunctionId();
	asCScriptFunction *init = asNEW(asCScriptFunction)(engine,module);

	init->id = id;
	module->initFunction = init;
	engine->SetScriptFunction(init);

	init->byteCode.SetLength(finalInit.GetSize());
	finalInit.Output(init->byteCode.AddressOf());
	init->AddReferences();
	init->stackNeeded = finalInit.largestStackUsed;

	// Convert all variables compiled for the enums to true enum values
	for( asUINT n = 0; n < globVariables.GetLength(); n++ )
	{
		asCObjectType *objectType;
		sGlobalVariableDescription *gvar = globVariables[n];
		if( !gvar->isEnumValue ) 
			continue;

		objectType = gvar->datatype.GetObjectType();
		asASSERT(NULL != objectType);

		asSEnumValue *e = asNEW(asSEnumValue);
		e->name = gvar->name;
		e->value = *(int*)&gvar->constantValue;

		objectType->enumValues.PushLast(e);

		// Destroy the gvar property
		if( gvar->node )
			gvar->node->Destroy(engine);
		if( gvar->property )
			asDELETE(gvar->property, asCGlobalProperty);

		asDELETE(gvar, sGlobalVariableDescription);
		globVariables[n] = 0;
	}

#ifdef AS_DEBUG
	// DEBUG: output byte code
	finalInit.DebugOutput("__@init.txt", module, engine);
#endif
}

void asCBuilder::CompileClasses()
{
	asUINT n;
	asCArray<sClassDeclaration*> toValidate((int)classDeclarations.GetLength());

	// Go through each of the classes and register the object type descriptions
	for( n = 0; n < classDeclarations.GetLength(); n++ )
	{
		sClassDeclaration *decl = classDeclarations[n];

		// Enumerate each of the declared properties
		asCScriptNode *node = decl->node->firstChild->next;

		// Skip list of classes and interfaces
		while( node && node->nodeType == snIdentifier )
			node = node->next;

		while( node )
		{
			if( node->nodeType == snDeclaration )
			{
				asCScriptCode *file = decl->script;
				asCDataType dt = CreateDataTypeFromNode(node->firstChild, file);
				GETSTRING(name, &file->code[node->lastChild->tokenPos], node->lastChild->tokenLength);

				if( dt.IsReadOnly() )
				{
					int r, c;
					file->ConvertPosToRowCol(node->tokenPos, &r, &c);

					WriteError(file->name.AddressOf(), TXT_PROPERTY_CANT_BE_CONST, r, c);
				}

				asCDataType st;
				st.SetObjectType(decl->objType);
				CheckNameConflictMember(st, name.AddressOf(), node->lastChild, file);

				// Store the properties in the object type descriptor
				asCObjectProperty *prop = asNEW(asCObjectProperty);
				prop->name = name;
				prop->type = dt;

				int propSize;
				if( dt.IsObject() )
				{
					propSize = dt.GetSizeOnStackDWords()*4;
					if( !dt.IsObjectHandle() )
					{
						if( dt.GetSizeInMemoryBytes() == 0 )
						{
							int r, c;
							file->ConvertPosToRowCol(node->tokenPos, &r, &c);
							asCString str;
							str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());
							WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
						}
						prop->type.MakeReference(true);
					}
				}
				else
				{
					propSize = dt.GetSizeInMemoryBytes();
					if( propSize == 0 )
					{
						int r, c;
						file->ConvertPosToRowCol(node->tokenPos, &r, &c);
						asCString str;
						str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());
						WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
					}
				}

				// Add extra bytes so that the property will be properly aligned
				if( propSize == 2 && (decl->objType->size & 1) ) decl->objType->size += 1;
				if( propSize > 2 && (decl->objType->size & 3) ) decl->objType->size += 3 - (decl->objType->size & 3);

				prop->byteOffset = decl->objType->size;
				decl->objType->size += propSize;

				decl->objType->properties.PushLast(prop);

				// Make sure the struct holds a reference to the config group where the object is registered
				asCConfigGroup *group = engine->FindConfigGroupForObjectType(prop->type.GetObjectType());
				if( group != 0 ) group->AddRef();
			}
			else
				asASSERT(false);

			node = node->next;
		}

		toValidate.PushLast(decl);
	}

	// Verify that the declared structures are valid, e.g. that the structure
	// doesn't contain a member of its own type directly or indirectly
	while( toValidate.GetLength() > 0 ) 
	{
		asUINT numClasses = (asUINT)toValidate.GetLength();

		asCArray<sClassDeclaration*> toValidateNext((int)toValidate.GetLength());
		while( toValidate.GetLength() > 0 )
		{
			sClassDeclaration *decl = toValidate[toValidate.GetLength()-1];
			int validState = 1;
			for( asUINT n = 0; n < decl->objType->properties.GetLength(); n++ )
			{
				// A valid structure is one that uses only primitives or other valid objects
				asCObjectProperty *prop = decl->objType->properties[n];
				asCDataType dt = prop->type;

				if( dt.IsTemplate() )
				{
					asCDataType sub = dt;
					while( sub.IsTemplate() && !sub.IsObjectHandle() )
						sub = sub.GetSubType();

					dt = sub;
				}

				if( dt.IsObject() && !dt.IsObjectHandle() )
				{
					// Find the class declaration
					sClassDeclaration *pdecl = 0;
					for( asUINT p = 0; p < classDeclarations.GetLength(); p++ )
					{
						if( classDeclarations[p]->objType == dt.GetObjectType() )
						{
							pdecl = classDeclarations[p];
							break;
						}
					}

					if( pdecl )
					{
						if( pdecl->objType == decl->objType )
						{
							int r, c;
							decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c);
							WriteError(decl->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c);
							validState = 2;
							break;
						}
						else if( pdecl->validState != 1 )
						{
							validState = pdecl->validState;
							break;
						}
					}
				}
			}

			if( validState == 1 )
			{
				decl->validState = 1;
				toValidate.PopLast();
			}
			else if( validState == 2 )
			{
				decl->validState = 2;
				toValidate.PopLast();
			}
			else
			{
				toValidateNext.PushLast(toValidate.PopLast());
			}
		}

		toValidate = toValidateNext;
		toValidateNext.SetLength(0);

		if( numClasses == toValidate.GetLength() )
		{
			int r, c;
			toValidate[0]->script->ConvertPosToRowCol(toValidate[0]->node->tokenPos, &r, &c);
			WriteError(toValidate[0]->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c);
			break;
		}
	}

	if( numErrors > 0 ) return;

	// TODO: The declarations form a graph, all circles in   
	//       the graph must be flagged as potential circles

	// Verify potential circular references
	for( n = 0; n < classDeclarations.GetLength(); n++ )
	{
		sClassDeclaration *decl = classDeclarations[n];
		asCObjectType *ot = decl->objType;

		// Is there some path in which this structure is involved in circular references?
		for( asUINT p = 0; p < ot->properties.GetLength(); p++ )
		{
			asCDataType dt = ot->properties[p]->type;
			if( dt.IsObject() )
			{
				if( dt.IsObjectHandle() )
				{
					// TODO: Can this handle really generate a circular reference?
					// Only if the handle is of a type that can reference this type, either directly or indirectly

					ot->flags |= asOBJ_GC;
				}
				else if( dt.GetObjectType()->flags & asOBJ_GC )
				{
					// TODO: Just because the member type is a potential circle doesn't mean that this one is
					// Only if the object is of a type that can reference this type, either directly or indirectly

					ot->flags |= asOBJ_GC;
				}

				if( dt.IsArrayType() )
				{
					asCDataType sub = dt.GetSubType();
					while( sub.IsObject() )
					{
						if( sub.IsObjectHandle() || (sub.GetObjectType()->flags & asOBJ_GC) )
						{
							decl->objType->flags |= asOBJ_GC;

							// Make sure the array object is also marked as potential circle
							sub = dt;
							while( sub.IsTemplate() )
							{
								sub.GetObjectType()->flags |= asOBJ_GC;
								sub = sub.GetSubType();
							}

							break;
						}

						if( sub.IsTemplate() )
							sub = sub.GetSubType();
						else
							break;
					}
				}
			}
		}
	}

	// Verify that the class implements all the methods from the interfaces it implements
	for( n = 0; n < classDeclarations.GetLength(); n++ )
	{
		sClassDeclaration *decl = classDeclarations[n];
		asCScriptCode *file = decl->script;

		// Enumerate each of the implemented interfaces
		asCScriptNode *node = decl->node->firstChild->next;
		while( node && node->nodeType == snIdentifier )
		{
			// Get the interface name from the node
			GETSTRING(name, &file->code[node->tokenPos], node->tokenLength);

			// Find the object type for the interface
			asCObjectType *objType = GetObjectType(name.AddressOf());

			if( objType == 0 )
			{
				int r, c;
				file->ConvertPosToRowCol(node->tokenPos, &r, &c);
				asCString str;
				str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, name.AddressOf());
				WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
			}
			else if( !(objType->flags & asOBJ_SCRIPT_STRUCT) || objType->size != 0 )
			{
				int r, c;
				file->ConvertPosToRowCol(node->tokenPos, &r, &c);
				WriteError(file->name.AddressOf(), TXT_MUST_BE_AN_INTERFACE, r, c);
			}
			else
			{
				if( decl->objType->Implements(objType) )
				{
					int r, c;
					file->ConvertPosToRowCol(node->tokenPos, &r, &c);
					WriteWarning(file->name.AddressOf(), TXT_INTERFACE_ALREADY_IMPLEMENTED, r, c);
				}
				else
				{
					decl->objType->interfaces.PushLast(objType);

					// Make sure all the methods of the interface are implemented
					for( asUINT i = 0; i < objType->methods.GetLength(); i++ )
					{
						if( !DoesMethodExist(decl->objType, objType->methods[i]) )
						{
							int r, c;
							file->ConvertPosToRowCol(decl->node->tokenPos, &r, &c);
							asCString str;
							str.Format(TXT_MISSING_IMPLEMENTATION_OF_s, 
								engine->GetFunctionDeclaration(objType->methods[i]).AddressOf());
							WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
						}
					}
				}
			}

			node = node->next;
		}
	}
}

bool asCBuilder::DoesMethodExist(asCObjectType *objType, int methodId)
{
	asCScriptFunction *method = GetFunctionDescription(methodId);

	for( asUINT n = 0; n < objType->methods.GetLength(); n++ )
	{
		asCScriptFunction *m = GetFunctionDescription(objType->methods[n]);

		if( m->name           != method->name           ) continue;
		if( m->returnType     != method->returnType     ) continue;
		if( m->isReadOnly     != method->isReadOnly     ) continue;
		if( m->parameterTypes != method->parameterTypes ) continue;
		if( m->inOutFlags     != method->inOutFlags     ) continue;

		return true;
	}

	return false;
}

void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file)
{
	int funcId = module->GetNextFunctionId();

	asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false);
	asCArray<asCDataType> parameterTypes;
	asCArray<int> inOutFlags;

	// Add the script function
	module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false, objType);
	
	// Set it as default constructor
	objType->beh.construct = funcId;
	objType->beh.constructors[0] = funcId;

	// Compile the bytecode
	asCCompiler compiler(engine);
	compiler.CompileDefaultConstructor(this, file, engine->scriptFunctions[funcId]);

	// Add a dummy function to the module so that it doesn't mix up the func Ids
	functions.PushLast(0);

	// Add a default factory as well
	funcId = module->GetNextFunctionId();
	objType->beh.factory = funcId;
	objType->beh.factories[0] = funcId;
	returnType = asCDataType::CreateObjectHandle(objType, false);
	module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false);
	functions.PushLast(0);
	compiler.CompileFactory(this, file, engine->scriptFunctions[funcId]);
}

int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file)
{
	int r, c;
	asCString		name;
	asCScriptNode	*tmp;

	//	Grab the name of the enumeration
	tmp = node->firstChild;
	asASSERT(snDataType == tmp->nodeType);
	tmp->DisconnectParent();

	//	Get the name of the enumeration
	asASSERT(snIdentifier == tmp->firstChild->nodeType);
	name.Assign(&file->code[tmp->firstChild->tokenPos], tmp->firstChild->tokenLength);
	file->ConvertPosToRowCol(tmp->firstChild->tokenPos, &r, &c);

	//	Check the name and add the enum
	r = CheckNameConflict(name.AddressOf(), tmp->firstChild, file);
	if(asSUCCESS == r) 
	{
		sClassDeclaration *decl;
		asCObjectType *st;
		asCDataType dataType;

		decl = asNEW(sClassDeclaration);
		st = asNEW(asCObjectType)(engine);
		dataType.CreatePrimitive(ttInt, false);

		st->arrayType = 0;
		st->flags = asOBJ_NAMED_ENUM;
		st->size = dataType.GetSizeInMemoryBytes();
		st->name = name;
		st->tokenType = ttIdentifier;

		decl->name = name;
		decl->script = file;
		decl->validState = 0;
		decl->node = NULL;
		decl->objType = st;

		module->classTypes.PushLast(st);
		st->AddRef();
		engine->classTypes.PushLast(st);
		namedTypeDeclarations.PushLast(decl);

		asCDataType type = CreateDataTypeFromNode(tmp, file);
		// TODO: Give error message if wrong
		asASSERT(!type.IsReference());

		tmp->Destroy(engine);

		while(NULL != node->firstChild) 
		{
			asCScriptNode	*tmp;

			tmp = node->firstChild;
			asASSERT(snIdentifier == tmp->nodeType);

			GETSTRING(name, &file->code[tmp->tokenPos], tmp->tokenLength);
			tmp->DisconnectParent();
			tmp->Destroy(engine);

			// TODO: Should only have to check for conflicts within the enum type
			// Check for name conflict errors
			r = CheckNameConflict(name.AddressOf(), tmp, file);
			if(asSUCCESS != r) 
			{
				continue;
			}

			//	check for assignment
			tmp = node->firstChild;
			if(tmp && snAssignment == tmp->nodeType) 
			{
				tmp->DisconnectParent();
			}
			else 
			{
				tmp = NULL;
			}

			// Create the global variable description so the enum value can be evaluated
			sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription);
			globVariables.PushLast(gvar);

			gvar->script		  = file;
			gvar->node			  = tmp;
			gvar->name			  = name;
			gvar->property        = asNEW(asCGlobalProperty);
			gvar->datatype		  = type;
			// No need to allocate space on the global memory stack since the values are stored in the asCObjectType
			gvar->index			  = 0;
			gvar->isCompiled	  = false;
			gvar->isPureConstant  = true;
			gvar->isEnumValue     = true;
			gvar->constantValue   = 0xdeadbeef;

			// Add script variable to engine
			gvar->property->name  = name;
			gvar->property->type  = gvar->datatype;
			gvar->property->index = gvar->index;
		}
	}

	node->Destroy(engine);

	return r;
}

// TODO: Analyze this properly
int asCBuilder::RegisterTypedef(asCScriptNode *const node, asCScriptCode *file)
{
	int r, c;
	asCString		name;
	asCScriptNode	*tmp;
	bool			isConst;

	isConst = false;

	// Grab the name of the enumeration
	tmp = node->firstChild;
	asASSERT(NULL != tmp);
	if(snConstant == tmp->nodeType) 
	{
		isConst = true;
		tmp = node->next;
	}

	// Get the native data type
	asASSERT(NULL != tmp && snDataType == tmp->nodeType);
	asCDataType dataType;
	dataType.CreatePrimitive(tmp->tokenType, false);
	dataType.SetTokenType(tmp->tokenType);
	tmp = tmp->next;

	// Grab the identifier
	asASSERT(NULL != tmp && NULL == tmp->next);
	name.Assign(&file->code[tmp->tokenPos], tmp->tokenLength);
	file->ConvertPosToRowCol(tmp->tokenPos, &r, &c);

	// If the name is not already in use add it!
 	r = CheckNameConflict(name.AddressOf(), tmp, file);
	if(asSUCCESS == r) 
	{
		// Create the new type
		sClassDeclaration *decl = asNEW(sClassDeclaration);
		asCObjectType *st = asNEW(asCObjectType)(engine);

		st->arrayType = 0;
		st->flags = asOBJ_NAMED_PSEUDO;
		st->size = dataType.GetSizeInMemoryBytes();
		st->name = name;
		st->tokenType = dataType.GetTokenType();

		decl->name = name;
		decl->script = file;
		decl->validState = 0;
		decl->node = NULL;
		decl->objType = st;

		st->AddRef();
		module->classTypes.PushLast(st);
		engine->classTypes.PushLast(st);
		namedTypeDeclarations.PushLast(decl);
	}

	node->Destroy(engine);

	if(r < 0) 
	{
		engine->ConfigError(r);
	}
	return 0;
}

int asCBuilder::RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface)
{
	// Find name 
	bool isConstructor = false;
	bool isDestructor = false;
	asCScriptNode *n = 0;
	if( node->firstChild->nodeType == snDataType )
		n = node->firstChild->next->next;
	else
	{
		// If the first node is a ~ token, then we know it is a destructor
		if( node->firstChild->tokenType == ttBitNot )
		{
			n = node->firstChild->next;
			isDestructor = true;
		}
		else
		{
			n = node->firstChild;
			isConstructor = true;
		}
	}

	// Check for name conflicts
	GETSTRING(name, &file->code[n->tokenPos], n->tokenLength);
	if( !isConstructor && !isDestructor )
	{
		asCDataType dt = asCDataType::CreateObject(objType, false);
		if( objType )
			CheckNameConflictMember(dt, name.AddressOf(), n, file);
		else
			CheckNameConflict(name.AddressOf(), n, file);
	}
	else
	{
		// Verify that the name of the constructor/destructor is the same as the class
		if( name != objType->name )
		{
			int r, c;
			file->ConvertPosToRowCol(n->tokenPos, &r, &c);
			WriteError(file->name.AddressOf(), TXT_CONSTRUCTOR_NAME_ERROR, r, c);
		}
	}

	if( !isInterface )
	{
		sFunctionDescription *func = asNEW(sFunctionDescription);
		functions.PushLast(func);

		func->script  = file;
		func->node    = node;
		if( isDestructor )
			func->name = "~" + name;
		else
			func->name = name;
		func->objType = objType;
		func->funcId  = funcID;
	}

	// Initialize a script function object for registration
	asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false);
	if( !isConstructor && !isDestructor )
	{
		returnType = CreateDataTypeFromNode(node->firstChild, file);
		returnType = ModifyDataTypeFromNode(returnType, node->firstChild->next, file, 0, 0);
	}

	// Is this a const method?
	bool isConstMethod = false;
	if( objType && n->next->next && n->next->next->tokenType == ttConst )
		isConstMethod = true;

	// Count the number of parameters
	int count = 0;
	asCScriptNode *c = n->next->firstChild;
	while( c )
	{
		count++;
		c = c->next->next;
		if( c && c->nodeType == snIdentifier )
			c = c->next;
	}

	// Destructors may not have any parameters
	if( isDestructor && count > 0 )
	{
		int r, c;
		file->ConvertPosToRowCol(node->tokenPos, &r, &c);

		WriteError(file->name.AddressOf(), TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM, r, c);
	}

	asCArray<asCDataType> parameterTypes(count);
	asCArray<int> inOutFlags(count);
	n = n->next->firstChild;
	while( n )
	{
		int inOutFlag;
		asCDataType type = CreateDataTypeFromNode(n, file);
		type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0);

		// Store the parameter type
		parameterTypes.PushLast(type);
		inOutFlags.PushLast(inOutFlag);

		// Move to next parameter
		n = n->next->next;
		if( n && n->nodeType == snIdentifier )
			n = n->next;
	}

	// Check that the same function hasn't been registered already
	asCArray<int> funcs;
	GetFunctionDescriptions(name.AddressOf(), funcs);
	if( funcs.GetLength() )
	{
		for( asUINT n = 0; n < funcs.GetLength(); ++n )
		{
			asCScriptFunction *func = GetFunctionDescription(funcs[n]);

			if( parameterTypes.GetLength() == func->parameterTypes.GetLength() )
			{
				bool match = true;
				if( func->objectType != objType )
				{
					match = false;
					break;
				}
				
				for( asUINT p = 0; p < parameterTypes.GetLength(); ++p )
				{
					if( parameterTypes[p] != func->parameterTypes[p] )
					{
						match = false;
						break;
					}
				}

				if( match )
				{
					int r, c;
					file->ConvertPosToRowCol(node->tokenPos, &r, &c);

					WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c);
					break;
				}
			}
		}
	}

	// Register the function
	module->AddScriptFunction(file->idx, funcID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), isInterface, objType, isConstMethod);

	if( objType )
	{
		if( isConstructor )
		{
			if( parameterTypes.GetLength() == 0 )
			{
				// Overload the default constructor
				objType->beh.construct = funcID;
				objType->beh.constructors[0] = funcID;

				// Register the default factory as well
				objType->beh.factory = module->GetNextFunctionId();
				objType->beh.factories[0] = objType->beh.factory;

				asCDataType dt = asCDataType::CreateObjectHandle(objType, false);
				module->AddScriptFunction(file->idx, objType->beh.factory, name.AddressOf(), dt, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false);

				// Add a dummy function to the module so that it doesn't mix up the func Ids
				functions.PushLast(0);

				// Compile the factory immediately
				asCCompiler	compiler(engine);
				compiler.CompileFactory(this, file, engine->scriptFunctions[objType->beh.factory]);
			}
			else
			{
				objType->beh.constructors.PushLast(funcID);

				// TODO: This is almost identical to above if block. Should be reduced to common code.
				// Register the factory as well
				int factoryId = module->GetNextFunctionId();
				objType->beh.factories.PushLast(factoryId);

				asCDataType dt = asCDataType::CreateObjectHandle(objType, false);
				module->AddScriptFunction(file->idx, factoryId, name.AddressOf(), dt, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false);

				// Add a dummy function to the module so that it doesn't mix up the fund Ids
				functions.PushLast(0);

				// Compile the factory immediately
				asCCompiler compiler(engine);
				compiler.CompileFactory(this, file, engine->scriptFunctions[factoryId]);
			}
		}
		else if( isDestructor )
			objType->beh.destruct = funcID;
		else
			objType->methods.PushLast(funcID);
	}

	// We need to delete the node already if this is an interface method
	if( isInterface && node )
	{
		node->Destroy(engine);
	}

	return 0;
}

int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCScriptCode *file)
{
	// Find name 
	asCScriptNode *f = node->firstChild;
	asCScriptNode *n = f->firstChild->next->next;

	// Check for name conflicts
	GETSTRING(name, &file->code[n->tokenPos], n->tokenLength);
	CheckNameConflict(name.AddressOf(), n, file);

	// Initialize a script function object for registration
	asCDataType returnType;
	returnType = CreateDataTypeFromNode(f->firstChild, file);
	returnType = ModifyDataTypeFromNode(returnType, f->firstChild->next, file, 0, 0);
		
	// Count the parameters
	int count = 0;
	asCScriptNode *c = n->next->firstChild;
	while( c )
	{
		count++;
		c = c->next->next;
		if( c && c->nodeType == snIdentifier )
			c = c->next;
	}

	asCArray<asCDataType> parameterTypes(count);
	asCArray<int> inOutFlags(count);
	n = n->next->firstChild;
	while( n )
	{
		int inOutFlag;
		asCDataType type = CreateDataTypeFromNode(n, file);
		type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0);

		// Store the parameter type
		n = n->next->next;
		parameterTypes.PushLast(type);
		inOutFlags.PushLast(inOutFlag);

		// Move to next parameter
		if( n && n->nodeType == snIdentifier )
			n = n->next;
	}

	// Check that the same function hasn't been registered already
	asCArray<int> funcs;
	GetFunctionDescriptions(name.AddressOf(), funcs);
	if( funcs.GetLength() )
	{
		for( asUINT n = 0; n < funcs.GetLength(); ++n )
		{
			asCScriptFunction *func = GetFunctionDescription(funcs[n]);

			// TODO: Isn't the name guaranteed to be equal, because of GetFunctionDescriptions()?
			if( name == func->name && 
				parameterTypes.GetLength() == func->parameterTypes.GetLength() )
			{
				bool match = true;
				for( asUINT p = 0; p < parameterTypes.GetLength(); ++p )
				{
					if( parameterTypes[p] != func->parameterTypes[p] )
					{
						match = false;
						break;
					}
				}

				if( match )
				{
					int r, c;
					file->ConvertPosToRowCol(node->tokenPos, &r, &c);

					WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c);
					break;
				}
			}
		}
		
	}

	// Read the module name as well
	n = node->firstChild->next;
	int moduleNameString = module->AddConstantString(&file->code[n->tokenPos+1], n->tokenLength-2);

	node->Destroy(engine);

	// Register the function
	module->AddImportedFunction(importID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), moduleNameString);

	return 0;
}


asCScriptFunction *asCBuilder::GetFunctionDescription(int id)
{
	// TODO: This should be improved
	// Get the description from the engine
	if( (id & 0xFFFF0000) == 0 )
		return engine->scriptFunctions[id];
	else 
		return module->importedFunctions[id & 0xFFFF];
}

void asCBuilder::GetFunctionDescriptions(const char *name, asCArray<int> &funcs)
{
	asUINT n;
	// TODO: optimize: Improve linear search
	for( n = 0; n < module->scriptFunctions.GetLength(); n++ )
	{
		if( module->scriptFunctions[n]->name == name &&
			module->scriptFunctions[n]->objectType == 0 )
			funcs.PushLast(module->scriptFunctions[n]->id);
	}

	// TODO: optimize: Improve linear search
	for( n = 0; n < module->importedFunctions.GetLength(); n++ )
	{
		if( module->importedFunctions[n]->name == name )
			funcs.PushLast(module->importedFunctions[n]->id);
	}

	// TODO: optimize: Improve linear search
	for( n = 0; n < engine->scriptFunctions.GetLength(); n++ )
	{
		if( engine->scriptFunctions[n] && 
			engine->scriptFunctions[n]->funcType == asFUNC_SYSTEM && 
			engine->scriptFunctions[n]->objectType == 0 && 
			engine->scriptFunctions[n]->name == name )
		{
			// Find the config group for the global function
			asCConfigGroup *group = engine->FindConfigGroupForFunction(engine->scriptFunctions[n]->id);
			if( !group || group->HasModuleAccess(module->name.AddressOf()) )
				funcs.PushLast(engine->scriptFunctions[n]->id);
		}
	}
}

void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray<int> &methods, bool objIsConst)
{
	// TODO: optimize: Improve linear search
	if( objIsConst )
	{
		// Only add const methods to the list
		for( asUINT n = 0; n < objectType->methods.GetLength(); n++ )
		{
			if( engine->scriptFunctions[objectType->methods[n]]->name == name &&
				engine->scriptFunctions[objectType->methods[n]]->isReadOnly )
				methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id);
		}
	}
	else
	{
		// TODO: Prefer non-const over const
		for( asUINT n = 0; n < objectType->methods.GetLength(); n++ )
		{
			if( engine->scriptFunctions[objectType->methods[n]]->name == name )
				methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id);
		}
	}
}

void asCBuilder::WriteInfo(const char *scriptname, const char *message, int r, int c, bool pre)
{
	// Need to store the pre message in a structure
	if( pre )
	{
		preMessage.isSet = true;
		preMessage.c = c;
		preMessage.r = r;
		preMessage.message = message;
	}
	else
	{
		preMessage.isSet = false;
		engine->WriteMessage(scriptname, r, c, asMSGTYPE_INFORMATION, message);
	}
}

void asCBuilder::WriteError(const char *scriptname, const char *message, int r, int c)
{
	numErrors++;

	// Need to pass the preMessage first
	if( preMessage.isSet )
		WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false);

	engine->WriteMessage(scriptname, r, c, asMSGTYPE_ERROR, message);
}

void asCBuilder::WriteWarning(const char *scriptname, const char *message, int r, int c)
{
	numWarnings++;

	// Need to pass the preMessage first
	if( preMessage.isSet )
		WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false);

	engine->WriteMessage(scriptname, r, c, asMSGTYPE_WARNING, message);
}


asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, bool acceptHandleForScope)
{
	asASSERT(node->nodeType == snDataType);

	asCDataType dt;

	asCScriptNode *n = node->firstChild;

	bool isConst = false;
	bool isImplicitHandle = false;
	if( n->tokenType == ttConst )
	{
		isConst = true;
		n = n->next;
	}

	if( n->tokenType == ttIdentifier )
	{
		asCString str;
		str.Assign(&file->code[n->tokenPos], n->tokenLength);

		asCObjectType *ot = GetObjectType(str.AddressOf());
		if( ot == 0 )
		{
			asCString msg;
			msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, (const char *)str.AddressOf());

			int r, c;
			file->ConvertPosToRowCol(n->tokenPos, &r, &c);

			WriteError(file->name.AddressOf(), msg.AddressOf(), r, c);

			dt.SetTokenType(ttInt);
		}
		else
		{
			if( ot->flags & asOBJ_IMPLICIT_HANDLE )
				isImplicitHandle = true;

			// Find the config group for the object type
			asCConfigGroup *group = engine->FindConfigGroupForObjectType(ot);
			if( !module || !group || group->HasModuleAccess(module->name.AddressOf()) )
			{
				if(asOBJ_NAMED_PSEUDO == (ot->flags & asOBJ_NAMED_PSEUDO))
				{
					// Create primitive data type based on object flags
					dt = asCDataType::CreatePrimitive(ot->tokenType, isConst);
				}
				else
				{
					// Create object data type
					dt = asCDataType::CreateObject(ot, isConst);
				}
			}
			else
			{
				asCString msg;
				msg.Format(TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE, (const char *)str.AddressOf());

				int r, c;
				file->ConvertPosToRowCol(n->tokenPos, &r, &c);

				WriteError(file->name.AddressOf(), msg.AddressOf(), r, c);

				dt.SetTokenType(ttInt);
			}
		}
	}
	else
	{
		// Create primitive data type
		dt = asCDataType::CreatePrimitive(n->tokenType, isConst);
	}

	// Determine array dimensions and object handles
	n = n->next;
	while( n && (n->tokenType == ttOpenBracket || n->tokenType == ttHandle) )
	{
		if( n->tokenType == ttOpenBracket )
		{
			// Make sure the sub type can be instanciated
			if( !dt.CanBeInstanciated() )
			{
				int r, c;
				file->ConvertPosToRowCol(n->tokenPos, &r, &c);

				asCString str;
				// TODO: Change to "Array sub type cannot be 'type'"
				str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());

				WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
			}
			
			// Make the type an array (or multidimensional array)
			if( dt.MakeArray(engine) < 0 )
			{
				int r, c;
				file->ConvertPosToRowCol(n->tokenPos, &r, &c);
				WriteError(file->name.AddressOf(), TXT_TOO_MANY_ARRAY_DIMENSIONS, r, c);
				break;
			}
		}
		else
		{
			// Make the type a handle
			if( dt.MakeHandle(true, acceptHandleForScope) < 0 )
			{
				int r, c;
				file->ConvertPosToRowCol(n->tokenPos, &r, &c);
				WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c);
				break;
			}
		}
		n = n->next;
	}

	if( isImplicitHandle )
	{
		// Make the type a handle
		if( dt.MakeHandle(true, acceptHandleForScope) < 0 )
		{
			int r, c;
			file->ConvertPosToRowCol(n->tokenPos, &r, &c);
			WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c);
		}
	}

	return dt;
}

asCDataType asCBuilder::ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, int *inOutFlags, bool *autoHandle)
{
	asCDataType dt = type;

	if( inOutFlags ) *inOutFlags = 0;

	// Is the argument sent by reference?
	asCScriptNode *n = node->firstChild;
	if( n && n->tokenType == ttAmp )
	{
		dt.MakeReference(true);
		n = n->next;

		if( n )
		{
			if( inOutFlags )
			{
				if( n->tokenType == ttIn ) 
					*inOutFlags = 1;
				else if( n->tokenType == ttOut )
					*inOutFlags = 2;
				else if( n->tokenType == ttInOut )
					*inOutFlags = 3;
				else
					asASSERT(false);
			}

			n = n->next;
		}
		else
		{
			if( inOutFlags )
				*inOutFlags = 3; // ttInOut
		}

		if( !engine->ep.allowUnsafeReferences &&
			inOutFlags && *inOutFlags == 3 )
		{				
			// Verify that the base type support &inout parameter types
			if( !dt.IsObject() || dt.IsObjectHandle() || !dt.GetObjectType()->beh.addref || !dt.GetObjectType()->beh.release )
			{
				int r, c;
				file->ConvertPosToRowCol(node->firstChild->tokenPos, &r, &c);
				WriteError(file->name.AddressOf(), TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT, r, c);
			}
		}
	}

	if( autoHandle ) *autoHandle = false;

	if( n && n->tokenType == ttPlus )
	{
		if( autoHandle ) *autoHandle = true;
	}

	return dt;
}

const asCString &asCBuilder::GetConstantString(int strID)
{
	return module->GetConstantString(strID);
}

asCObjectType *asCBuilder::GetObjectType(const char *type)
{
	// TODO: Only search in config groups to which the module has access
	asCObjectType *ot = engine->GetObjectType(type);
	if( !ot && module )
		ot = module->GetObjectType(type);

	return ot;
}

int asCBuilder::GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue)
{
	bool found = false;

	// Search all available enum types
	asUINT t;
	for( t = 0; t < engine->objectTypes.GetLength(); t++ )
	{
		asCObjectType *ot = engine->objectTypes[t];
		if( ot && (ot->flags & asOBJ_NAMED_ENUM) )
		{
			for( asUINT n = 0; n < ot->enumValues.GetLength(); n++ )
			{
				if( ot->enumValues[n]->name == name )
				{
					if( !found )
					{
						found = true;
						outDt = asCDataType::CreateObject(ot, true);
						outValue = ot->enumValues[n]->value;
						break;
					}
					else
					{
						// Found more than one value in different enum types
						return 2;
					}
				}
			}
		}
	}

	for( t = 0; t < module->classTypes.GetLength(); t++ )
	{
		asCObjectType *ot = module->classTypes[t];
		if( ot && (ot->flags & asOBJ_NAMED_ENUM) )
		{
			for( asUINT n = 0; n < ot->enumValues.GetLength(); n++ )
			{
				if( ot->enumValues[n]->name == name )
				{
					if( !found )
					{
						found = true;
						outDt = asCDataType::CreateObject(ot, true);
						outValue = ot->enumValues[n]->value;
						break;
					}
					else
					{
						// Found more than one value in different enum types
						return 2;
					}
				}
			}
		}
	}

	if( found )
		return 1;

	// Didn't find any value
	return 0;
}

END_AS_NAMESPACE
