#include <assert.h>
#include <string.h>
#include "scriptdictionary.h"
#include "../scriptarray/scriptarray.h"

BEGIN_AS_NAMESPACE

using namespace std;

//------------------------------------------------------------------------
// Object types are cached as user data to avoid costly runtime lookups

// We just define a number here that we assume nobody else is using for
// object type user data. The add-ons have reserved the numbers 1000
// through 1999 for this purpose, so we should be fine.
const asPWORD DICTIONARY_CACHE = 1003;

// This cache holds the object type of the dictionary type and array type
// so it isn't necessary to look this up each time the dictionary or array
// is created.
struct SDictionaryCache
{
	asITypeInfo *dictType;
	asITypeInfo *arrayType;
	asITypeInfo *keyType;

	// This is called from RegisterScriptDictionary
	static void Setup(asIScriptEngine *engine)
	{
		SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
		if( cache == 0 )
		{
			cache = new SDictionaryCache;
			engine->SetUserData(cache, DICTIONARY_CACHE);
			engine->SetEngineUserDataCleanupCallback(SDictionaryCache::Cleanup, DICTIONARY_CACHE);

			cache->dictType = engine->GetTypeInfoByName("dictionary");
			cache->arrayType = engine->GetTypeInfoByDecl("array<string>");
			cache->keyType = engine->GetTypeInfoByDecl("string");
		}
	}

	// This is called from the engine when shutting down
	static void Cleanup(asIScriptEngine *engine)
	{
		SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
		if( cache )
			delete cache;
	}
};

//--------------------------------------------------------------------------
// CScriptDictionary implementation

CScriptDictionary *CScriptDictionary::Create(asIScriptEngine *engine)
{
	// Use the custom memory routine from AngelScript to allow application to better control how much memory is used
	CScriptDictionary *obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
	new(obj) CScriptDictionary(engine);
	return obj;
}

CScriptDictionary *CScriptDictionary::Create(asBYTE *buffer)
{
	// Use the custom memory routine from AngelScript to allow application to better control how much memory is used
	CScriptDictionary *obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
	new(obj) CScriptDictionary(buffer);
	return obj;
}

CScriptDictionary::CScriptDictionary(asIScriptEngine *engine)
{
	Init(engine);
}

void CScriptDictionary::Init(asIScriptEngine *e)
{
	// We start with one reference
	refCount = 1;
	gcFlag = false;

	// Keep a reference to the engine for as long as we live
	// We don't increment the reference counter, because the 
	// engine will hold a pointer to the object in the GC.
	engine = e;

	// The dictionary object type is cached to avoid dynamically parsing it each time
	SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));

	// Notify the garbage collector of this object
	engine->NotifyGarbageCollectorOfNewObject(this, cache->dictType);
}

