#ifndef __MODELINFO
#define __MODELINFO

#include "GeneralSA.h"

// TODO: Move to a separate file?
typedef struct
{
    CVector     vecMin;
    CVector     vecMax;
    CVector     vecOffset;
    FLOAT       fRadius;
} CBoundingBox;


typedef struct
{
    CVector     vecCenter;
    float       fRadius;
} CColSphere;


typedef struct
{
    CVector     min;
    CVector     max;
} CColBox;


/*typedef struct
{
    unsigned short  v1;
    unsigned short  v2;
    unsigned short  v3;
    EColSurface     material;
    CColLighting    lighting;
} CColTriangle;


typedef struct
{
    BYTE pad0 [ 12 ];
} CColTrianglePlane;*/


typedef struct
{
    char version[4];
    DWORD size;
    char name[0x18];
} ColModelFileHeader;

class CColData
{
public:
	unsigned short   m_wNumSpheres;
	unsigned short   m_wNumBoxes;
	unsigned short   m_wNumTriangles;
	unsigned char    m_bNumLines;
	unsigned char    m_bFlags;
	CColSphere        *m_pSpheres;
	CColBox           *m_pBoxes;
	/* possibly was the union with some unknown yet collision model which was used for CMtruck only.
	union{
	CColLine          *m_pLines;
	CMtruckColLine    *m_pMtruckLines;
	};
	*/
	void          *m_pLines;
	void  *m_pVertices;
	void      *m_pTriangles;
	void *m_pTrianglePlanes;
	unsigned int   m_dwNumShadowTriangles;
	unsigned int   m_dwNumShadowVertices;
	void  *m_pShadowVertices;
	void      *m_pShadowTriangles;
};

static_assert( sizeof(CColData) == 0x30, "Wrong size: CColData" );

class CColModel
{
public:
    CBoundingBox	                boundingBox;
    BYTE                            level;
    BYTE                            unknownFlags;
    BYTE                            pad [ 2 ];
    CColData*						pColData;

public:
	~CColModel()
	{
		// Dirty!
		((void(__thiscall*)(CColModel*))0x40F700)(this);
	}

	void operator delete(void* ptr);
};

class C2dEffect
{
public:
	CVector							vecPos;
	BYTE							bType;
	DWORD							nCount;
};

class CTimeInfo
{
public:
	unsigned char					bTimeOn, bTimeOff;
	signed short					nPairedModel;

public:
	CTimeInfo(signed short nModel)
		: nPairedModel(nModel)
	{}
};

struct UpgradePosnDesc
{
	CVector				m_vPosition;
	CVector				m_qRotation;
	float				imag;
	int					m_dwParentComponentId;
};

class CAtomicModelInfo;
class CDamageAtomicModelInfo;
class CLodAtomicModelInfo;

class NOVMT CBaseModelInfo
{
public:
    unsigned int			ulHashKey;                  // +4   Generated by CKeyGen::GetUppercaseKey(char const *) called by CBaseModelInfo::SetModelName(char const *)
    unsigned short          usNumberOfRefs: 16;         // +8
    short					usTextureDictionary: 16;    // +10
    unsigned char           ucAlpha: 8;                 // +12

    unsigned char           ucNumOf2DEffects: 8;        // +13
    unsigned short          usUnknown: 16;              // +14     Something with 2d effects

    unsigned char           ucDynamicIndex: 8;          // +16

    unsigned char           dwUnknownFlag9: 1;          // +17
    unsigned char           dwUnknownFlag10: 1;
    unsigned char           dwUnknownFlag11: 1;
    unsigned char           dwUnknownFlag12: 1;
    unsigned char           dwUnknownFlag13: 1;
    unsigned char           dwUnknownFlag14: 1;
    unsigned char           dwUnknownFlag15: 1;
    unsigned char           dwUnknownFlag16: 1;

    // Flags used by CBaseModelInfo
	unsigned char           bHasBeenPreRendered: 1;     // +18
	unsigned char           bAlphaTransparency: 1;
	unsigned char           bIsLod: 1;
	unsigned char           bDontCastShadowsOn: 1;
	unsigned char           bDontWriteZBuffer: 1;
	unsigned char           bDrawAdditive: 1;
	unsigned char           bDrawLast: 1;
	unsigned char           bDoWeOwnTheColModel: 1;

