// Open SanAndreas
// 
// Game objects

#include "stdafx.h"

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

//
void (__thiscall * _705B30)(DWORD _this, void * ptr) = (void (__thiscall *)(DWORD, void *))0x705B30;
void (__thiscall * entryInfoList__flush)(void * _this) = (void (__thiscall *)(void *))0x536E10;	// TODO: CEntryInfoList::flush ();
//

XYZStore * xyzStore = (XYZStore *)0xB74288;

CReferences * g_pReferences	= (CReferences *)0xB9B9A8;

XYZ * defaultXYZ = (XYZ *)0xB74240;

CPlayer * player = (CPlayer *)0xB7CD98;

CVehicle * (*getPlayerVehicle)(int, BYTE) = (CVehicle * (__cdecl *)(int, BYTE))0x56E0D0;
//=============================================================================
// ### XYZStore ###
XYZ * XYZStore::allocate() {
	XYZ * xyz = freeListHead.pNext;
	if (xyz == &freeListTail)
		return NULL;
	
	xyz->pNext->pPrev = xyz->pPrev;
	xyz->pPrev->pNext = xyz->pNext;

	xyz->pNext = allocatedListHead.pNext;
	allocatedListHead.pNext->pPrev = xyz;
	xyz->pPrev = &allocatedListHead;
	allocatedListHead.pNext = xyz;
	return xyz;
}
//=============================================================================
// ### XYZ ###
//=============================================================================
// ### CReferences ###
// TODO: SA-version: replace with IV's ASAP
void CReferences::initialize() {
	debug ("CReferences::initialize\n");
	m_pFreeList = m_refs;
	for (int i = 0; i < 2999; i++) {
		m_refs[i].m_pEntity = NULL;
		m_refs[i].m_pNext = m_refs+i+1;
	}
	m_refs[2999].m_pEntity = NULL;
	m_refs[2999].m_pNext = NULL;
}

//=============================================================================
// CPlaceable
XYZ * CPlaceable::allocateXYZ() {
	if (xyz) {	// xyz already allocated. relink it to allocated list
		xyz->pNext->pPrev = xyz->pPrev;
		xyz->pPrev->pNext = xyz->pNext;

		xyz->pNext = xyzStore->allocatedListHead.pNext;		
		xyzStore->allocatedListHead.pNext->pPrev = xyz;
		xyz->pPrev = &(xyzStore->allocatedListHead);
		xyzStore->allocatedListTail.pNext = xyz;
		return xyz;
	}
	xyz = xyzStore->allocate ();
	if (!xyz) {
		xyzStore->_l1Tail.pPrev->ptr->releaseXYZ ();
		xyz = xyzStore->allocate ();
	}

	xyz->ptr = this;
	return xyz;
}

void CPlaceable::releaseXYZ() {
	pl.pos = xyz->m.pos;
	pl.angle = atan2f (-xyz->m.top.x, xyz->m.top.y);

	// remove XYZ from linked list
	xyz->ptr = NULL;
	xyz->pPrev->pNext = xyz->pNext;
	xyz->pNext->pPrev = xyz->pPrev;

	// add XYZ to free list
	xyz->pNext = xyzStore->freeListHead.pNext;
	xyzStore->freeListHead.pNext->pPrev = xyz;
	xyz->pPrev = &(xyzStore->freeListHead);
	xyzStore->freeListHead.pNext = xyz;

	xyz = NULL;
}

//=============================================================================
// ### CEntity ###
void CEntity::registerReference (CEntity **ppEntity) {
	if (((type & 7) == 1) && !(_f1C & 0x4400000) && !_f2E)
		return;
	for (SReference * n = m_pRef; n; n = n->m_pNext)
		if (n->m_pEntity == ppEntity)
			return;	// already in list

	// проверим, есть ли свободные узлы. Если нет, попробуем почистить имеющиеся объекты - вдруг найдется?
	if (!g_pReferences->m_pFreeList) // out of free nodes ?
		for (int i = (*pedPool)->size-1; i >= 0; i--) {
			CEntity * e = (CEntity *)(*pedPool)->at (i);
			if (e)
				e->pruneReferences ();			
			if (g_pReferences->m_pFreeList)
				break;
		}	
	if (!g_pReferences->m_pFreeList)	// still out of free nodes ?
		for (int i = (*vehiclePool)->size-1; i >= 0; i--) {
			CEntity * e = (CEntity *)(*vehiclePool)->at (i);
			if (e)
				e->pruneReferences ();			
			if (g_pReferences->m_pFreeList)
				break;
		}

	if (!g_pReferences->m_pFreeList)	// still out of free nodes ?
		for (int i = (*objectPool)->size-1; i >= 0; i--) {
			CEntity * e = (CEntity *)(*objectPool)->at (i);
			if (e)
				e->pruneReferences ();			
			if (g_pReferences->m_pFreeList)
				break;
		}

	SReference * n = g_pReferences->m_pFreeList;
	if (!n)
		return;
	g_pReferences->m_pFreeList = n->m_pNext;
	n->m_pNext = m_pRef;
	n->m_pEntity = ppEntity;
	m_pRef = n;
}

