#include <stdafx.h>
#include <stdlib.h>

#include "ogMath.h"
#include "ogMemory.h"
#include "ogRW.h"
#include "ogTextures.h"
#include "ogModels.h"
#include "ogGameObjects.h"
#include "gtaInterface.h"

// game objects
extern void initializeReferences () {
	g_pReferences->initialize ();
}

//-----------------------------------------------------------------------------

ColLink * freeColLinks = (ColLink *)0x965944;	

CModelBase ** modelPtrs = (CModelBase **)0xA9B0C8;
PredefinedObjects * objectIndices = (PredefinedObjects *)0x8CD4F4;
CObjectInfo * objectInfo = (CObjectInfo *)0xBB4A90;



void (*releaseModel)(int index) = (void (__cdecl *)(int))0x4089A0;
int (*getIfpIndexByName)(char * name) = (int (__cdecl *)(char *))0x4D3990;
void (*ifpAddRef)(int index) = (void (__cdecl *)(int))0x4D3FB0;
void (*ifpRelease)(int index) = (void (__cdecl *)(int))0x4D3FD0;

BYTE * _A9B0B0 = (BYTE *)0xA9B0B0;

short getDefaultTxd (int index) {	// >> 7316D0 (1)	TODO: обязательно к реализации, после того, как будет самостоятельно проинициализирован txdPool
	return texMan.getDefaultTxd (index);
}

void RwTextureSetFindCallBack (textureCBFind cb) {
	texMan.setFindCB (cb);
}
textureCBFind RwTextureGetFindCallBack () {
	return texMan.getFindCB ();
}
void RwTextureSetMipmapping (bool enable) {
	texMan.setMipmapping (enable);
}
bool RwTextureGetMipmapping () {
	return texMan.getMipmapping ();
}
void RwTextureSetAutoMipmapping (bool enable) {
	texMan.setAutoMipmapping (enable);
}
bool RwTextureGetAutoMipmapping () {
	return texMan.getAutoMipmapping ();
}
CTexDictionary * RwTexDictionaryCreate () {
	return new CTexDictionary ();
}
void RwTexDictionaryDestroy (CTexDictionary * txd) {
	delete txd;
}
CTexture * RwTextureCreate (RwRaster * raster) {
	return new CTexture (raster);
}
bool RwTextureDestroy (CTexture * tex) {
	tex->destroy ();
	return true;
}
void RwTexDictionarySetCurrent (CTexDictionary * txd) {
	texMan.setCurrentTxd (txd);
}
const CTexDictionary * RwTexDictionaryGetCurrent () {
	return texMan.getCurrentTxd ();
}
CTexture * RwReadTexture (char * name, char * maskName) {
	return texMan.readTexture (name, maskName);
}
RwRaster *(*TextureRasterDefaultBuildMipmaps)(RwRaster * raster, RwImage * image) = (RwRaster *(__cdecl *)(RwRaster *, RwImage *))0x7F5140;
RwRaster * RwTextureRasterGenerateMipmaps (RwRaster * raster, RwImage * image) {
	return TextureRasterDefaultBuildMipmaps (raster, image);
}
CTexture * RwTexDictionaryFindNamedTexture(CTexDictionary *dict, char *name) {	// >> 7F39F0 (16)
	return dict->findNamedTexture (name);
}

int txdAdd (char * name) {
	return (*txdPool)->addEntry (name);
}
int txdIndexByName (char * name) {
	return (*txdPool)->indexByName (name);
}

// TODO: заглушка-времянка
// void (*RwTextureSetFindCallBack)(DWORD) = (void (__cdecl *)(DWORD))0x7F34D0;
// void (*RwTextureSetReadCallBack)(DWORD) = (void (__cdecl *)(DWORD))0x7F3500;


extern short defaultTxds[4];
void initTxdPool () { // >> 731F20
	debug (">> initializing TXD pool\n");
	(*txdPool) = new CTxdPool ();
	for (int i = 0; i < 4; i++)
		defaultTxds[i] = (*txdPool)->indexOf (new CTxdEntry ("*"));
	// RwTextureSetFindCallBack (0x731720);
	// -- не требуется -- RwTextureSetReadCallBack (0x731710);
	texMan.setFindCB ((textureCBFind)0x731720);
}

void releaseTxdPool () { // >> 732000
	debug (">> releaseing TXD pool\n");
	delete (*txdPool);
	*txdPool = NULL;
}