CScriptDictionary::CScriptDictionary(asBYTE *buffer)
{
	// This constructor will always be called from a script
	// so we can get the engine from the active context
	asIScriptContext *ctx = asGetActiveContext();
	Init(ctx->GetEngine());
	
	// Determine if the dictionary key type is registered as reference type or value type
	SDictionaryCache& cache = *reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
	bool keyAsRef = cache.keyType->GetFlags() & asOBJ_REF ? true : false;

	// Initialize the dictionary from the buffer
	asUINT length = *(asUINT*)buffer;
	buffer += 4;

	while( length-- )
	{
		// Align the buffer pointer on a 4 byte boundary in 
		// case previous value was smaller than 4 bytes
		if( asPWORD(buffer) & 0x3 )
			buffer += 4 - (asPWORD(buffer) & 0x3);

		// Get the name value pair from the buffer and insert it in the dictionary
		dictKey_t name;
		if (keyAsRef)
		{
			name = **(dictKey_t**)buffer;
			buffer += sizeof(dictKey_t*);
		}
		else
		{
			name = *(dictKey_t*)buffer;
			buffer += sizeof(dictKey_t);
		}

		// Get the type id of the value
		int typeId = *(int*)buffer;
		buffer += sizeof(int);

		// Depending on the type id, the value will inline in the buffer or a pointer
		void *ref = (void*)buffer;

		if( typeId >= asTYPEID_INT8 && typeId <= asTYPEID_DOUBLE )
		{
			// Convert primitive values to either int64 or double, so we can use the overloaded Set methods
			asINT64 i64;
			double d;
			switch( typeId )
			{
			case asTYPEID_INT8:   i64 = *(char*)          ref; break;
			case asTYPEID_INT16:  i64 = *(short*)         ref; break;
			case asTYPEID_INT32:  i64 = *(int*)           ref; break;
			case asTYPEID_INT64:  i64 = *(asINT64*)       ref; break;
			case asTYPEID_UINT8:  i64 = *(unsigned char*) ref; break;
			case asTYPEID_UINT16: i64 = *(unsigned short*)ref; break;
			case asTYPEID_UINT32: i64 = *(unsigned int*)  ref; break;
			case asTYPEID_UINT64: i64 = *(asINT64*)       ref; break;
			case asTYPEID_FLOAT:  d   = *(float*)         ref; break;
			case asTYPEID_DOUBLE: d   = *(double*)        ref; break;
			}
			
			if( typeId >= asTYPEID_FLOAT )
				Set(name, d);
			else
				Set(name, i64);
		}
		else
		{
			if( (typeId & asTYPEID_MASK_OBJECT) && 
				!(typeId & asTYPEID_OBJHANDLE) && 
				(engine->GetTypeInfoById(typeId)->GetFlags() & asOBJ_REF) )
			{
				// Dereference the pointer to get the reference to the actual object
				ref = *(void**)ref;
			}

			Set(name, ref, typeId);
		}

		// Advance the buffer pointer with the size of the value
		if( typeId & asTYPEID_MASK_OBJECT )
		{
			asITypeInfo *ti = engine->GetTypeInfoById(typeId);
			if( ti->GetFlags() & asOBJ_VALUE )
				buffer += ti->GetSize();
			else
				buffer += sizeof(void*);
		}
		else if( typeId == 0 )
		{
			// null pointer
			buffer += sizeof(void*);
		}
		else
		{
			buffer += engine->GetSizeOfPrimitiveType(typeId);
		}
	}
}

CScriptDictionary::~CScriptDictionary()
{
	// Delete all keys and values
	DeleteAll();
}

void CScriptDictionary::AddRef() const
{
	// We need to clear the GC flag
	gcFlag = false;
	asAtomicInc(refCount);
}

void CScriptDictionary::Release() const
{
	// We need to clear the GC flag
	gcFlag = false;
	if( asAtomicDec(refCount) == 0 )
	{
		this->~CScriptDictionary();
		asFreeMem(const_cast<CScriptDictionary*>(this));
	}
}

int CScriptDictionary::GetRefCount()
{
	return refCount;
}

void CScriptDictionary::SetGCFlag()
{
	gcFlag = true;
}

bool CScriptDictionary::GetGCFlag()
{
	return gcFlag;
}

void CScriptDictionary::EnumReferences(asIScriptEngine *inEngine)
{
	// TODO: If garbage collection can be done from a separate thread, then this method must be
	//       protected so that it doesn't get lost during the iteration if the dictionary is modified

	// Call the gc enum callback for each of the objects
	dictMap_t::iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
	{
		if (it->second.m_typeId & asTYPEID_MASK_OBJECT)
		{
			asITypeInfo *subType = engine->GetTypeInfoById(it->second.m_typeId);
			if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
			{
				// For value types we need to forward the enum callback
				// to the object so it can decide what to do
				engine->ForwardGCEnumReferences(it->second.m_valueObj, subType);
			}
			else
			{
				// For others, simply notify the GC about the reference
				inEngine->GCEnumCallback(it->second.m_valueObj);
			}
		}
	}
}

void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/)
{
	// We're being told to release all references in 
	// order to break circular references for dead objects
	DeleteAll();
}

