// Open SanAndreas
// (c) 2006-2008 Stanislav "listener" Golovin
// 
// Camera object
#include "stdafx.h"

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


CCamera * camera = (CCamera *)0xB6F028;
CRwCamera * gtaCamera = (CRwCamera *)0xC1703C;

void (*_415540)() = (void (__cdecl *)())0x415540;	// какая-то операция с массивом 0x9655A0
void (__thiscall * camera__updateTargetEntity)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x50C360;
void (__thiscall * camera__527FA0)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x527FA0;
void (__thiscall * camera__5164A0)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x5164A0;
void (__thiscall * camera__516440)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x516440;
void (__thiscall * camera__516500)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x516500;
void (__thiscall * camera__processFade)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x50B5D0;
void (__thiscall * camera__50B6D0)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x50B6D0;
void (__thiscall * camera__50B890)(CCamera * _this) = (void (__thiscall *)(CCamera *))0x50B890;
void (__thiscall * camera__516560)(CCamera * _this, float) = (void (__thiscall *)(CCamera *, float))0x516560;	

void (__thiscall * cam__process)(CCam * _this) = (void (__thiscall *)(CCam *))0x526FC0;

float (*atanOfXY)(float x, float y) = (float (__cdecl *)(float, float ))0x53CC70;	// TODO: а можно ли _это_ просто заменить на atan2f?

void CCamera::setPositionAndRotation (D3DVECTOR * position, D3DVECTOR * rotation) {
	cameraPosition = *position;
	cameraRotation = *rotation;
	_f4E = 0;
}

void CCamera::attach (CEntity * target, short mode1, short mode2, int _aC) {
	debug ("CCamera::attach (%08x, %d, %d, %d)\n", target, mode1, mode2, _aC);
	if (_fCF0 == 0 && _aC == 2 && _fB0 == 1)
		return;
	_fB0 = _aC;
	if (!target) {
		target = getPlayerVehicle (-1, 0);
		if (!target)
			target = getPlayerPed (-1);
	}
	if (mode1 == 0) {	// TODO: check targetEntity is not null 
		BYTE t = targetEntity->type & 7;
		if (t == 3)
			mode1 = 4;
		else if (t == 2)
			mode1 = 0x12;
	}
	_f2C = 0;
//	if (targetEntity)
//		targetEntity->unregisterReference (&targetEntity);
	targetEntity = target;
//	targetEntity->addToLList (&targetEntity);

	_fC38 = mode1;
	_fC3C = mode2;
	_f2B = 0;
	_f32 = 1;
}

void CCamera::updateTargetEntity () {	// >> 50C360
	BYTE _v1 = 0;
	_f27 = 0;

	if (targetEntity && (targetEntity->type&7) == 2) {
		CPhysical * e = (CPhysical *)((CEntity *)targetEntity);
		if (vectorLengthSq (&(e->_f44)) > 0.3f)	// TODO: (test ah, 41h; jnz)
			_f27 = 1;	
	}
	BYTE f = 0;
	if (_fB0 == 2) {
		f = 1;
		if (_fC34 == 7 || _fC34 == 8) {
			if (getPlayerPed (-1)->_f534 != 0x3F)
				f = 0;
	/*
			if (getPlayerVehicle (-1, 0)) 
				targetEntity->removeFromLList (&targetEntity);
			targetEntity = getPlayerPed (-1);
			targetEntity->addToLList (&targetEntity);
	*/
		}		
	}	// >> 50C41C
	if (((_f2B == 1) && _f58) || !f || _f58) 	// TODO: упростить
		if (targetEntity && (_f34 != 1))
			return;
	
	if (getPlayerVehicle (-1, 0)) {

	} else {	// >> 50C4B9
	}
	//....................	
}

void CCamera::process () {	// >> 52B730
	_415540 ();	// TODO: inline ? 

	int _v58 = 0;
	_f2A = 0;

	_f848 = *getPos();
	if (_f2B == 1 || _f34 == 1 || _fB0 == 2) 
		camera__updateTargetEntity (this);	// TODO: make method

	if (targetEntity != NULL)
		targetEntity = getPlayerPed (-1);

	if (cams[activeCam]._f21C != NULL)
		cams[activeCam]._f21C = targetEntity;
	
	if (cams[activeCam+1]._f21C != NULL)
		cams[activeCam+1]._f21C = targetEntity;

	camera__527FA0 (this);
	camera__5164A0 (this);
	camera__516440 (this);	// CCamera::processTransverse ();
	camera__516500 (this);	// CCamera::processZoom ();
	if (isFading)
		camera__processFade (this);
	if (_f52)
		camera__50B6D0 (this);
	if (_f3D)
		camera__50B890 (this);
	
	// >> 52B88D
	gtaCamera->setNearClipPlane (0.3f);	

	float at = atanOfXY (cams[activeCam]._f190.x, cams[activeCam]._f190.y);	// проверки на нулевые значения делаются внутри	
	cam__process(cams+activeCam);	// TODO: cams[activeCam].process ();
	float at2 = atanOfXY (cams[activeCam]._f190.x, cams[activeCam]._f190.y);	

	if (_f58) {
		if (*currentTime > _f7C+_f78) {
			_f58 = 0;
			_f4F = 0;
			_f3A = 0;
		}
	}

	if (_f31) 
		gtaCamera->setNearClipPlane (nearClipPlane);

	at = at2-at;
	while  (at >= M_PI) 
		at -= (float)M_PI*2.0f;

	while (at < -M_PI)
		at += (float)M_PI*2.0f;

	if (abs (at) > 0.3) 
		_f2A = 1;

	// >> 52BA06
	BYTE flag = (cams[activeCam]._f028 != 3) && (((targetEntity->type & 7)) == 2) ? 1 : 0;

	if ((float)*currentTime < endJiggleTime) 
		camera__516560 (this, ((float)*currentTime-startJiggleTime)/(endJiggleTime-startJiggleTime));	// CCamera::processJiggle
	
	if (_f58 && flag == 0) {
		DWORD tm = *currentTime-_f78;
		if (tm > _f7C)
			tm = _f7C;
		float num = ((float)tm/(float)_f7C)/(float)_f80;
		if (num < 0.0f)
			num = 0.0f;
		else if (num > 1.0f)
			num = 1.0f;
			
		if (num <= _fC18) {	// TODO: условие!
			num = _fC18 == 0.0f ? 0.0f : (_fC18-num)/_fC18;
			num = 0.5f-cosf(num * (float)M_PI)*0.5f;
			
		} else {	// >> 52BBC6
		}
		// >> 52BCD9

	} else {	// >> 52C3D9
	}
	// >> 52C53D
}
//=============================================================================
void CCamera::setRwCamera(CRwCamera *rwc) {
	rwCamera = rwc;
	cameraViewMatrix.attach (&(rwc->viewMatrix), 0);
}