CTexture * (*RwReadTextureNative)(CRwStream * stream) = (CTexture *(__cdecl *)(CRwStream *))0x730E60;
RwFrameList * (*_rwFrameListStreamRead)(CRwStream * stream, RwFrameList * fl) = (RwFrameList * (__cdecl *)(CRwStream *, RwFrameList *))0x807970;
//-----------------------------------------------------------------------------
// world
CAtomicCallBackRender atomicDefaultRenderCallBack = (CAtomicCallBackRender)0x7491C0;
RwObjectHasFrameSyncFunction	worldAtomicSync = (RwObjectHasFrameSyncFunction)0x7507E0;
void (__cdecl * _rtAtomicResyncInterpolatedSphere)(CAtomic * atomic) = (void (__cdecl *)(CAtomic *))0x7491F0;

RwSphere * (*RpAtomicGetWorldBoundingSphere)(CAtomic *) = (RwSphere * (__cdecl *)(CAtomic *))0x749330;

bool (*RwResourcesFreeResEntry)(RwResEntry * entry) = (bool (__cdecl *)(RwResEntry *))0x807DE0;

CAtomic * (*ClumpAtomicStreamRead)(CRwStream * stream, RwFrameList * frames, RpGeometry ** geometryList) = 
(CAtomic * (__cdecl *)(CRwStream *, RwFrameList *, RpGeometry **))0x74B060;

// matrices
RwMatrix * (*RwMatrixInvert)(RwMatrix * out, RwMatrix * in) = (RwMatrix *(__cdecl *)(RwMatrix *, RwMatrix *))0x7F2070;
RwMatrix * (*RwMatrixTransform)(RwMatrix * matrix, RwMatrix * transform, int combineOp) = (RwMatrix *(__cdecl *)(RwMatrix *, RwMatrix *, int))0x7F25A0;
D3DVECTOR * (*RwTransformPoints)(D3DVECTOR * pointsOut, D3DVECTOR * pointIn, int numPoints, RwMatrix * transformMatrix) =
(D3DVECTOR * (__cdecl *)(D3DVECTOR *, D3DVECTOR *, int, RwMatrix *))0x7EDD90;
RwMatrix * (*RwMatrixRotate)(RwMatrix *, D3DVECTOR *,  float, int) = (RwMatrix * (__cdecl *)(RwMatrix *, D3DVECTOR *, float, int))0x7F1FD0;
int (*RwMatrixDestroy)(RwMatrix *m) = (int (__cdecl *)(RwMatrix *))0x7F2A20;	// TODO: delete m (with custom delete)

// light
CLight * (*RpLightStreamRead)(CRwStream *) =(CLight *(__cdecl *)(CRwStream *))0x751F00;

// camera 
CRwCamera * (*RwCameraStreamRead)(CRwStream *) = (CRwCamera *(__cdecl *)(CRwStream *))0x808DE0;
void (*RwCameraDestroy)(CRwCamera *) = (void (__cdecl *)(CRwCamera *))0x7EE4B0;
// geometry
RpGeometry * (*RpGeometryStreamRead) (CRwStream *) = (RpGeometry * (__cdecl *)(CRwStream *))0x74D190;
void (*RpGeometryDestroy)(RpGeometry *) = (void (__cdecl *)(RpGeometry *))0x74CCC0;

// materials
void (*_rpMaterialListDeinitialize)(RpMaterialList *) = (void (__cdecl *)(RpMaterialList *))0x74E150;

// frame
void (*RwFrameDestroyHierarchy)(CFrame *) = (void (__cdecl *)(CFrame *))0x7F08A0;
CFrame * (*rwFrameCloneAndLinkClones)(CFrame * f) = (CFrame * (__cdecl *)(CFrame *))0x7EFFB0;
void (*rwFramePurgeClone)(CFrame *root) = (void (__cdecl *)(CFrame *))0x7F01A0;

CFrame * RwFrameCreate () {	// >> 7F0410 (28)
	return new CFrame ();
}

bool RwFrameDestroy (CFrame * frame) {	// >> 7F05A0 (30)
	delete frame;
	return true;
}

CFrame * RwFrameAddChild (CFrame * parent, CFrame * child) {	// 7F0B00 (9) 
	parent->addChild (child);
	return parent;
}

CFrame * (*RwFrameTransform)(CFrame *, RwMatrix *, int combineOp) = (CFrame * (__cdecl *)(CFrame *, RwMatrix *, int))0x7F0F70;