    unsigned char           dwUnknownFlag25: 1;         // +19
    unsigned char           dwUnknownFlag26: 1;
    unsigned char           dwUnknownFlag27: 1;
    unsigned char           bSwaysInWind: 1;
    unsigned char           bCollisionWasStreamedWithModel: 1;  // CClumpModelInfo::SetCollisionWasStreamedWithModel(unsigned int)
    unsigned char           bDontCollideWithFlyer: 1;           // CAtomicModelInfo::SetDontCollideWithFlyer(unsigned int)
    unsigned char           bHasComplexHierarchy: 1;            // CClumpModelInfo::SetHasComplexHierarchy(unsigned int)
    unsigned char           bWetRoadReflection: 1;              // CAtomicModelInfo::SetWetRoadReflection(unsigned int)
    CColModel*				pColModel;                  // +20      CColModel: public CBoundingBox
    float                   fLodDistanceUnscaled;       // +24      Scaled is this value multiplied with flt_B6F118
    RwObject*               pRwObject;                  // +28

public:
	virtual							~CBaseModelInfo() {}
	virtual CAtomicModelInfo*		AsAtomicModelInfoPtr() { return nullptr; }
    virtual CDamageAtomicModelInfo*	AsDamageAtomicModelInfoPtr() { return nullptr; }
    virtual CLodAtomicModelInfo*	AsLodAtomicModelInfoPtr() { return nullptr; }
    virtual unsigned char			GetModelType()=0;
	virtual CTimeInfo*				GetTimeInfo() { return nullptr; }
    virtual void					Init();
    virtual void					Shutdown();
    virtual void					DeleteRwObject()=0;
    virtual int						GetRwModelType()=0;
	virtual RpAtomic*				CreateInstance_(RwMatrix* pMatrix)=0;
    virtual RpAtomic*				CreateInstance()=0;
	virtual void					SetAnimFile(const char* pName) { UNREFERENCED_PARAMETER(pName); }
	virtual void					ConvertAnimFileIndex() {}
	virtual int						GetAnimFileIndex() { return -1; }

	CBaseModelInfo()
		: usNumberOfRefs(0), usTextureDictionary(-1)
	{}

	inline CColModel*		GetColModel() { return pColModel; }
	inline unsigned int		GetHash() { return ulHashKey; }
	inline short			GetTextureDict() { return usTextureDictionary; }

	void					RecalcDrawDistance(float fOldDist);
	void					SetTexDictionary(const char* pDict);
	void					AddRef();
};

class NOVMT CClumpModelInfo : public CBaseModelInfo
{
public:
	int						nAnimIndex;

public:
	virtual unsigned char			GetModelType() override { return 5; }
	virtual void					Init() override;
	virtual void					DeleteRwObject() override;
	virtual int						GetRwModelType() override { return rpCLUMP; }
	virtual RpAtomic*				CreateInstance_(RwMatrix* pMatrix) override;
	virtual RpAtomic*				CreateInstance() override;
	virtual void					SetAnimFile(const char* pName) override;
	virtual void					ConvertAnimFileIndex() override;
	virtual int						GetAnimFileIndex() override { return nAnimIndex; }
	virtual CColModel*				GetBoundingBox() { return pColModel; }
	virtual void					SetClump(RpClump* pClump);
};

struct PlateMaterialsData // Added in SilentPatch
{
	RpMaterial** m_plates = nullptr;
	RpMaterial** m_platebacks = nullptr;

	size_t m_numPlates = 0;
	size_t m_numPlatebacks = 0;

	~PlateMaterialsData()
	{
		delete[] m_plates;
		delete[] m_platebacks;
	}
};

class NOVMT CVehicleModelInfo : public CClumpModelInfo
{
public:
	static const size_t		PLATE_TEXT_LEN = 8;