CScriptDictionary &CScriptDictionary::operator =(const CScriptDictionary &other)
{
	// Clear everything we had before
	DeleteAll();

	// Do a shallow copy of the dictionary
	dictMap_t::const_iterator it;
	for( it = other.dict.begin(); it != other.dict.end(); it++ )
	{
		if( it->second.m_typeId & asTYPEID_OBJHANDLE )
			Set(it->first, (void*)&it->second.m_valueObj, it->second.m_typeId);
		else if( it->second.m_typeId & asTYPEID_MASK_OBJECT )
			Set(it->first, (void*)it->second.m_valueObj, it->second.m_typeId);
		else
			Set(it->first, (void*)&it->second.m_valueInt, it->second.m_typeId);
	}

	return *this;
}

CScriptDictValue *CScriptDictionary::operator[](const dictKey_t &key)
{
	// Return the existing value if it exists, else insert an empty value
	return &dict[key];
}

const CScriptDictValue *CScriptDictionary::operator[](const dictKey_t &key) const
{
	// Return the existing value if it exists
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return &it->second;

	// Else raise an exception
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
		ctx->SetException("Invalid access to non-existing value");

	return 0;
}

void CScriptDictionary::Set(const dictKey_t &key, void *value, int typeId)
{
	dictMap_t::iterator it;
	it = dict.find(key);
	if( it == dict.end() )
		it = dict.insert(dictMap_t::value_type(key, CScriptDictValue())).first;

	it->second.Set(engine, value, typeId);
}

// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the dictionary as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a 
// different type.
void CScriptDictionary::Set(const dictKey_t &key, const asINT64 &value)
{
	Set(key, const_cast<asINT64*>(&value), asTYPEID_INT64);
}

// This overloaded method is implemented so that all floating point types 
// will be stored in the dictionary as double through implicit conversions. 
// This simplifies the management of the numeric types when the script 
// retrieves the stored value using a different type.
void CScriptDictionary::Set(const dictKey_t &key, const double &value)
{
	Set(key, const_cast<double*>(&value), asTYPEID_DOUBLE);
}

// Returns true if the value was successfully retrieved
bool CScriptDictionary::Get(const dictKey_t &key, void *value, int typeId) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return it->second.Get(engine, value, typeId);

	// AngelScript has already initialized the value with a default value,
	// so we don't have to do anything if we don't find the element, or if 
	// the element is incompatible with the requested type.

	return false;
}

// Returns the type id of the stored value
int CScriptDictionary::GetTypeId(const dictKey_t &key) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return it->second.m_typeId;

	return -1;
}

bool CScriptDictionary::Get(const dictKey_t &key, asINT64 &value) const
{
	return Get(key, &value, asTYPEID_INT64);
}

bool CScriptDictionary::Get(const dictKey_t &key, double &value) const
{
	return Get(key, &value, asTYPEID_DOUBLE);
}

bool CScriptDictionary::Exists(const dictKey_t &key) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return true;

	return false;
}

bool CScriptDictionary::IsEmpty() const
{
	if( dict.size() == 0 )
		return true;

	return false;
}

asUINT CScriptDictionary::GetSize() const
{
	return asUINT(dict.size());
}

bool CScriptDictionary::Delete(const dictKey_t &key)
{
	dictMap_t::iterator it;
	it = dict.find(key);
	if( it != dict.end() )
	{
		it->second.FreeValue(engine);
		dict.erase(it);
		return true;
	}

	return false;
}

void CScriptDictionary::DeleteAll()
{
	dictMap_t::iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
		it->second.FreeValue(engine);

	dict.clear();
}

CScriptArray* CScriptDictionary::GetKeys() const
{
	// Retrieve the object type for the array<string> from the cache
	SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
	asITypeInfo *ti = cache->arrayType;

	// Create the array object
	CScriptArray *array = CScriptArray::Create(ti, asUINT(dict.size()));
	long current = -1;
	dictMap_t::const_iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
	{
		current++;
		*(dictKey_t*)array->At(current) = it->first;
	}

	return array;
}

//--------------------------------------------------------------------------
// Generic wrappers

void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen)
{
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(gen->GetEngine());
}

void ScriptDictionaryListFactory_Generic(asIScriptGeneric *gen)
{
	asBYTE *buffer = (asBYTE*)gen->GetArgAddress(0);
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(buffer);
}