// skin
RwMatrixWeights * (*RpSkinGetVertexBoneWeights)(RpSkin *) = (RwMatrixWeights * (__cdecl *)(RpSkin *))0x7C77F0;

// engine
CEngine ** engine = (CEngine **)0xC97B24;
DWORD * wolrdObjOffset = (DWORD *)0xC9259C;

// textures and rasters
RwRaster * (*RwRasterCreate)(int width, int height, int depth, DWORD flags) = (RwRaster * (__cdecl *)(int, int, int, DWORD))0x7FB230;
int (*RwRasterDestroy)(RwRaster * raster) = (int (__cdecl *)(RwRaster *))0x7FB020;


// atomics
CAtomic * RpAtomicCreate () {	// >> 749C50 (6)
	return new CAtomic ();
};

void RpAtomicDestroy (CAtomic * atomic) {	// >> 749DC0 (26)
	delete atomic;
}

CAtomic * RpAtomicClone (CAtomic *src)  { // >> 749E60 (18)
	return new CAtomic (src);
}

// clumps
CClump * RpClumpCreate () { // >> 74A290 (11)
	return new CClump ();
}

void RpClumpDestroy (CClump * clump) {
	delete clump;
}

void (*gtaHAnimDestruct)(CClump *) = (void (__cdecl *)(CClump *))0x4D6110;
RtAnimAnimation  * (*RtAnimAnimationCreate)(int typeID, int numFrames, int flags, float duration) = (RtAnimAnimation *(__cdecl *)(int, int, int, float))0x7CCE40;

bool (*RtAnimInterpolatorSetCurrentAnim)(RtAnimInterpolator *animI, RtAnimAnimation *anim) = (bool (__cdecl *)(RtAnimInterpolator *, RtAnimAnimation *))0x7CD5A0;

// lights
RwObjectHasFrameSyncFunction	worldLightSync = (RwObjectHasFrameSyncFunction)0x750AC0;
void (*RpLightDestroy)(CLight *) = (void (__cdecl *)(CLight *))0x7520D0;

// world
void (*RpWorldAddClump) (RpWorld *, CClump * c) = (void (__cdecl *)(RpWorld *, CClump *))0x751300;

// engine 
DWORD * worldObjOffset = (DWORD *)0xC9259C;
DWORD * textureModuleOffset = (DWORD *)0xC97B4C;

// memory allocators

void * newWrapper (int size) {
	return malloc (size);
}
void deleteWrapper (void * ptr) {
	free (ptr);
}

void * FreeListCreate (int entrySize,int entriesPerBlock,int alignment, int numBlocksToPreallocate,void *inPlaceSpaceForFreeListStruct,int hint) {	// 8019B0
	// return new CBlockAllocator (entrySize, entriesPerBlock, numBlocksToPreallocate, alignment);
	// TODO: временная заплата для создания __atomicAllocator и прочих аллокаторов
	CBlockAllocator * ba = new CBlockAllocator (entrySize, entriesPerBlock, numBlocksToPreallocate, alignment);
	if (entrySize == sizeof (CAtomic))
		__atomicAllocator = ba;
	else if (entrySize == sizeof (CClump) && entriesPerBlock == 128)
		__clumpAllocator = ba;
	else if (entrySize == sizeof (RpTie) && entriesPerBlock == 256)
		__tieAllocator = ba;
	else if (entrySize == sizeof (CRwStream) && !__streamAllocator)
		__streamAllocator = ba;
	else if (entrySize == sizeof (CTexDictionary) && entriesPerBlock == 5)
		__txdAllocator = ba;
	else if (entrySize == sizeof (CTexture))
		__textureAllocator = ba;
	else if (entrySize == sizeof (CFrame))
		__frameAllocator = ba;
	else if (entrySize == sizeof (RpHAnimHierarchy) && entriesPerBlock == 128)
		__hanimHierAllocator = ba;
	return ba;
}
int RwFreeListDestroy (void * freelist) { // >> 801B80
	delete ((CBlockAllocator *)freelist);
	return 1;
}
BYTE * _rwFreeListAllocReal (void * freelist, int hint) {	// 801C30
	return ((CBlockAllocator *)freelist)->allocateEntry ();
}
void * _rwFreeListFreeReal (void * freelist, BYTE * ptr) { // >> 801D50
	((CBlockAllocator *)freelist)->freeEntry (ptr);
	return freelist;
}
int RwFreeListPurge (void * freelist) { // 801E00
	return ((CBlockAllocator *)freelist)->purge ();
}