void CEntity::unregisterReference (CEntity ** ppEntity) {
	SReference * n = m_pRef;
	SReference ** prev = &m_pRef;

	while (n) {
		if (n->m_pEntity == ppEntity) {
			*prev = n->m_pNext;
			n->m_pEntity = NULL;
			n->m_pNext = g_pReferences->m_pFreeList;
			g_pReferences->m_pFreeList = n;
			return;
		}
		prev = &(n->m_pNext);
		n = n->m_pNext;
	}
}

void CEntity::pruneReferences () {
	SReference * n = m_pRef;
	SReference ** prev = &m_pRef;
	while (n) {
		if (*(n->m_pEntity) != this) {
			*prev = n->m_pNext;
			n->m_pNext = g_pReferences->m_pFreeList;
			g_pReferences->m_pFreeList = n;
			n = *prev;			
		} else {
			prev = &(n->m_pNext);
			n = n->m_pNext;
		}
	}
}

CEntity::~CEntity() {	// >> 535E90 (7)
	if (_f30) 
		_f30[0x34]--;	// TODO: _f30->_f34--;
	deleteRwObject ();
	resolveReferences ();
}

// TODO: убрать заглушки
int * buildingCount = (int *)0xB71804;	
void (*_409710)(void *) = (void (__cdecl *)(void *))0x409710;
void (__thiscall * entityList__removeObject)(DWORD _this, CEntity * entity) = (void (__thiscall *)(DWORD, CEntity *))0x5336B0;
void (__thiscall * _532B20)(CEntity * _this) = (void (__thiscall *)(CEntity *))0x532B20;
void (__thiscall * _533BF0)(CEntity * _this) = (void (__thiscall *)(CEntity *))0x533BF0;
void (__thiscall * _717900)(CEntity * _this) = (void (__thiscall *)(CEntity *))0x717900;
void (__thiscall * entity_resolveReferences)(CEntity * _this) = (void (__thiscall *)(CEntity *))0x571A40;

void (__thiscall * entity_m04) (CEntity * _this, CRect * p) = (void (__thiscall *)(CEntity *, CRect *))0x5347D0;	// TODO: !
void (__thiscall * entity_remove) (CEntity * _this)  = (void (__thiscall *)(CEntity *))0x534AE0;
void (__thiscall * entity_setModelIndexNC) (CEntity * _this, int index) = (void (__thiscall *)(CEntity *, int))0x533700;
void * (__thiscall * entity_createRwObject) (CEntity * _this) = (void * (__thiscall *)(CEntity *))0x533D30;
CRect * (__thiscall * entity_getBoundRect)(CEntity * _this, CRect * r) = (CRect * (__thiscall *)(CEntity *, CRect *))0x534120;
void (__thiscall * entity_preRender) (CEntity * _this) = (void (__thiscall *)(CEntity *))0x535FA0;
void (__thiscall * entity_render) (CEntity * _this) = (void (__thiscall *)(CEntity *))0x534310;
void (__thiscall * entity_setupLighting)(CEntity * _this) = (void (__thiscall *)(CEntity *))0x553DC0;
void (__thiscall * entity_removeLighting)(CEntity * _this, DWORD p) = (void (__thiscall *)(CEntity *, DWORD))0x553370;

void CEntity::_m04 (CRect * _a0) {	// >> 5347D0	TODO: name! return type! 
	return entity_m04 (this, _a0);
}

void CEntity::remove () {
	entity_remove (this);
}

void CEntity::setModelIndexNoCreate (int modelId) {
	entity_setModelIndexNC (this, modelId);
}

void * CEntity::createRwObject () {
	return entity_createRwObject (this);
}