void ScriptDictionaryAddRef_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->AddRef();
}

void ScriptDictionaryRelease_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->Release();
}

void ScriptDictionaryAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	CScriptDictionary *other = *(CScriptDictionary**)gen->GetAddressOfArg(0);
	*dict = *other;
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = dict;
}

void ScriptDictionarySet_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	int typeId = gen->GetArgTypeId(1);
	dict->Set(*key, ref, typeId);
}

void ScriptDictionarySetInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	dict->Set(*key, *(asINT64*)ref);
}

void ScriptDictionarySetFlt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	dict->Set(*key, *(double*)ref);
}

void ScriptDictionaryGet_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	int typeId = gen->GetArgTypeId(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, typeId);
}

void ScriptDictionaryGetInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(asINT64*)ref);
}

void ScriptDictionaryGetFlt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(double*)ref);
}

void ScriptDictionaryExists_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	bool ret = dict->Exists(*key);
	*(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryIsEmpty_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	bool ret = dict->IsEmpty();
	*(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryGetSize_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	asUINT ret = dict->GetSize();
	*(asUINT*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryDelete_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Delete(*key);
}

void ScriptDictionaryDeleteAll_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->DeleteAll();
}

static void ScriptDictionaryGetRefCount_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}

static void ScriptDictionarySetGCFlag_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	self->SetGCFlag();
}

static void ScriptDictionaryGetGCFlag_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag();
}

static void ScriptDictionaryEnumReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->EnumReferences(engine);
}

static void ScriptDictionaryReleaseAllReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->ReleaseAllReferences(engine);
}

static void CScriptDictionaryGetKeys_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(CScriptArray**)gen->GetAddressOfReturnLocation() = self->GetKeys();
}

static void CScriptDictionary_opIndex_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}