void * RwFreeListForAllUsed (void * freelist, cbaCallback cb, void * param) { // >> 801E90
	((CBlockAllocator *)freelist)->forAllUsed (cb, param);
	return freelist;
}

int RwPurgeAllFreeLists () { // >> 801F90
	return CBlockAllocator::purgeAll ();
}

static void * hmalloc (size_t size, DWORD hint) {
	return malloc (size);
}
static void * hrealloc (void * ptr, size_t newsize, DWORD hint) {
	return realloc (ptr, newsize);
}
static void * hcalloc (size_t count, size_t size, DWORD hint) {
	return calloc (count, size);
}

bool _rwMemoryOpen (rwMemoryFunctions * mem) {	// >> 801FD0
	if (mem)
		(*engine)->mem = *mem;
	else {
		(*engine)->mem.rwmalloc = hmalloc;
		(*engine)->mem.rwfree = free;
		(*engine)->mem.rwrealloc = hrealloc;
		(*engine)->mem.rwcalloc = hcalloc;
	}
	debug ("_rwMemoryOpen\n");
	return true;
}

void _rwMemoryClose () { // >> 8020F0
	while (__allocatorsList)
		delete __allocatorsList;
	debug ("_rwMemoryClose\n");
}

// memory
void * RwMalloc (int size, int flags) {
	return malloc (size);
}

void RwFree (void * ptr) {
	return free (ptr);
}

void * RwRealloc (void * ptr, int size) {
	return realloc (ptr, size);
}

void * RwCalloc (int size, int count) {
	return calloc (size, count);
}

// streams
struct RwMemory {	// параметр для memory stream
	BYTE *	start;
	DWORD	length;
};

CRwStream * RwStreamOpen (RwStreamType type, RwStreamAccessType accessType, void * data) {
	if (type == rwSTREAMFILENAME)
		return new CRwFilenameStream ((char *) data);
	if (type == rwSTREAMMEMORY) {
		RwMemory * m = (RwMemory *)data;
		return new CRwMemoryStream (m->start, m->length);
	}
	debug ("ERROR: RwStreamOpen: Invalid stream type\n");
	return NULL;
}

bool RwStreamClose (CRwStream * stream, void * data) {
	if (data && stream->type == rwSTREAMMEMORY) {	// сохранить параметры буфера
		RwMemory * m = (RwMemory *)data;
		m->start = stream->memory.block;
		m->length = stream->memory.size;
	}
	delete stream;
	return true;
}

int RwStreamRead(CRwStream * stream, void *buffer, int length) {
	return stream->read (buffer, length);
}

CRwStream * RwStreamWrite (CRwStream * stream, void * buffer, int length) {
	return NULL;
}

CRwStream * RwStreamSkip (CRwStream * stream, int offset) {
	return stream->skip (offset) ? stream : NULL;	
}

CRwStream * _rwStreamInitialize (CRwStream * stream, bool rwOwned, RwStreamType type, RwStreamAccessType accessType, void * data) {
	RwMemory * m = (RwMemory *)data;	
	CRwMemoryStream * s = new (stream) CRwMemoryStream (m->start, m->length);
	s->owned = rwOwned;
	// stream->reinit (rwOwned, m->start, m->length);
	return stream;
}

bool _rwStreamReadChunkHeader (CRwStream * stream, DWORD * type, int * length, DWORD * version, DWORD * build) {
	return stream->readChunkHeader (type, length, version, build);
}

CRwStream * _rwStreamReadData (CRwStream * stream, void * data, int bytes) {
	int l = stream->read (data, bytes);
	return l == bytes ? stream : NULL;
}

CRwStream * RwStreamReadChunkHeaderInfo (CRwStream * stream, ChunkHeaderInfo * info) {
	return stream->readChunkHeaderInfo (info) ? stream : NULL;
}

bool RwStreamFindChunk (CRwStream * stream, DWORD type, int * outLength, DWORD * outVersion) {
	return stream->findChunk (type, outLength, outVersion);
}

extern int _rwStringStreamGetSize (char * str); // >> 80A470 (6)
extern char * _rwStringStreamFindAndRead(char *string, CRwStream * stream); // >> 80A510 (3);
