#ifndef __GENERAL
#define __GENERAL

#include <stdint.h>
#include "TheFLAUtils.h"

class CSimpleTransform
{
public:
    CVector                         m_translate;
    float                           m_heading;
};

class CRGBA
{
public:
	uint8_t r, g, b, a;

	inline CRGBA() {}

	inline constexpr CRGBA(const CRGBA& in)
		: r(in.r), g(in.g), b(in.b), a(in.a)
	{}

	inline constexpr CRGBA(const CRGBA& in, uint8_t alpha)
		: r(in.r), g(in.g), b(in.b), a(alpha)
	{}


	inline constexpr CRGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
		: r(red), g(green), b(blue), a(alpha)
	{}

	friend constexpr CRGBA Blend(const CRGBA& From, const CRGBA& To, double BlendVal)
		{	double InvBlendVal = 1.0 - BlendVal;
			return CRGBA( uint8_t(To.r * BlendVal + From.r * InvBlendVal),
			uint8_t(To.g * BlendVal + From.g * InvBlendVal),
			uint8_t(To.b * BlendVal + From.b * InvBlendVal),
			uint8_t(To.a * BlendVal + From.a * InvBlendVal)); }

	friend constexpr CRGBA BlendSqr(const CRGBA& From, const CRGBA& To, double BlendVal)
		{	double InvBlendVal = 1.0 - BlendVal;
			return CRGBA( uint8_t(sqrt((To.r * To.r) * BlendVal + (From.r * From.r) * InvBlendVal)),
			uint8_t(sqrt((To.g * To.g) * BlendVal + (From.g * From.g) * InvBlendVal)),
			uint8_t(sqrt((To.b * To.b) * BlendVal + (From.b * From.b) * InvBlendVal)),
			uint8_t(sqrt((To.a * To.a) * BlendVal + (From.a * From.a) * InvBlendVal))); }

