// ### OpenGTA3 ###
// Object Models

#pragma pack(push, 1)

// forward declarations
struct RpAtomic;
struct RwMatrix;


// Структура, описывающая параметры переключения объектов с активацией по времени
struct CModelTimeinfo {
	BYTE	timeOn;			// time to activate (only hours)
	BYTE	timeOff;		// time to deactivate (only hours)
	short	pairedModel;	// night model for daytime object or day model for nighttime objest

	CModelTimeinfo * initPairedModel (char * modelName);
};

class CColModel {
public:
	CColModel ();
	~CColModel ();

	void initModelData (int numSpheres, int numBoxes, int num1, int numMeshVertices, int numFaces, BYTE flags);
	void initBBox ();
	void releaseData ();

	void * operator new (size_t);
	void operator delete (void *);

	D3DVECTOR	inf;	
	D3DVECTOR	sup;	// sup (NOTE: возможно, стоит собрать их в bbox)
	D3DVECTOR	center;	
	float		radius;	// TODO: собрать в RpSphere
	BYTE		_f28;
	BYTE		flags;
	BYTE		_f2A;
	BYTE		_f2B;
	void *		_f2C;
};

struct CIfpFile {	// TODO: move to IFP-related stuff
	char	name[16];
	BYTE	loaded;
	BYTE	_f11;
	WORD	usageCount;
	DWORD	startAnimation;	// first index in ifpAnimations
	DWORD	animationCount;
	DWORD	_f1C;
};

struct CIfpAnimations {
	DWORD	hash;
	void * objects;	// массив объектов по 12 байт
	WORD	_f8;
	BYTE	_fA;
	BYTE	_fB;
	DWORD	_fC;
	DWORD	_f10;
	DWORD	_f14;
};

struct ModelComponent {
	char *	name;
	DWORD	_f4;
	DWORD	_f8;
};

class CModelBase {
public:
	DWORD		modelNameHash;	// 04
	WORD		usageCount;		// 08
	short		txdIndex;		// 0A
	BYTE		_f0C;			// 0C
	BYTE		_f0D;			// 0D
	short		_f0E;			// 0E
	short		_f10;			// 10
	WORD		flags;			// 12
	CColModel *	colModel;		// 14 TODO: CColModel *
	float		drawDistance;	// 18
	union {
		RpAtomic *	atomic;			// 1C
		RpClump * clump; 
	};

	CModelBase () { usageCount = 0; txdIndex = -1; }
	virtual ~CModelBase () {}

	virtual CModelBase * _m04 () { return NULL; }
	virtual CModelBase * _m08 () { return NULL; }
	virtual int _m0C () { return 0; }
	virtual const BYTE getType () const = 0;
	virtual CModelTimeinfo * getTimeinfo () { return NULL; }
	virtual void init ();
	virtual void clear();
	virtual void clearData () = 0;
	virtual int  _m24 () = 0;
	virtual RpAtomic * cloneDataAndSetMM (RwMatrix *) = 0;	
	virtual RpAtomic * cloneData () = 0; 
	virtual void initIfpName (char * name) {}	// _m30
	virtual void initIfp () {}					// _m34
	virtual int getIfpIndex () { return -1; }	// _m38

	void setTxd (char * txdName);
	void unsetTdx () { txdIndex = -1; }
	void addRef ();
	void release ();

	// назначить модели collision data
	// если выставлен initPairedModel, то для timed модели, collision data назначается и для второй модели в паре
	void setColModel (CColModel * colModel, bool initPairedModel);	
	void unsetColModel ();

};

class CModelObjs : public CModelBase {
public:
	CModelObjs () : CModelBase () {}

	virtual CModelBase * _m04 () { return this; }
	virtual const BYTE getType () const { return 1; }
	virtual void clearData ();
	virtual int _m24 () { return 1; }
	virtual RpAtomic * cloneDataAndSetMM (RwMatrix *);
	virtual RpAtomic * cloneData ();
	virtual void _m3C (RpAtomic * _atomic);	
};

class CModelObjs2c : public CModelObjs {
public:
	CModelObjs2c () : CModelObjs () {}

	virtual CModelBase * _m08 () { return this; }
	virtual void init ();
	virtual void clearData ();
	virtual RpAtomic * cloneDataAndSetMM (RwMatrix *);
	virtual RpAtomic * cloneData ();

	RpAtomic * atomic2;	
};

class CModelTobj : public CModelObjs {
public:
	CModelTobj () : CModelObjs () { timeinfo.pairedModel = -1; }

	virtual const BYTE getType () const { return 3; }
	virtual CModelTimeinfo * getTimeinfo () { return &timeinfo; }

	CModelTimeinfo	timeinfo;
};

// TODO: originally CClumpModelInfo
class CModelHier : public CModelBase {
public:

	CModelHier () : CModelBase () {}

	virtual const BYTE getType () const { return 5; }
	virtual void init () { CModelBase::init (); ifpIndex = -1; }
	virtual void clearData ();
	virtual int	_m24 () { return 2; }
	virtual RpAtomic * cloneDataAndSetMM (RwMatrix *);
	virtual RpAtomic * cloneData ();
	virtual void initIfpName (char * name);
	virtual void initIfp ();
	virtual int getIfpIndex () { return ifpIndex; }
	virtual CColModel * getColModel () { return colModel; }		
	virtual void setClump (RpClump * c);

