text: #include <new>
  #include <stdlib.h>
  #include <string.h>
  #include <assert.h>
  #include <stdio.h> // sprintf
  
  #include "scriptgrid.h"
  
  using namespace std;
  
  BEGIN_AS_NAMESPACE
  
  // Set the default memory routines
  // Use the angelscript engine's memory routines by default
  static asALLOCFUNC_t userAlloc = asAllocMem;
  static asFREEFUNC_t  userFree  = asFreeMem;
  
  // Allows the application to set which memory routines should be used by the array object
  void CScriptGrid::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc)
  {
  	userAlloc = allocFunc;
  	userFree = freeFunc;
  }
  
  static void RegisterScriptGrid_Native(asIScriptEngine *engine);
  
  struct SGridBuffer
  {
  	asDWORD width;
  	asDWORD height;
  	asBYTE  data[1];
  };
  
  CScriptGrid *CScriptGrid::Create(asITypeInfo *ti)
  {
  	return CScriptGrid::Create(ti, 0, 0);
  }
  
  CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h)
  {
  	// Allocate the memory
  	void *mem = userAlloc(sizeof(CScriptGrid));
  	if( mem == 0 )
  	{
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Out of memory");
  
  		return 0;
  	}
  
  	// Initialize the object
  	CScriptGrid *a = new(mem) CScriptGrid(w, h, ti);
  
  	return a;
  }
  
  CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, void *initList)
  {
  	// Allocate the memory
  	void *mem = userAlloc(sizeof(CScriptGrid));
  	if( mem == 0 )
  	{
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Out of memory");
  
  		return 0;
  	}
  
  	// Initialize the object
  	CScriptGrid *a = new(mem) CScriptGrid(ti, initList);
  
  	return a;
  }
  
  CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h, void *defVal)
  {
  	// Allocate the memory
  	void *mem = userAlloc(sizeof(CScriptGrid));
  	if( mem == 0 )
  	{
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Out of memory");
  
  		return 0;
  	}
  
  	// Initialize the object
  	CScriptGrid *a = new(mem) CScriptGrid(w, h, defVal, ti);
  
  	return a;
  }
  
  // This optional callback is called when the template type is first used by the compiler.
  // It allows the application to validate if the template can be instantiated for the requested
  // subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
  // allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
  // i.e. no asOBJ_GC flag.
  static bool ScriptGridTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect)
  {
  	// Make sure the subtype can be instantiated with a default factory/constructor,
  	// otherwise we won't be able to instantiate the elements.
  	int typeId = ti->GetSubTypeId();
  	if( typeId == asTYPEID_VOID )
  		return false;
  	if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
  	{
  		asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
  		asDWORD flags = subtype->GetFlags();
  		if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
  		{
  			// Verify that there is a default constructor
  			bool found = false;
  			for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
  			{
  				asEBehaviours beh;
  				asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
  				if( beh != asBEHAVE_CONSTRUCT ) continue;
  
  				if( func->GetParamCount() == 0 )
  				{
  					// Found the default constructor
  					found = true;
  					break;
  				}
  			}
  
  			if( !found )
  			{
  				// There is no default constructor
  				ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor");
  				return false;
  			}
  		}
  		else if( (flags & asOBJ_REF) )
  		{
  			bool found = false;
  
  			// If value assignment for ref type has been disabled then the array
  			// can be created if the type has a default factory function
  			if( !ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) )
  			{
  				// Verify that there is a default factory
  				for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
  				{
  					asIScriptFunction *func = subtype->GetFactoryByIndex(n);
  					if( func->GetParamCount() == 0 )
  					{
  						// Found the default factory
  						found = true;
  						break;
  					}
  				}
  			}
  
  			if( !found )
  			{
  				// No default factory
  				ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory");
  				return false;
  			}
  		}
  
  		// If the object type is not garbage collected then the array also doesn't need to be
  		if( !(flags & asOBJ_GC) )
  			dontGarbageCollect = true;
  	}
  	else if( !(typeId & asTYPEID_OBJHANDLE) )
  	{
  		// Arrays with primitives cannot form circular references,
  		// thus there is no need to garbage collect them
  		dontGarbageCollect = true;
  	}
  	else
  	{
  		assert( typeId & asTYPEID_OBJHANDLE );
  
  		// It is not necessary to set the array as garbage collected for all handle types.
  		// If it is possible to determine that the handle cannot refer to an object type
  		// that can potentially form a circular reference with the array then it is not 
  		// necessary to make the array garbage collected.
  		asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
  		asDWORD flags = subtype->GetFlags();
  		if( !(flags & asOBJ_GC) )
  		{
  			if( (flags & asOBJ_SCRIPT_OBJECT) )
  			{
  				// Even if a script class is by itself not garbage collected, it is possible
  				// that classes that derive from it may be, so it is not possible to know 
  				// that no circular reference can occur.
  				if( (flags & asOBJ_NOINHERIT) )
  				{
  					// A script class declared as final cannot be inherited from, thus
  					// we can be certain that the object cannot be garbage collected.
  					dontGarbageCollect = true;
  				}
  			}
  			else
  			{
  				// For application registered classes we assume the application knows
  				// what it is doing and don't mark the array as garbage collected unless
  				// the type is also garbage collected.
  				dontGarbageCollect = true;
  			}
  		}
  	}
  
  	// The type is ok
  	return true;
  }
  
  // Registers the template array type
  void RegisterScriptGrid(asIScriptEngine *engine)
  {
  	// TODO: Implement the generic calling convention
  	RegisterScriptGrid_Native(engine);
  }
  
  static void RegisterScriptGrid_Native(asIScriptEngine *engine)
  {
  	[[maybe_unused]] int r;
  
  	// Register the grid type as a template
  	r = engine->RegisterObjectType("Grid<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
  
  	// Register a callback for validating the subtype before it is used
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptGridTemplateCallback), asCALL_CDECL); assert( r >= 0 );
  
  	// Templates receive the object type as the first parameter. To the script writer this is hidden
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_FACTORY, "Grid<T>@ f(int&in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_FACTORY, "Grid<T>@ f(int&in, uint, uint)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_FACTORY, "Grid<T>@ f(int&in, uint, uint, const T &in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT, void *), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
  
  	// Register the factory that will be used for initialization lists
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_LIST_FACTORY, "Grid<T>@ f(int&in type, int&in list) {repeat {repeat_same T}}", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, void*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
  
  	// The memory management methods
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptGrid,AddRef), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptGrid,Release), asCALL_THISCALL); assert( r >= 0 );
  
  	// The index operator returns the template subtype
  	r = engine->RegisterObjectMethod("Grid<T>", "T &opIndex(uint, uint)", asMETHODPR(CScriptGrid, At, (asUINT, asUINT), void*), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectMethod("Grid<T>", "const T &opIndex(uint, uint) const", asMETHODPR(CScriptGrid, At, (asUINT, asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 );
  
  	// Other methods
  	r = engine->RegisterObjectMethod("Grid<T>", "void resize(uint width, uint height)", asMETHODPR(CScriptGrid, Resize, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectMethod("Grid<T>", "uint width() const", asMETHODPR(CScriptGrid, GetWidth, () const, asUINT), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectMethod("Grid<T>", "uint height() const", asMETHODPR(CScriptGrid, GetHeight, () const, asUINT), asCALL_THISCALL); assert( r >= 0 );
  
  	r = engine->RegisterObjectMethod("Grid<T>", "void fill(const T& in)", asMETHODPR(CScriptGrid, Fill, (void*), void), asCALL_THISCALL); assert(r >= 0);
  
  
  	// Register GC behaviours in case the array needs to be garbage collected
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHODPR(CScriptGrid, GetRefCount, (), int), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHODPR(CScriptGrid, SetFlag, (), void), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHODPR(CScriptGrid, GetFlag, (), bool), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHODPR(CScriptGrid, EnumReferences, (asIScriptEngine*), void), asCALL_THISCALL); assert( r >= 0 );
  	r = engine->RegisterObjectBehaviour("Grid<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHODPR(CScriptGrid, ReleaseAllHandles, (asIScriptEngine*), void), asCALL_THISCALL); assert( r >= 0 );
  }
  
  CScriptGrid::CScriptGrid(asITypeInfo *ti, void *buf)
  {
  	refCount = 1;
  	gcFlag = false;
  	objType = ti;
  	objType->AddRef();
  	buffer = 0;
  	subTypeId = objType->GetSubTypeId();
  
  	asIScriptEngine *engine = ti->GetEngine();
  
  	// Determine element size
  	if( subTypeId & asTYPEID_MASK_OBJECT )
  		elementSize = sizeof(asPWORD);
  	else
  		elementSize = engine->GetSizeOfPrimitiveType(subTypeId);
  
  	// Determine the initial size from the buffer
  	asUINT height = *(asUINT*)buf;
  	asUINT width = height ? *(asUINT*)((char*)(buf)+4) : 0;
  
  	// Make sure the grid size isn't too large for us to handle
  	if( !CheckMaxSize(width, height) )
  	{
  		// Don't continue with the initialization
  		return;
  	}
  
  	// Skip the height value at the start of the buffer
  	buf = (asUINT*)(buf)+1;
  
  	// Copy the values of the grid elements from the buffer
  	if( (ti->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 )
  	{
  		CreateBuffer(&buffer, width, height);
  
  		// Copy the values of the primitive type into the internal buffer
  		for( asUINT y = 0; y < height; y++ )
  		{
  			// Skip the length value at the start of each row
  			buf = (asUINT*)(buf)+1;
  
  			// Copy the line
  			if( width > 0 )
  				memcpy(At(0,y), buf, width*elementSize);
  
  			// Move to next line
  			buf = (char*)(buf) + width*elementSize;
  
  			// Align to 4 byte boundary
  			if( asPWORD(buf) & 0x3 )
  				buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
  		}
  	}
  	else if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
  	{
  		CreateBuffer(&buffer, width, height);
  
  		// Copy the handles into the internal buffer
  		for( asUINT y = 0; y < height; y++ )
  		{
  			// Skip the length value at the start of each row
  			buf = (asUINT*)(buf)+1;
  
  			// Copy the line
  			if( width > 0 )
  				memcpy(At(0,y), buf, width*elementSize);
  
  			// With object handles it is safe to clear the memory in the received buffer
  			// instead of increasing the ref count. It will save time both by avoiding the
  			// call the increase ref, and also relieve the engine from having to release
  			// its references too
  			memset(buf, 0, width*elementSize);
  
  			// Move to next line
  			buf = (char*)(buf) + width*elementSize;
  
  			// Align to 4 byte boundary
  			if( asPWORD(buf) & 0x3 )
  				buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
  		}
  	}
  	else if( ti->GetSubType()->GetFlags() & asOBJ_REF )
  	{
  		// Only allocate the buffer, but not the objects
  		subTypeId |= asTYPEID_OBJHANDLE;
  		CreateBuffer(&buffer, width, height);
  		subTypeId &= ~asTYPEID_OBJHANDLE;
  
  		// Copy the handles into the internal buffer
  		for( asUINT y = 0; y < height; y++ )
  		{
  			// Skip the length value at the start of each row
  			buf = (asUINT*)(buf)+1;
  
  			// Copy the line
  			if( width > 0 )
  				memcpy(At(0,y), buf, width*elementSize);
  
  			// With object handles it is safe to clear the memory in the received buffer
  			// instead of increasing the ref count. It will save time both by avoiding the
  			// call the increase ref, and also relieve the engine from having to release
  			// its references too
  			memset(buf, 0, width*elementSize);
  
  			// Move to next line
  			buf = (char*)(buf) + width*elementSize;
  
  			// Align to 4 byte boundary
  			if( asPWORD(buf) & 0x3 )
  				buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
  		}
  	}
  	else
  	{
  		// TODO: Optimize by calling the copy constructor of the object instead of
  		//       constructing with the default constructor and then assigning the value
  		// TODO: With C++11 ideally we should be calling the move constructor, instead
  		//       of the copy constructor as the engine will just discard the objects in the
  		//       buffer afterwards.
  		CreateBuffer(&buffer, width, height);
  
  		// For value types we need to call the opAssign for each individual object
  		asITypeInfo *subType = ti->GetSubType();
  		asUINT subTypeSize = subType->GetSize();
  		for( asUINT y = 0;y < height; y++ )
  		{
  			// Skip the length value at the start of each row
  			buf = (asUINT*)(buf)+1;
  
  			// Call opAssign for each of the objects on the row
  			for( asUINT x = 0; x < width; x++ )
  			{
  				void *obj = At(x,y);
  				asBYTE *srcObj = (asBYTE*)(buf) + x*subTypeSize;
  				engine->AssignScriptObject(obj, srcObj, subType);
  			}
  
  			// Move to next line
  			buf = (char*)(buf) + width*subTypeSize;
  
  			// Align to 4 byte boundary
  			if( asPWORD(buf) & 0x3 )
  				buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
  		}
  	}
  
  	// Notify the GC of the successful creation
  	if( objType->GetFlags() & asOBJ_GC )
  		objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  }
  
  CScriptGrid::CScriptGrid(asUINT width, asUINT height, asITypeInfo *ti)
  {
  	refCount = 1;
  	gcFlag = false;
  	objType = ti;
  	objType->AddRef();
  	buffer = 0;
  	subTypeId = objType->GetSubTypeId();
  
  	// Determine element size
  	if( subTypeId & asTYPEID_MASK_OBJECT )
  		elementSize = sizeof(asPWORD);
  	else
  		elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  
  	// Make sure the array size isn't too large for us to handle
  	if( !CheckMaxSize(width, height) )
  	{
  		// Don't continue with the initialization
  		return;
  	}
  
  	CreateBuffer(&buffer, width, height);
  
  	// Notify the GC of the successful creation
  	if( objType->GetFlags() & asOBJ_GC )
  		objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  }
  
  void CScriptGrid::Resize(asUINT width, asUINT height)
  {
  	// Make sure the size isn't too large for us to handle
  	if( !CheckMaxSize(width, height) )
  		return;
  
  	// Create a new buffer
  	SGridBuffer *tmpBuffer = 0;
  	CreateBuffer(&tmpBuffer, width, height);
  	if( tmpBuffer == 0 )
  		return;
  
  	if( buffer )
  	{
  		// Copy the existing values to the new buffer
  		asUINT w = width > buffer->width ? buffer->width : width;
  		asUINT h = height > buffer->height ? buffer->height : height;
  		for( asUINT y = 0; y < h; y++ )
  			for( asUINT x = 0; x < w; x++ )
  				SetValue(tmpBuffer, x, y, At(buffer, x, y));
  
  		// Replace the internal buffer
  		DeleteBuffer(buffer);
  	}
  
  	buffer = tmpBuffer;
  }
  
  CScriptGrid::CScriptGrid(asUINT width, asUINT height, void *defVal, asITypeInfo *ti)
  {
  	refCount = 1;
  	gcFlag = false;
  	objType = ti;
  	objType->AddRef();
  	buffer = 0;
  	subTypeId = objType->GetSubTypeId();
  
  	// Determine element size
  	if( subTypeId & asTYPEID_MASK_OBJECT )
  		elementSize = sizeof(asPWORD);
  	else
  		elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  
  	// Make sure the array size isn't too large for us to handle
  	if( !CheckMaxSize(width, height) )
  	{
  		// Don't continue with the initialization
  		return;
  	}
  
  	CreateBuffer(&buffer, width, height);
  
  	// Notify the GC of the successful creation
  	if( objType->GetFlags() & asOBJ_GC )
  		objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  
  	// Initialize the elements with the default value
  	for( asUINT y = 0; y < GetHeight(); y++ )
  		for( asUINT x = 0; x < GetWidth(); x++ )
  			SetValue(x, y, defVal);
  }
  
  void CScriptGrid::SetValue(asUINT x, asUINT y, void *value)
  {
  	SetValue(buffer, x, y, value);
  }
  
  void CScriptGrid::Fill(void* value)
  {
  	for (asUINT y = 0; y < GetHeight(); ++y)
  	{
  		for (asUINT x = 0; x < GetWidth(); ++x)
  		{
  			SetValue(buffer, x, y, value);
  		}
  	}
  }
  
  void CScriptGrid::SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value)
  {
  	// At() will take care of the out-of-bounds checking, though
  	// if called from the application then nothing will be done
  	void *ptr = At(buf, x, y);
  	if( ptr == 0 ) return;
  
  	if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  		objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
  	else if( subTypeId & asTYPEID_OBJHANDLE )
  	{
  		void *tmp = *(void**)ptr;
  		*(void**)ptr = *(void**)value;
  		objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
  		if( tmp )
  			objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
  	}
  	else if( subTypeId == asTYPEID_BOOL ||
  			 subTypeId == asTYPEID_INT8 ||
  			 subTypeId == asTYPEID_UINT8 )
  		*(char*)ptr = *(char*)value;
  	else if( subTypeId == asTYPEID_INT16 ||
  			 subTypeId == asTYPEID_UINT16 )
  		*(short*)ptr = *(short*)value;
  	else if( subTypeId == asTYPEID_INT32 ||
  			 subTypeId == asTYPEID_UINT32 ||
  			 subTypeId == asTYPEID_FLOAT ||
  			 subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
  		*(int*)ptr = *(int*)value;
  	else if( subTypeId == asTYPEID_INT64 ||
  			 subTypeId == asTYPEID_UINT64 ||
  			 subTypeId == asTYPEID_DOUBLE )
  		*(double*)ptr = *(double*)value;
  }
  
  CScriptGrid::~CScriptGrid()
  {
  	if( buffer )
  	{
  		DeleteBuffer(buffer);
  		buffer = 0;
  	}
  	if( objType ) objType->Release();
  }
  
  asUINT CScriptGrid::GetWidth() const
  {
  	if( buffer )
  		return buffer->width;
  
  	return 0;
  }
  
  asUINT CScriptGrid::GetHeight() const
  {
  	if( buffer )
  		return buffer->height;
  
  	return 0;
  }
  
  // internal
  bool CScriptGrid::CheckMaxSize(asUINT width, asUINT height)
  {
  	// This code makes sure the size of the buffer that is allocated
  	// for the array doesn't overflow and becomes smaller than requested
  
  	asUINT maxSize = 0xFFFFFFFFul - sizeof(SGridBuffer) + 1;
  	if( elementSize > 0 )
  		maxSize /= elementSize;
  
  	asINT64 numElements  = width * height;
  
  	if( (numElements >> 32) || numElements > maxSize )
  	{
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Too large grid size");
  
  		return false;
  	}
  
  	// OK
  	return true;
  }
  
  asITypeInfo *CScriptGrid::GetGridObjectType() const
  {
  	return objType;
  }
  
  int CScriptGrid::GetGridTypeId() const
  {
  	return objType->GetTypeId();
  }
  
  int CScriptGrid::GetElementTypeId() const
  {
  	return subTypeId;
  }
  
  void *CScriptGrid::At(asUINT x, asUINT y)
  {
  	return At(buffer, x, y);
  }
  
  // Return a pointer to the array element. Returns 0 if the index is out of bounds
  void *CScriptGrid::At(SGridBuffer *buf, asUINT x, asUINT y)
  {
  	if( buf == 0 || x >= buf->width || y >= buf->height )
  	{
  		// If this is called from a script we raise a script exception
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Index out of bounds");
  		return 0;
  	}
  
  	asUINT index = x+y*buf->width;
  	if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  		return *(void**)(buf->data + elementSize*index);
  	else
  		return buf->data + elementSize*index;
  }
  const void *CScriptGrid::At(asUINT x, asUINT y) const
  {
  	return const_cast<CScriptGrid*>(this)->At(const_cast<SGridBuffer*>(buffer), x, y);
  }
  
  
  // internal
  void CScriptGrid::CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h)
  {
  	asUINT numElements = w * h;
  
  	*buf = reinterpret_cast<SGridBuffer*>(userAlloc(sizeof(SGridBuffer)-1+elementSize*numElements));
  
  	if( *buf )
  	{
  		(*buf)->width  = w;
  		(*buf)->height = h;
  		Construct(*buf);
  	}
  	else
  	{
  		// Oops, out of memory
  		asIScriptContext *ctx = asGetActiveContext();
  		if( ctx )
  			ctx->SetException("Out of memory");
  	}
  }
  
  // internal
  void CScriptGrid::DeleteBuffer(SGridBuffer *buf)
  {
  	assert( buf );
  
  	Destruct(buf);
  
  	// Free the buffer
  	userFree(buf);
  }
  
  // internal
  void CScriptGrid::Construct(SGridBuffer *buf)
  {
  	assert( buf );
  
  	if( subTypeId & asTYPEID_OBJHANDLE )
  	{
  		// Set all object handles to null
  		void *d = (void*)(buf->data);
  		memset(d, 0, (buf->width*buf->height)*sizeof(void*));
  	}
  	else if( subTypeId & asTYPEID_MASK_OBJECT )
  	{
  		void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
  		void **d = (void**)(buf->data);
  
  		asIScriptEngine *engine = objType->GetEngine();
  		asITypeInfo *subType = objType->GetSubType();
  
  		for( ; d < max; d++ )
  		{
  			*d = (void*)engine->CreateScriptObject(subType);
  			if( *d == 0 )
  			{
  				// Set the remaining entries to null so the destructor 
  				// won't attempt to destroy invalid objects later
  				memset(d, 0, sizeof(void*)*(max-d));
  
  				// There is no need to set an exception on the context,
  				// as CreateScriptObject has already done that
  				return;
  			}
  		}
  	}
  }
  
  // internal
  void CScriptGrid::Destruct(SGridBuffer *buf)
  {
  	assert( buf );
  
  	if( subTypeId & asTYPEID_MASK_OBJECT )
  	{
  		asIScriptEngine *engine = objType->GetEngine();
  
  		void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
  		void **d   = (void**)(buf->data);
  
  		for( ; d < max; d++ )
  		{
  			if( *d )
  				engine->ReleaseScriptObject(*d, objType->GetSubType());
  		}
  	}
  }
  
  // GC behaviour
  void CScriptGrid::EnumReferences(asIScriptEngine *engine)
  {
  	if( buffer == 0 ) return;
  
  	// If the grid is holding handles, then we need to notify the GC of them
  	if (subTypeId & asTYPEID_MASK_OBJECT)
  	{
  		asUINT numElements = buffer->width * buffer->height;
  		void **d = (void**)buffer->data;
  		asITypeInfo *subType = engine->GetTypeInfoById(subTypeId);
  		if ((subType->GetFlags() & asOBJ_REF))
  		{
  			// For reference types we need to notify the GC of each instance
  			for (asUINT n = 0; n < numElements; n++)
  			{
  				if (d[n])
  					engine->GCEnumCallback(d[n]);
  			}
  		}
  		else 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
  			for (asUINT n = 0; n < numElements; n++)
  			{
  				if (d[n])
  					engine->ForwardGCEnumReferences(d[n], subType);
  			}
  		}
  	}
  }
  
  // GC behaviour
  void CScriptGrid::ReleaseAllHandles(asIScriptEngine*)
  {
  	if( buffer == 0 ) return;
  
  	DeleteBuffer(buffer);
  	buffer = 0;
  }
  
  void CScriptGrid::AddRef() const
  {
  	// Clear the GC flag then increase the counter
  	gcFlag = false;
  	asAtomicInc(refCount);
  }
  
  void CScriptGrid::Release() const
  {
  	// Clearing the GC flag then descrease the counter
  	gcFlag = false;
  	if( asAtomicDec(refCount) == 0 )
  	{
  		// When reaching 0 no more references to this instance
  		// exists and the object should be destroyed
  		this->~CScriptGrid();
  		userFree(const_cast<CScriptGrid*>(this));
  	}
  }
  
  // GC behaviour
  int CScriptGrid::GetRefCount()
  {
  	return refCount;
  }
  
  // GC behaviour
  void CScriptGrid::SetFlag()
  {
  	gcFlag = true;
  }
  
  // GC behaviour
  bool CScriptGrid::GetFlag()
  {
  	return gcFlag;
  }
  
  END_AS_NAMESPACE
  