	// SilentPatch
	CRGBA*			BlendGangColour(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
	CRGBA*			BlendGangColour_Dynamic(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
};

class CRect
{
public:
	float x1, y1;
	float x2, y2;

	inline CRect() {}
	inline constexpr CRect(float a, float b, float c, float d)
		: x1(a), y1(b), x2(c), y2(d)
	{}
};


class CPlaceable
{
private:
    CSimpleTransform				m_transform;
    CMatrix*						m_pCoords;

public:
	// Line up the VMTs
	virtual ~CPlaceable() {}

	inline CPlaceable() {}

	explicit inline CPlaceable(int dummy)
	{
		// Dummy ctor
		UNREFERENCED_PARAMETER(dummy);
	}

	inline CVector*					GetCoords()
		{ return m_pCoords ? &m_pCoords->GetPos() : &m_transform.m_translate; }
	inline CMatrix*					GetMatrix()
		{ return m_pCoords; }
	inline CSimpleTransform&		GetTransform()
		{ return m_transform; }
	inline float					GetHeading()
		{ return m_pCoords ? atan2(-m_pCoords->GetUp().x, m_pCoords->GetUp().y) : m_transform.m_heading; }

	inline void						SetCoords(const CVector& pos)
	{	if ( m_pCoords ) { m_pCoords->GetPos() = pos; }
		else m_transform.m_translate = pos; }
	inline void						SetHeading(float fHeading)
		{ if ( m_pCoords ) m_pCoords->SetRotateZOnly(fHeading); else m_transform.m_heading = fHeading; }
};

enum // nStatus
{
	STATUS_PLAYER = 0,
	STATUS_PLAYER_PLAYBACKFROMBUFFER = 1,
	STATUS_SIMPLE = 2,
	STATUS_PHYSICS = 3,
	STATUS_ABANDONED = 4,
	STATUS_WRECKED = 5,

	STATUS_PLAYER_REMOTE = 8,
	STATUS_PLAYER_DISABLED = 9,
	STATUS_TRAILER = 10,
	STATUS_SIMPLE_TRAILER = 11,
	STATUS_GHOST = 12,
};

// TODO: May not be the best place to put it?
class NOVMT CEntity	: public CPlaceable
{
public:
    virtual void	Add_CRect();
    virtual void	Add();
    virtual void	Remove();
    virtual void	SetIsStatic(bool);
    virtual void	SetModelIndex(int nModelIndex);
    virtual void	SetModelIndexNoCreate(int nModelIndex);
    virtual void	CreateRwObject();
    virtual void	DeleteRwObject();
    virtual CRect	GetBoundRect();
    virtual void	ProcessControl();
    virtual void	ProcessCollision();
    virtual void	ProcessShift();
    virtual void	TestCollision();
    virtual void	Teleport();
    virtual void	SpecialEntityPreCollisionStuff();
    virtual void	SpecialEntityCalcCollisionSteps();
    virtual void	PreRender();
    virtual void	Render();
    virtual void	SetupLighting();
    virtual void	RemoveLighting(bool bRemove=true);
    virtual void	FlagToDestroyWhenNextProcessed();

//private:
	RpClump*		m_pRwObject;						// 0x18

    /********** BEGIN CFLAGS (0x1C) **************/
    unsigned long	bUsesCollision : 1;				// does entity use collision
    unsigned long	bCollisionProcessed : 1;			// has object been processed by a ProcessEntityCollision function
    unsigned long	bIsStatic : 1;					// is entity static
    unsigned long	bHasContacted : 1;				// has entity processed some contact forces
    unsigned long	bIsStuck : 1;						// is entity stuck
    unsigned long	bIsInSafePosition : 1;			// is entity in a collision free safe position
    unsigned long	bWasPostponed : 1;				// was entity control processing postponed
    unsigned long	bIsVisible : 1;					//is the entity visible

    unsigned long	bIsBIGBuilding : 1;				// Set if this entity is a big building
    unsigned long	bRenderDamaged : 1;				// use damaged LOD models for objects with applicable damage
    unsigned long	bStreamingDontDelete : 1;			// Dont let the streaming remove this
    unsigned long	bRemoveFromWorld : 1;				// remove this entity next time it should be processed
    unsigned long	bHasHitWall : 1;					// has collided with a building (changes subsequent collisions)
    unsigned long	bImBeingRendered : 1;				// don't delete me because I'm being rendered
    unsigned long	bDrawLast :1;						// draw object last
    unsigned long	bDistanceFade :1;					// Fade entity because it is far away

    unsigned long	bDontCastShadowsOn : 1;			// Dont cast shadows on this object
    unsigned long	bOffscreen : 1;					// offscreen flag. This can only be trusted when it is set to true
    unsigned long	bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them
    unsigned long	bDontStream : 1;					// tell the streaming not to stream me
    unsigned long	bUnderwater : 1;					// this object is underwater change drawing order
    unsigned long	bHasPreRenderEffects : 1;			// Object has a prerender effects attached to it
    unsigned long	bIsTempBuilding : 1;				// whether or not the building is temporary (i.e. can be created and deleted more than once)
    unsigned long	bDontUpdateHierarchy : 1;			// Don't update the aniamtion hierarchy this frame

    unsigned long	bHasRoadsignText : 1;				// entity is roadsign and has some 2deffect text stuff to be rendered
    unsigned long	bDisplayedSuperLowLOD : 1;
    unsigned long	bIsProcObject : 1;				// set object has been generate by procedural object generator
    unsigned long	bBackfaceCulled : 1;				// has backface culling on
    unsigned long	bLightObject : 1;					// light object with directional lights
    unsigned long	bUnimportantStream : 1;			// set that this object is unimportant, if streaming is having problems
    unsigned long	bTunnel : 1;						// Is this model part of a tunnel
    unsigned long	bTunnelTransition : 1;			// This model should be rendered from within and outside of the tunnel
    /********** END CFLAGS **************/

    WORD			RandomSeed;					// 0x20
    FLAUtils::int16	m_nModelIndex;				// 0x22
    void*			pReferences;				// 0x24
    void*			m_pLastRenderedLink;		// 0x28
    WORD			m_nScanCode;				// 0x2C
    BYTE			m_iplIndex;					// 0x2E
    BYTE			m_areaCode;					// 0x2F
    CEntity*		m_pLod;					// 0x30
    BYTE			numLodChildren;				// 0x34
    char			numLodChildrenRendered;		// 0x35

    //********* BEGIN CEntityInfo **********//
    uint8_t			nType : 3;							// what type is the entity	// 0x36 (2 == Vehicle)
	uint8_t			nStatus : 5;						// control status			// 0x36
    //********* END CEntityInfo ************//

public:
	uint8_t	GetStatus() const { return nStatus; }

	bool IsVisible();

	void SetPositionAndAreaCode( CVector position );
};

class NOVMT CPhysical : public CEntity
{
private:
    float			pad1; // 56
    int				__pad2; // 60

    unsigned int	b0x01 : 1; // 64
    unsigned int	bApplyGravity : 1;
    unsigned int	bDisableFriction : 1;
    unsigned int	bCollidable : 1;
    unsigned int	b0x10 : 1;
    unsigned int	bDisableMovement : 1;
    unsigned int	b0x40 : 1;
    unsigned int	b0x80 : 1;

    unsigned int	bSubmergedInWater : 1; // 65
    unsigned int	bOnSolidSurface : 1;
    unsigned int	bBroken : 1;
    unsigned int	b0x800 : 1; // ref @ 0x6F5CF0
    unsigned int	b0x1000 : 1;//
    unsigned int	b0x2000 : 1;//
    unsigned int	b0x4000 : 1;//
    unsigned int	b0x8000 : 1;//

    unsigned int	b0x10000 : 1; // 66
    unsigned int	b0x20000 : 1; // ref @ CPhysical__processCollision
    unsigned int	bBulletProof : 1;
    unsigned int	bFireProof : 1;
    unsigned int	bCollisionProof : 1;
    unsigned int	bMeeleProof : 1;
    unsigned int	bInvulnerable : 1;
    unsigned int	bExplosionProof : 1;

    unsigned int	b0x1000000 : 1; // 67
    unsigned int	bAttachedToEntity : 1;
    unsigned int	b0x4000000 : 1;
    unsigned int	bTouchingWater : 1;
    unsigned int	bEnableCollision : 1;
    unsigned int	bDestroyed : 1;
    unsigned int	b0x40000000 : 1;
    unsigned int	b0x80000000 : 1;

    CVector			m_vecLinearVelocity; // 68
    CVector			m_vecAngularVelocity; // 80
    CVector			m_vecCollisionLinearVelocity; // 92
    CVector			m_vecCollisionAngularVelocity; // 104
    CVector			m_vForce;							// 0x74
    CVector			m_vTorque;							// 0x80
	float			fMass;								// 0x8C
    float			fTurnMass;							// 0x90
    float			m_fVelocityFrequency;					// 0x94
    float			m_fAirResistance;						// 0x98
    float			m_fElasticity;						// 0x9C
    float			m_fBuoyancyConstant;					// 0xA0

    CVector			vecCenterOfMass;					// 0xA4
    DWORD			dwUnk1;								// 0xB0
    void*			unkCPtrNodeDoubleLink;				// 0xB4
    BYTE			byUnk: 8;								// 0xB8
    BYTE			byCollisionRecords: 8;					// 0xB9
    BYTE			byUnk2: 8;								// 0xBA (Baracus)
    BYTE			byUnk3: 8;								// 0xBB

    float			pad4[6];								// 0xBC

    float			fDistanceTravelled;					// 0xD4
    float			fDamageImpulseMagnitude;				// 0xD8
    CEntity*		damageEntity;						// 0xDC
    BYTE			pad2[28];								// 0xE0
    CEntity*		pAttachedEntity;					// 0xFC
    CVector			m_vAttachedPosition;				// 0x100
    CVector			m_vAttachedRotation;				// 0x10C
    BYTE			pad3[20];								// 0x118
    float			fLighting;							// 0x12C col lighting? CPhysical::GetLightingFromCol
    float			fLighting_2;							// 0x130 added to col lighting in CPhysical::GetTotalLighting
    BYTE			pad3a[4];								// 0x134
};

enum // m_objectCreatedBy
{
	GAME_OBJECT = 1,
	MISSION_OBJECT = 2,
	TEMP_OBJECT = 3,
	MISSION_BRAIN_OBJECT = 6,
};

class NOVMT CObject : public CPhysical
{
public:
	void*				m_pObjectList;
	uint8_t				m_objectCreatedBy;
	__int8				field_13D;
	__int16				field_13E;
	bool				bObjectFlag0 : 1;
	bool				bObjectFlag1 : 1;
	bool				bObjectFlag2 : 1;
	bool				bObjectFlag3 : 1;
	bool				bObjectFlag4 : 1;
	bool				bObjectFlag5 : 1;
	bool				m_bIsExploded : 1;
	bool				bObjectFlag7 : 1;
	bool				m_bIsLampPost : 1;
	bool				m_bIsTargetable : 1;
	bool				m_bIsBroken : 1;
	bool				m_bTrainCrossEnabled : 1;
	bool				m_bIsPhotographed : 1;
	bool				m_bIsLiftable : 1;
	bool				bObjectFlag14 : 1;
	bool				m_bIsDoor : 1;
	bool				m_bHasNoModel : 1;
	bool				m_bIsScaled : 1;
	bool				m_bCanBeAttachedToMagnet : 1;
	bool				bObjectFlag19 : 1;
	bool				bObjectFlag20 : 1;
	bool				bObjectFlag21 : 1;
	bool				m_bFadingIn : 1;
	bool				m_bAffectedByColBrightness : 1;
	bool				bObjectFlag24 : 1;
	bool				m_bDoNotRender : 1;
	bool				m_bFadingIn2 : 1;
	bool				bObjectFlag27 : 1;
	bool				bObjectFlag28 : 1;
	bool				bObjectFlag29 : 1;
	bool				bObjectFlag30 : 1;
	bool				bObjectFlag31 : 1;
	unsigned char		m_nColDamageEffect;
	__int8 field_145;
	__int8 field_146;
	__int8 field_147;
	unsigned char		m_nLastWeaponDamage;
	unsigned char		m_nColBrightness;
	FLAUtils::int16     m_wCarPartModelIndex;
	// this is used for detached car parts
	FLAUtils::int8      m_nCarColor[4];
	// time when this object must be deleted
	unsigned __int32    m_dwRemovalTime;
	float               m_fHealth;
	// this is used for door objects
	float               m_fDoorStartAngle;
	float               m_fScale;
	void*				m_pObjectInfo;
	void*				m_pFire; // CFire *
	short				field_168;
	// this is used for detached car parts
	short				m_wPaintjobTxd;
	// this is used for detached car parts
	RwTexture*          m_pPaintjobTex;
	void*				m_pDummyObject;
	// time when particles must be stopped
	signed int			m_dwTimeToStopParticles;
	float               m_fParticlesIntensity;

public:
	inline void			Render_Stub()
	{ CObject::Render(); }

	virtual void		Render() override;

	static void					TryToFreeUpTempObjects_SilentPatch( int numObjects );
	static std::tuple<int,int>	TryOrFreeUpTempObjects( int numObjects, bool force );
};

class CZoneInfo
{
public:
	unsigned char			GangDensity[10];
	unsigned char			DrugDealerCounter;
	CRGBA					ZoneColour;
	bool					unk1 : 1;
	bool					unk2 : 1;
	bool					unk3 : 1;
	bool					unk4 : 1;
	bool					unk5 : 1;
	bool					bUseColour : 1;
	bool					bInGangWar : 1;
	bool					bNoCops : 1;
	unsigned char			flags;
};

enum eWeaponSkill
{
    WEAPONSKILL_POOR = 0,
    WEAPONSKILL_STD,
    WEAPONSKILL_PRO,
    WEAPONSKILL_SPECIAL,    // for cops using pistols differently for example
    WEAPONSKILL_MAX_NUMBER
};

enum eFireType
{
    FIRETYPE_MELEE,
    FIRETYPE_INSTANT_HIT,
    FIRETYPE_PROJECTILE,
    FIRETYPE_AREA_EFFECT,
    FIRETYPE_CAMERA,
    FIRETYPE_USE,

    FIRETYPE_LAST_FIRETYPE
};

enum eWeaponSlot
{
    WEAPONSLOT_TYPE_UNARMED = 0,
    WEAPONSLOT_TYPE_MELEE,
    WEAPONSLOT_TYPE_HANDGUN,
    WEAPONSLOT_TYPE_SHOTGUN,
    WEAPONSLOT_TYPE_SMG,        //4
    WEAPONSLOT_TYPE_MG, 
    WEAPONSLOT_TYPE_RIFLE,
    WEAPONSLOT_TYPE_HEAVY,
    WEAPONSLOT_TYPE_THROWN,
    WEAPONSLOT_TYPE_SPECIAL,    //9
    WEAPONSLOT_TYPE_GIFT,       //10
    WEAPONSLOT_TYPE_PARACHUTE,  //11
    WEAPONSLOT_TYPE_DETONATOR,  //12

    WEAPONSLOT_MAX
};

enum eWeaponState
{
    WEAPONSTATE_READY,
    WEAPONSTATE_FIRING,
    WEAPONSTATE_RELOADING,
    WEAPONSTATE_OUT_OF_AMMO,
    WEAPONSTATE_MELEE_MADECONTACT
};

/**
 * Contains the weapon types/models
 */
enum eWeaponType
{
    WEAPONTYPE_UNARMED=0,
    WEAPONTYPE_BRASSKNUCKLE, 
    WEAPONTYPE_GOLFCLUB,
    WEAPONTYPE_NIGHTSTICK,
    WEAPONTYPE_KNIFE,
    WEAPONTYPE_BASEBALLBAT,
    WEAPONTYPE_SHOVEL,
    WEAPONTYPE_POOL_CUE,
    WEAPONTYPE_KATANA,
    WEAPONTYPE_CHAINSAW,
    
    // gifts
    WEAPONTYPE_DILDO1, // 10
    WEAPONTYPE_DILDO2,
    WEAPONTYPE_VIBE1,
    WEAPONTYPE_VIBE2,
    WEAPONTYPE_FLOWERS,
    WEAPONTYPE_CANE,

    WEAPONTYPE_GRENADE,
    WEAPONTYPE_TEARGAS,
    WEAPONTYPE_MOLOTOV,
    WEAPONTYPE_ROCKET,
    WEAPONTYPE_ROCKET_HS, // 20
    WEAPONTYPE_FREEFALL_BOMB,

    // FIRST SKILL WEAPON
    WEAPONTYPE_PISTOL,          // handguns
    WEAPONTYPE_PISTOL_SILENCED,
    WEAPONTYPE_DESERT_EAGLE,
    WEAPONTYPE_SHOTGUN,         // shotguns
    WEAPONTYPE_SAWNOFF_SHOTGUN, // one handed
    WEAPONTYPE_SPAS12_SHOTGUN,
    WEAPONTYPE_MICRO_UZI,       // submachine guns
    WEAPONTYPE_MP5,
    WEAPONTYPE_AK47, // 30      // machine guns 
    WEAPONTYPE_M4,          
    WEAPONTYPE_TEC9,            // this uses stat from the micro_uzi
    // END SKILL WEAPONS
    
    WEAPONTYPE_COUNTRYRIFLE,    // rifles
    WEAPONTYPE_SNIPERRIFLE, 
    WEAPONTYPE_ROCKETLAUNCHER,  // specials
    WEAPONTYPE_ROCKETLAUNCHER_HS,
    WEAPONTYPE_FLAMETHROWER,
    WEAPONTYPE_MINIGUN,
    WEAPONTYPE_REMOTE_SATCHEL_CHARGE,
    WEAPONTYPE_DETONATOR, // 40 // plastic explosive
    WEAPONTYPE_SPRAYCAN,
    WEAPONTYPE_EXTINGUISHER,
    WEAPONTYPE_CAMERA,
    WEAPONTYPE_NIGHTVISION,
    WEAPONTYPE_INFRARED,
    WEAPONTYPE_PARACHUTE,
    WEAPONTYPE_LAST_WEAPONTYPE,

    WEAPONTYPE_ARMOUR,
    // these are possible ways to die
    WEAPONTYPE_RAMMEDBYCAR,
    WEAPONTYPE_RUNOVERBYCAR, // 50
    WEAPONTYPE_EXPLOSION,
    WEAPONTYPE_UZI_DRIVEBY,
    WEAPONTYPE_DROWNING,
    WEAPONTYPE_FALL,
    WEAPONTYPE_UNIDENTIFIED,    // Used for damage being done
    WEAPONTYPE_ANYMELEE,
    WEAPONTYPE_ANYWEAPON,
    WEAPONTYPE_FLARE,
};

class CWeapon
{
public:
	eWeaponType		m_eWeaponType;
	eWeaponState	m_eState;
	int				m_nAmmoInClip;
	int				m_nAmmoTotal;
	int				m_nTimer;
	int				m_Unknown;
	void*			m_pParticle;
};

class CWeaponInfo
{
public:
	DWORD				weaponType;
	DWORD				targetRange;
	DWORD				weaponRange;
	int					dwModelID;
	int					dwModelID2;
	int					nSlot;
	DWORD				hexFlags;
	DWORD				animStyle;
	WORD				ammoClip;
	DWORD				fireOffsetX;
	DWORD				fireOffsetY;
	DWORD				fireOffsetZ;
	DWORD				skillLevel;
	DWORD				reqStatLevel;
	float				accuracy;
	DWORD				moveSpeed;
	DWORD				animLoopStart;
	DWORD				animLoopEnd;
	DWORD				animLoopFire;
	DWORD				animLoop2Start;
	DWORD				animLoop2End;
	DWORD				animLoop2Fire;
	DWORD				breakoutTime;
	DWORD				speed;
	DWORD				radius;
	DWORD				lifespan;
	DWORD				spread;
	DWORD				animStyle2;

public:
	inline float				GetAccuracy() 
							{ return accuracy; };
	inline DWORD				GetWeaponType() 
							{ return weaponType; };
	inline DWORD				GetClipSize() 
							{ return ammoClip; };
	inline DWORD				GetWeaponSlot() 
							{ return nSlot; };

	static CWeaponInfo*		(*GetWeaponInfo)(eWeaponType weaponID, signed char bType);
};

class CShadowCamera
{
public:
	RwCamera*		m_pCamera;
	RwTexture*		m_pTexture;

public:
	void			InvertRaster();

	RwCamera*		Update(CEntity* pEntity);
};

#include <vector>

class CEscalator
{
private:
	CVector		field_0;
	CVector		field_C;
	CVector		field_18;
	CVector		field_24;
	CMatrix		m_matrix;
	bool		field_78;
	bool		m_bExists;
	bool		field_7A;
	int			field_7C;
	int			field_80;
	int			field_84;
	char		gap_88[8];
	CVector		field_90;
	float		field_9C;
	float		field_A0;
	CEntity*	m_pMainEntity;
	CEntity*	m_pSteps[42];

public:
	static std::vector<CEntity*>	ms_entitiesToRemove;

public:
	void		SwitchOffNoRemove();
};


RpAtomic* ShadowCameraRenderCB(RpAtomic* pAtomic);

static_assert(sizeof(CEntity) == 0x38, "Wrong size: CEntity");
static_assert(sizeof(CPhysical) == 0x138, "Wrong size: CPhysical");
static_assert(sizeof(CObject) == 0x17C, "Wrong size: CObject");
static_assert(sizeof(CEscalator) == 0x150, "Wrong size: CEscalator");

#endif