static void CScriptDictionary_opIndex_const_Generic(asIScriptGeneric *gen)
{
	const CScriptDictionary *self = (const CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(const CScriptDictValue**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}


//-------------------------------------------------------------------------
// CScriptDictValue

CScriptDictValue::CScriptDictValue()
{
	m_valueObj = 0;
	m_typeId   = 0;
}

CScriptDictValue::CScriptDictValue(asIScriptEngine *engine, void *value, int typeId)
{
	m_valueObj = 0;
	m_typeId   = 0;
	Set(engine, value, typeId);
}

CScriptDictValue::~CScriptDictValue()
{
	if (m_valueObj && m_typeId)
	{
		asIScriptContext *ctx = asGetActiveContext();
		if (ctx)
			FreeValue(ctx->GetEngine());
		else
		{
			// Must not hold an object when destroyed, as then the object will never be freed
			assert((m_typeId & asTYPEID_MASK_OBJECT) == 0);
		}
	}
}

void CScriptDictValue::FreeValue(asIScriptEngine *engine)
{
	// If it is a handle or a ref counted object, call release
	if( m_typeId & asTYPEID_MASK_OBJECT )
	{
		// Let the engine release the object
		engine->ReleaseScriptObject(m_valueObj, engine->GetTypeInfoById(m_typeId));
		m_valueObj = 0;
		m_typeId = 0;
	}

	// For primitives, there's nothing to do
}

void CScriptDictValue::EnumReferences(asIScriptEngine *inEngine)
{
	// If we're holding a reference, we'll notify the garbage collector of it
	if (m_valueObj)
		inEngine->GCEnumCallback(m_valueObj);

	// The object type itself is also garbage collected
	if (m_typeId)
		inEngine->GCEnumCallback(inEngine->GetTypeInfoById(m_typeId));
}

void CScriptDictValue::Set(asIScriptEngine *engine, void *value, int typeId)
{
	FreeValue(engine);

	m_typeId = typeId;
	if( typeId & asTYPEID_OBJHANDLE )
	{
		// We're receiving a reference to the handle, so we need to dereference it
		m_valueObj = *(void**)value;
		engine->AddRefScriptObject(m_valueObj, engine->GetTypeInfoById(typeId));
	}
	else if( typeId & asTYPEID_MASK_OBJECT )
	{
		// Create a copy of the object
		m_valueObj = engine->CreateScriptObjectCopy(value, engine->GetTypeInfoById(typeId));
		if( m_valueObj == 0 )
		{
			asIScriptContext *ctx = asGetActiveContext();
			if( ctx )
				ctx->SetException("Cannot create copy of object");
		}
	}
	else
	{
		// Copy the primitive value
		// We receive a pointer to the value.
		int size = engine->GetSizeOfPrimitiveType(typeId);
		memcpy(&m_valueInt, value, size);
	}
}

void CScriptDictValue::Set(asIScriptEngine *engine, CScriptDictValue &value)
{
	if( value.m_typeId & asTYPEID_OBJHANDLE )
		Set(engine, (void*)&value.m_valueObj, value.m_typeId);
	else if( value.m_typeId & asTYPEID_MASK_OBJECT )
		Set(engine, (void*)value.m_valueObj, value.m_typeId);
	else
		Set(engine, (void*)&value.m_valueInt, value.m_typeId);
}

// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the dictionary as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a 
// different type.
void CScriptDictValue::Set(asIScriptEngine *engine, const asINT64 &value)
{
	Set(engine, const_cast<asINT64*>(&value), asTYPEID_INT64);
}

// This overloaded method is implemented so that all floating point types 
// will be stored in the dictionary as double through implicit conversions. 
// This simplifies the management of the numeric types when the script 
// retrieves the stored value using a different type.
void CScriptDictValue::Set(asIScriptEngine *engine, const double &value)
{
	Set(engine, const_cast<double*>(&value), asTYPEID_DOUBLE);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, void *value, int typeId) const
{
	// Return the value
	if( typeId & asTYPEID_OBJHANDLE )
	{
		// A handle can be retrieved if the stored type is a handle of same or compatible type
		// or if the stored type is an object that implements the interface that the handle refer to.
		if( (m_typeId & asTYPEID_MASK_OBJECT) )
		{
			// Don't allow the get if the stored handle is to a const, but the desired handle is not
			if( (m_typeId & asTYPEID_HANDLETOCONST) && !(typeId & asTYPEID_HANDLETOCONST) )
				return false;

			// RefCastObject will increment the refcount if successful
			engine->RefCastObject(m_valueObj, engine->GetTypeInfoById(m_typeId), engine->GetTypeInfoById(typeId), reinterpret_cast<void**>(value));

			return true;
		}
	}
	else if( typeId & asTYPEID_MASK_OBJECT )
	{
		// Verify that the copy can be made
		bool isCompatible = false;

		// Allow a handle to be value assigned if the wanted type is not a handle
		if( (m_typeId & ~(asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST) ) == typeId && m_valueObj != 0 )
			isCompatible = true;

		// Copy the object into the given reference
		if( isCompatible )
		{
			engine->AssignScriptObject(value, m_valueObj, engine->GetTypeInfoById(typeId));

			return true;
		}
	}
	else
	{
		if( m_typeId == typeId )
		{
			int size = engine->GetSizeOfPrimitiveType(typeId);
			memcpy(value, &m_valueInt, size);
			return true;
		}

		// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
		// Only bool and enums needs to be treated separately
		if( typeId == asTYPEID_DOUBLE )
		{
			if( m_typeId == asTYPEID_INT64 )
				*(double*)value = double(m_valueInt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(double*)value = localValue ? 1.0 : 0.0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(double*)value = double(localValue); // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(double*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId == asTYPEID_INT64 )
		{
			if( m_typeId == asTYPEID_DOUBLE )
				*(asINT64*)value = asINT64(m_valueFlt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(asINT64*)value = localValue ? 1 : 0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(asINT64*)value = localValue; // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(asINT64*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0 )
		{
			// The desired type is an enum. These are always 32bit integers
			if( m_typeId == asTYPEID_DOUBLE )
				*(int*)value = int(m_valueFlt);
			else if( m_typeId == asTYPEID_INT64 )
				*(int*)value = int(m_valueInt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(int*)value = localValue ? 1 : 0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(int*)value = localValue; // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(int*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId == asTYPEID_BOOL )
		{
			if (m_typeId & asTYPEID_OBJHANDLE)
			{
				// TODO: Check if the object has a conversion operator to a primitive value
				*(bool*)value = m_valueObj ? true : false;
			}
			else if( m_typeId & asTYPEID_MASK_OBJECT )
			{
				// TODO: Check if the object has a conversion operator to a primitive value
				*(bool*)value = true;
			}
			else
			{
				// Compare only the bytes that were actually set
				asQWORD zero = 0;
				int size = engine->GetSizeOfPrimitiveType(m_typeId);
				*(bool*)value = memcmp(&m_valueInt, &zero, size) == 0 ? false : true;
			}
			return true;
		}
	}

	// It was not possible to retrieve the value using the desired typeId
	return false;
}

const void * CScriptDictValue::GetAddressOfValue() const
{
	if( (m_typeId & asTYPEID_MASK_OBJECT) && !(m_typeId & asTYPEID_OBJHANDLE) )
	{
		// Return the address to the object directly
		return m_valueObj;
	}
	
	// Return the address of the primitive or the pointer to the object
	return reinterpret_cast<const void*>(&m_valueObj);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, asINT64 &value) const
{
	return Get(engine, &value, asTYPEID_INT64);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, double &value) const
{
	return Get(engine, &value, asTYPEID_DOUBLE);
}

int CScriptDictValue::GetTypeId() const
{
	return m_typeId;
}

static void CScriptDictValue_Construct(void *mem)
{
	new(mem) CScriptDictValue();
}

static void CScriptDictValue_Destruct(CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->FreeValue(engine);
	}
	obj->~CScriptDictValue();
}

static CScriptDictValue &CScriptDictValue_opAssign(void *ref, int typeId, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Set(engine, ref, typeId);
	}
	return *obj;
}

static CScriptDictValue &CScriptDictValue_opAssign(const CScriptDictValue &other, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx ) 
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Set(engine, const_cast<CScriptDictValue&>(other));
	}

	return *obj;
}

static CScriptDictValue &CScriptDictValue_opAssign(double val, CScriptDictValue *obj)
{
	return CScriptDictValue_opAssign(&val, asTYPEID_DOUBLE, obj);
}

static CScriptDictValue &CScriptDictValue_opAssign(asINT64 val, CScriptDictValue *obj)
{
	return CScriptDictValue_opAssign(&val, asTYPEID_INT64, obj);
}

static void CScriptDictValue_opCast(void *ref, int typeId, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Get(engine, ref, typeId);
	}
}

static asINT64 CScriptDictValue_opConvInt(CScriptDictValue *obj)
{
	asINT64 value;
	CScriptDictValue_opCast(&value, asTYPEID_INT64, obj);
	return value;
}

static double CScriptDictValue_opConvDouble(CScriptDictValue *obj)
{
	double value;
	CScriptDictValue_opCast(&value, asTYPEID_DOUBLE, obj);
	return value;
}

//-------------------------------------------------------------------
// generic wrapper for CScriptDictValue

static void CScriptDictValue_opConvDouble_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	double value;
	self->Get(gen->GetEngine(), value);
	*(double*)gen->GetAddressOfReturnLocation() = value;
}

static void CScriptDictValue_opConvInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	asINT64 value;
	self->Get(gen->GetEngine(), value);
	*(asINT64*)gen->GetAddressOfReturnLocation() = value;
}

static void CScriptDictValue_opCast_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->Get(gen->GetEngine(), gen->GetArgAddress(0), gen->GetArgTypeId(0));
}