void CEntity::deleteRwObject() {	// >> 534030 (3/14)
	if (!rwObject)
		return;
	if (rwObject->type == 1) {	// ATOMIC
		CAtomic * a = (CAtomic *)rwObject;
		CFrame * f = rwObject->parent;
		delete a;
		delete f;
	} else if (rwObject->type == 2) {	// CLUMP
		CClump * c = (CClump *)rwObject;
		CAtomic * a = c->getFirstAtomic ();
		if (a && a->geometry->skin) 
			c->forAllAtomics ((CAtomicCallback)0x734B90, NULL);	// TODO: destroyAtomicAnimationCB
		delete c;
	}
	rwObject = NULL;

	CModelBase * model = modelPtrs[modelIndex];
	model->release ();
	_409710 (_f28);
	_f28 = NULL;
	if ((type & 7) == 1)
		(*buildingCount)--;

	if (model->getType() == 5 && model->flags & 0x100 && (type&7) != 4)
		entityList__removeObject (0xB9ACC8, this);

	_533BF0 (this);
	_717900 (this);
}

CRect * CEntity::getBoundRect(CRect *rect) {
	return entity_getBoundRect (this, rect);
}

void CEntity::preRender() {
	entity_preRender (this);
}

void CEntity::render() {
	entity_render (this);
}

void CEntity::setupLighting() {
	entity_setupLighting (this);
}

void CEntity::removeLighting(DWORD p) {
	entity_removeLighting (this, p);
}

void CEntity::resolveReferences() {
	entity_resolveReferences (this);
}

void * CDummyObject::operator new(size_t) {
	return (*dummyPool)->allocate ();
}

void CDummyObject::operator delete (void * ptr) {
	(*dummyPool)->release ((BYTE *)ptr);
}

void * CBuilding::operator new(size_t) {
	return (*buildingPool)->allocate ();
}

void CBuilding::operator delete(void * ptr)  {
	(*buildingPool)->release ((BYTE *)ptr);
}

void * CTreadable::operator new(size_t) {
	return (*buildingPool)->allocate ();
}

void CTreadable::operator delete(void * ptr)  {
	(*buildingPool)->release ((BYTE *)ptr);
}


void CTreadable::preRender() {
	if (rwObject) {
		CEntity::preRender ();
		rwObject->parent->updateObject ();
	}
	_532B20 (this);
}

/*
//=============================================================================
//	### CPhysiscal ###
CPhysical::CPhysical() : CEntity () {
	_fB0 = 0;
	allocateXYZ ();
	xyz->setIdentityMatrix ();
	_f1C |= 1;
	_f38 = 100.0f;
	_f40 = 2;

	// TODO: memset
	_f44.x = _f44.y = _f44.z = 0.0f;
	_f50.x = _f50.y = _f50.z = 0.0f;
	_f5C.x = _f5C.y = _f5C.z = 0.0f;
	_f68.x = _f68.y = _f68.z = 0.0f;
	_f74.x = _f74.y = _f74.z = 0.0f;
	_f80.x = _f80.y = _f80.z = 0.0f;

	_f8C = _f90 = _f94 = 1.0f;
	_f98 = 0.1f;
	_fA4.x = _fA4.y = _fA4.z = 0.0f;
	_fB0 = 0;
	_fB4 = 0;
	_fB8 = _fB9 = 0;	
	memset (_fBC, 0, 24);
	_fD4 = 0;
	_fD8 = 0;
	_fDC = 0;
	_fE0.x = _fE0.y = _fE0.z = 0.0f;
	_fEC.x = _fEC.y = _fEC.z = 0.0f;
	_fF8 = 0;
	_fFC = 0;

	_f118 = 0;
	_f11C = 0;
	_f120 = 0;
	_f124 = 0;
	_f128 = 0;
	_f12C = 1.0f;
	_f130 = 0;
	_f134 = NULL;
}

CPhysical::~CPhysical() {
	if (_f134) 
		_705B30 (0xC40350, _f134);
	entryInfoList__flush (&_fB0);
}
//=============================================================================
void * CPed::operator new(size_t size) {
	return (*pedPool)->allocate ();
}

void CPed::operator delete (void * ptr) {
	return (*pedPool)->release ((BYTE *)ptr);
}

CPed::CPed (int pedType) {
}

CPed::~CPed () {
}
*/
//=============================================================================
CPed * getPlayerPed (int __unused__) {
	return player->pPed;
}
/*
CVehicle * getPlayerVehicle (int __unused__, BYTE _a4) {
	CPed * p = player->pPed;
	if (!p || !(p->_f46C & 0x100))
		return NULL;	
	if (_a4 && player->vehicle)
		return player->vehicle;
	return p->_f58C;
}
*/