	void initComponents (ModelComponent * components);

	union {	// _f20
		char *	ifpName;
		int		ifpIndex;
	};	
};

class CModelWeap : public CModelHier {
public:
	CModelWeap () : CModelHier () {}

	virtual const BYTE getType () const { return 4; }
	virtual void init () { CModelHier::init (); _f24 = 0; }
	virtual void setClump (RpClump * c);

	DWORD	_f24;
};

class CModelPeds : public CModelHier {
public:
	CModelPeds () : CModelHier () { _f34 = NULL; }
	virtual ~CModelPeds ();

	virtual const BYTE getType () const { return 7; }
	virtual void clearData ();
	virtual void setClump (RpClump * c);

	void initPedColModel (RpClump * c);

	DWORD	animType;
	DWORD	pedType;
	DWORD	statType;
	WORD	carMask;
	WORD	flags;
	CColModel * _f34;
	BYTE	radio1;
	BYTE	radio2;
	BYTE	pedModel;
	BYTE	__align;
	short	voiceType;
	short	voice1;
	short	voice2;
	short	voice3;
};

// TODO: move somewhere else
struct CQuaternion {

	void	get (RwMatrix * m);

	RtQuat	q;
};


struct VehicleStruct_2 {
	D3DVECTOR	_f0;
	CQuaternion	q;
	int			_f1C;
};

struct CVehicleStruct {
	CVehicleStruct ();
	~CVehicleStruct ();

	void * operator new (size_t);
	void operator delete (void * ptr);

	D3DVECTOR		_f00[15];
	VehicleStruct_2	_fB4[18];
	RpAtomic *		atomics[6];
	BYTE			atomicCount;
	BYTE			_f30D;
	WORD			_f30E;
	DWORD			_f310;	// component present or component renderable mask
};

// TODO: original name CVehicleModelInfo
class CModelCars : public CModelHier {	
public:
	CModelCars ();

	virtual const BYTE	getType () const { return 6; }	
	virtual void	init ();
	virtual void	clearData ();
	virtual RpAtomic *	cloneData ();
	virtual void initIfpName (char * name);
	virtual void initIfp ();
	virtual int getIfpIndex () { return carIfpIndex; }
	virtual void	setClump (RpClump * clump);

	int	getCarVariation1 ();
	int getCarVariation2 ();

	void setVehicleRenderCallbacks ();
	void _4C8900 ();
	void preprocessHierarchy ();	
	void _4C8BD0 ();	// some debug + clear visibility flags for all atomics in the model
	void setVehicleComponentFlags  (RwFrame * f, DWORD flags);	// set component visibility flags

	DWORD		_f24;
	BYTE		_f28;
	BYTE		_f29;
	BYTE		_f2A;
	BYTE		_f2B;
	DWORD		_f2C;
	BYTE		_f30;
	BYTE		_f31;
	char		gamename[8];
	WORD		_f3A;
	int			type;
	float		_p2;
	float		_p3;
	short		_p1;
	WORD		handlingIndex;
	BYTE		_f4C;
	BYTE		vclass;	// vehicle class
	BYTE		vflags;	// vehicle flags
	BYTE		_p4;
	WORD		_f50;
	WORD		frq;
	DWORD		comprules;
	float		_f58;
	CVehicleStruct *	vehicleStruct;
	BYTE		__fill[0x270];	// 060
	BYTE		_f2D0;
	BYTE		_f2D1;
	BYTE		_f2D2;
	BYTE		_f2D3;
	BYTE		_f2D4;
	BYTE		_f2D5;
	int			_f2D6[9];
	int			_f2FA;
	int			_f2FE;
	WORD		_f302;
	union {	// _f304
		char *	carIfpName;
		int		carIfpIndex;
	};	

};

#pragma pack(pop)
extern CModelBase ** modelPtrs;

// Model arrays
#define MODEL_OBJS_COUNT	14000
#define MODEL_OBJS2C_COUNT	70
#define MODEL_TOBJ_COUNT	169
#define MODEL_WEAP_COUNT	51
#define MODEL_HIER_COUNT	92
#define MODEL_PEDS_COUNT	278
#define MODEL_CARS_COUNT	212

struct ModelObjsData {
	ModelObjsData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelObjs	objects[MODEL_OBJS_COUNT];
};

struct ModelObjs2cData {
	ModelObjs2cData () : count (0) {};
	CModelBase * allocate (int objectID);

	int				count;
	CModelObjs2c	objects[MODEL_OBJS2C_COUNT];
};

struct ModelTobjData {
	ModelTobjData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelTobj	objects[MODEL_TOBJ_COUNT];
};

struct ModelHierData {
	ModelHierData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelHier	objects[MODEL_HIER_COUNT];
};

struct ModelWeapData {
	ModelWeapData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelWeap	objects[MODEL_WEAP_COUNT];
};

struct ModelPedsData {
	ModelPedsData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelPeds	objects[MODEL_PEDS_COUNT];
};
struct ModelCarsData {
	ModelCarsData () : count (0) {}
	CModelBase * allocate (int objectID);

	int			count;
	CModelCars	objects[MODEL_CARS_COUNT];
};



CModelBase * getModelIndexByName (char * name, DWORD * pIndex);	// 4C5940 >> (31)