static void CScriptDictValue_opAssign_int64_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign((asINT64)gen->GetArgQWord(0), self);
}

static void CScriptDictValue_opAssign_double_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(gen->GetArgDouble(0), self);
}

static void CScriptDictValue_opAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(gen->GetArgAddress(0), gen->GetArgTypeId(0), self);
}

static void CScriptDictValue_opCopyAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(*reinterpret_cast<CScriptDictValue*>(gen->GetArgAddress(0)), self);
}

static void CScriptDictValue_Construct_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	CScriptDictValue_Construct(self);
}

static void CScriptDictValue_Destruct_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	CScriptDictValue_Destruct(self);
}

static void CScriptDictValue_EnumReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->EnumReferences(gen->GetEngine());
}

static void CScriptDictValue_FreeValue_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->FreeValue(gen->GetEngine());
}

//--------------------------------------------------------------------------
// Register the type

void RegisterScriptDictionary(asIScriptEngine *engine)
{
	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
		RegisterScriptDictionary_Generic(engine);
	else
		RegisterScriptDictionary_Native(engine);
}

void RegisterScriptDictionary_Native(asIScriptEngine *engine)
{
	[[maybe_unused]] int r;

	// The array<string> type must be available
	assert( engine->GetTypeInfoByDecl("array<string>") );

#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags that represents the C++ class
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptDictValue>()); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CD); assert( r >= 0 );
#endif
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptDictValue_Construct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptDictValue_Destruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictValue, EnumReferences), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictValue, FreeValue), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const dictionaryValue &in)", asFUNCTIONPR(CScriptDictValue_opAssign, (const CScriptDictValue &, CScriptDictValue *), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const ?&in)", asFUNCTIONPR(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const dictionaryValue &in)", asFUNCTIONPR(CScriptDictValue_opAssign, (const CScriptDictValue &, CScriptDictValue *), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const ?&in)", asFUNCTIONPR(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(double)", asFUNCTIONPR(CScriptDictValue_opAssign, (double, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(int64)", asFUNCTIONPR(CScriptDictValue_opAssign, (asINT64, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opCast(?&out)", asFUNCTIONPR(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opConv(?&out)", asFUNCTIONPR(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "int64 opConv()", asFUNCTIONPR(CScriptDictValue_opConvInt, (CScriptDictValue*), asINT64), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "double opConv()", asFUNCTIONPR(CScriptDictValue_opConvDouble, (CScriptDictValue*), double), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	
	r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
	// Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_LIST_FACTORY, "dictionary @f(int &in) {repeat {string, ?}}", asFUNCTION(ScriptDictionaryListFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary,AddRef), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary,Release), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asMETHODPR(CScriptDictionary, operator=, (const CScriptDictionary &), CScriptDictionary&), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const ?&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,void*,int),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const int64&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,const asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const double&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,const double&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,double&) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asMETHOD(CScriptDictionary,Exists), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool isEmpty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "uint getSize() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool delete(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asMETHOD(CScriptDictionary,GetKeys), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionaryValue &opIndex(const string &in)", asMETHODPR(CScriptDictionary, operator[], (const dictKey_t &), CScriptDictValue*), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opIndex(const string &in) const", asMETHODPR(CScriptDictionary, operator[], (const dictKey_t &) const, const CScriptDictValue*), asCALL_THISCALL); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptDictionary,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptDictionary,SetGCFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptDictionary,GetGCFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 );

#if AS_USE_STLNAMES == 1
	// Same as isEmpty
	r = engine->RegisterObjectMethod("dictionary", "bool empty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
	// Same as getSize
	r = engine->RegisterObjectMethod("dictionary", "uint size() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
	// Same as delete
	r = engine->RegisterObjectMethod("dictionary", "void erase(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 );
	// Same as deleteAll
	r = engine->RegisterObjectMethod("dictionary", "void clear()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
#endif

	// Cache some things the dictionary will need at runtime
	SDictionaryCache::Setup(engine);
}

void RegisterScriptDictionary_Generic(asIScriptEngine *engine)
{
        [[maybe_unused]] int r;

	// Register the cleanup callback for the object type cache
	engine->SetEngineUserDataCleanupCallback(SDictionaryCache::Cleanup, DICTIONARY_CACHE);

#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags that represents the C++ class
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptDictValue>()); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CD); assert( r >= 0 );
#endif
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptDictValue_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptDictValue_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptDictValue_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptDictValue_FreeValue_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const dictionaryValue &in)", asFUNCTION(CScriptDictValue_opCopyAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const dictionaryValue &in)", asFUNCTION(CScriptDictValue_opCopyAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(double)", asFUNCTION(CScriptDictValue_opAssign_double_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(int64)", asFUNCTION(CScriptDictValue_opAssign_int64_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opCast(?&out)", asFUNCTION(CScriptDictValue_opCast_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opConv(?&out)", asFUNCTION(CScriptDictValue_opCast_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "int64 opConv()", asFUNCTION(CScriptDictValue_opConvInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "double opConv()", asFUNCTION(CScriptDictValue_opConvDouble_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_LIST_FACTORY, "dictionary @f(int &in) {repeat {string, ?}}", asFUNCTION(ScriptDictionaryListFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictionaryAddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictionaryRelease_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asFUNCTION(ScriptDictionaryAssign_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const int64&in)", asFUNCTION(ScriptDictionarySetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptDictionaryGetInt_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const double&in)", asFUNCTION(ScriptDictionarySetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asFUNCTION(ScriptDictionaryGetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asFUNCTION(ScriptDictionaryExists_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool isEmpty() const", asFUNCTION(ScriptDictionaryIsEmpty_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "uint getSize() const", asFUNCTION(ScriptDictionaryGetSize_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool delete(const string &in)", asFUNCTION(ScriptDictionaryDelete_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asFUNCTION(ScriptDictionaryDeleteAll_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asFUNCTION(CScriptDictionaryGetKeys_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionaryValue &opIndex(const string &in)", asFUNCTION(CScriptDictionary_opIndex_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opIndex(const string &in) const", asFUNCTION(CScriptDictionary_opIndex_const_Generic), asCALL_GENERIC); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptDictionaryGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptDictionarySetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptDictionaryGetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 );

	// Cache some things the dictionary will need at runtime
	SDictionaryCache::Setup(engine);
}

//------------------------------------------------------------------
// Iterator implementation

CScriptDictionary::CIterator CScriptDictionary::begin() const
{
	return CIterator(*this, dict.begin());
}

CScriptDictionary::CIterator CScriptDictionary::end() const
{
	return CIterator(*this, dict.end());
}

CScriptDictionary::CIterator CScriptDictionary::find(const dictKey_t &key) const
{
	return CIterator(*this, dict.find(key));
}

CScriptDictionary::CIterator::CIterator(
		const CScriptDictionary &dict,
		dictMap_t::const_iterator it)
	: m_it(it), m_dict(dict)
{}

void CScriptDictionary::CIterator::operator++() 
{ 
	++m_it; 
}

void CScriptDictionary::CIterator::operator++(int) 
{ 
	++m_it;

	// Normally the post increment would return a copy of the object with the original state,
	// but it is rarely used so we skip this extra copy to avoid unnecessary overhead
}

CScriptDictionary::CIterator &CScriptDictionary::CIterator::operator*()
{
	return *this;
}

bool CScriptDictionary::CIterator::operator==(const CIterator &other) const 
{ 
	return m_it == other.m_it;
}

bool CScriptDictionary::CIterator::operator!=(const CIterator &other) const 
{ 
	return m_it != other.m_it; 
}

const dictKey_t &CScriptDictionary::CIterator::GetKey() const 
{ 
	return m_it->first; 
}

int CScriptDictionary::CIterator::GetTypeId() const
{ 
	return m_it->second.m_typeId; 
}

bool CScriptDictionary::CIterator::GetValue(asINT64 &value) const
{ 
	return m_it->second.Get(m_dict.engine, &value, asTYPEID_INT64); 
}

bool CScriptDictionary::CIterator::GetValue(double &value) const
{ 
	return m_it->second.Get(m_dict.engine, &value, asTYPEID_DOUBLE); 
}

bool CScriptDictionary::CIterator::GetValue(void *value, int typeId) const
{ 
	return m_it->second.Get(m_dict.engine, value, typeId); 
}

const void *CScriptDictionary::CIterator::GetAddressOfValue() const
{
	return m_it->second.GetAddressOfValue();
}

END_AS_NAMESPACE