	PlateMaterialsData*		m_apPlateMaterials;		// Changed in SilentPatch
	char					m_plateText[PLATE_TEXT_LEN];
	char					field_30;
	signed char				m_nPlateType;
	char					m_nGameName[8];
	unsigned int			m_dwType;
	float					m_fWheelSizeFront;
	float					m_fWheelSizeRear;
	unsigned short			m_wWheelModelId;
	unsigned short			m_wHandlingIndex;
	unsigned char			m_nNumDoors;
	unsigned char			m_nClass;
	unsigned char			m_nFlags;
	unsigned char			m_nWheelUpgradeClass;
	unsigned short			m_wTimesUsed;
	unsigned short			m_wFrq;
	union{
		unsigned int		m_dwCompRules;
		struct{
			unsigned int m_nExtraA_comp1 : 4;
			unsigned int m_nExtraA_comp2 : 4;
			unsigned int m_nExtraA_comp3 : 4;
			unsigned int m_nExtraA_rule : 4;
			unsigned int m_nExtraB_comp1 : 4;
			unsigned int m_nExtraB_comp2 : 4;
			unsigned int m_nExtraB_comp3 : 4;
			unsigned int m_nExtraB_rule : 4;
		};
	};
	float m_fBikeSteerAngle;

	class CVehicleStructure{
	public:
		CVector				m_avDummyPosn[15];
		UpgradePosnDesc		m_aUpgrades[18];
		RpAtomic*			m_apExtras[6];
		uint8_t				m_nNumExtras;
		unsigned int		m_dwMaskComponentsDamagable;
	}						*m_pVehicleStruct;

	char					field_60[464];

	static const size_t IN_PLACE_BUFFER_DIRT_SIZE = 30;
	union{
		struct{;
			RpMaterial**	m_dirtMaterials;
			size_t			m_numDirtMaterials;
			RpMaterial*		m_staticDirtMaterials[IN_PLACE_BUFFER_DIRT_SIZE];
		};
		RpMaterial*			__oldDirtMaterials[32]; // Unused with SilentPatch
	};
	unsigned char			m_anPrimaryColors[8];
	unsigned char			m_anSecondaryColors[8];
	unsigned char			m_anTertiaryColors[8];
	unsigned char			m_anQuaternaryColors[8];
	unsigned char			m_nNumColorVariations;
	unsigned char			m_nLastColorVariation;
	signed char				m_nPrimaryColor;
	signed char				m_nSecondaryColor;
	signed char				m_nTertiaryColor;
	signed char				m_nQuaternaryColor;
	short					m_awUpgrades[18];
	FLAUtils::int16			m_awRemapTxds[4];
	class CAnimBlock*		m_pAnimBlock;

public:
	static int8_t*			ms_compsUsed;
	static int8_t*			ms_compsToUse;

	static void				ResetCompsForNoExtras();

	inline const char*		GetCustomCarPlateText()
		{ return m_plateText[0] ? m_plateText : nullptr; }

	inline void				Shutdown_Stub()
		{ CVehicleModelInfo::Shutdown(); }

	virtual void			Shutdown() override;

	void					FindEditableMaterialList();
	void					SetCarCustomPlate();
	void					SetVehicleColour( int32_t color1, int32_t color2, int32_t color3, int32_t color4 );

	uint32_t				GetNumRemaps() const;

};

extern CBaseModelInfo** const		ms_modelInfoPtrs;

void RemapDirt( CVehicleModelInfo* modelInfo, uint32_t dirtID );

class CCustomCarPlateMgr
{
	// Pretty much rewritten for SilentPatch
public:
	static const size_t		NUM_MAX_PLATES = 12;

	static RwTexture*		(*CreatePlateTexture)(const char* pText, signed char nDesign);
	static bool				(*GeneratePlateText)(char* pBuf, int nLen);
	static signed char		(*GetMapRegionPlateDesign)();
	static void				(*SetupMaterialPlatebackTexture)(RpMaterial* pMaterial, signed char nDesign);

	static void				SetupClump(RpClump* pClump, PlateMaterialsData* pMatsArray);
	static void				SetupClumpAfterVehicleUpgrade(RpClump* pClump, PlateMaterialsData* pMatsArray, signed char nDesign);

private:
	static void				PollPlates( RpClump* clump, PlateMaterialsData* materials );
};

static_assert(sizeof(CBaseModelInfo) == 0x20, "Wrong size: CBaseModelInfo");
static_assert(sizeof(CClumpModelInfo) == 0x24, "Wrong size: CClumpModelInfo");
static_assert(sizeof(CVehicleModelInfo) == 0x308, "Wrong size: CvehicleModelInfo");

#endif