#include "general.h"
#include "NoC4DefuseOnLeave.h"

#include "scripts.h"
#include "engine.h"
#include "engine_da.h"
#include "da.h"
#include "GameObjManager.h"
#include "da_settings.h"

struct ProxyOwner_t
{
	C4GameObj *C4;
	cPlayer *cPlayer;
};

/* Vector with as key the ID of the proxy C4 object and the value is the cPlayer* of the original owner */
SimpleDynVecClass<ProxyOwner_t> ProxyOwners;

/* Array of bools to cache if a player has spawned yet or not */
bool SpawnedOnce[128];

//Settings
bool Disarm;
float DisarmTime;
bool PokeOwnership;
bool PokeDisarm;
bool AttackOwnership;
bool AttackDisarm;
bool OwnerProxyDisarm;
bool ProxyMessageSent;

void NoC4DefuseOnLeave::Init()
{
	Register_Event(DAEvent::LEVELLOADED);
	Register_Event(DAEvent::SETTINGSLOADED);
	Register_Event(DAEvent::PLAYERJOIN);
	Register_Event(DAEvent::PLAYERLEAVE);
	Register_Object_Event(DAObjectEvent::CREATED, DAObjectEvent::ALL);
}

void NoC4DefuseOnLeave::Settings_Loaded_Event()
{
	Disarm = DASettingsManager::Get_Bool("Disarm", true);
	DisarmTime = DASettingsManager::Get_Float("DisarmTime", 600.0f);
	PokeOwnership = DASettingsManager::Get_Bool("PokeOwnership", false);
	PokeDisarm = DASettingsManager::Get_Bool("PokeDisarm", false);
	AttackOwnership = DASettingsManager::Get_Bool("AttackOwnership", false);
	AttackDisarm = DASettingsManager::Get_Bool("AttackDisarm", false);
	OwnerProxyDisarm = DASettingsManager::Get_Bool("OwnerProxyDisarm", false);
}

NoC4DefuseOnLeave::~NoC4DefuseOnLeave(){
}

/* Attach the "C4NoDefuse_Script" because we need its ::Killed() event */
void NoC4DefuseOnLeave::Object_Created_Event(GameObject *obj)
{
	/* if C4 and C4 mode is that of a Proxy C4 */
	if (Is_C4(obj) && (Get_C4_Mode(obj) == 3))
	{
		//Attach_Script_Once(obj, "C4NoDefuse_Script", "");
		obj->Add_Observer(new C4ObserverClass );

		/* Set player data to NULL */
		C4GameObj* C4 = (C4GameObj*)obj;
		C4->Set_Player_Data(0);

		/* Add C4GameObj pointer and cPlayer* pointer to our vector */
		cPlayer* cP = Find_Player(Get_Player_ID(Get_C4_Planter(obj)));

		struct ProxyOwner_t Data;
		Data.C4 = C4;
		Data.cPlayer = cP;

		ProxyOwners.Add(Data);
	}
	else if (Commands->Is_A_Star(obj))
	{
		int ID = Get_Player_ID(obj);

		if (SpawnedOnce[ID] == false)
		{
			SpawnedOnce[ID] = true;
			Restore_Proxy_C4_After_Join(ID);
		}

		for (auto c4_node = GameObjManager::C4GameObjList.Head(); c4_node; c4_node = c4_node->Next())
		{
			C4GameObj* C4 = (C4GameObj *)c4_node->Data();
			for (int i = 0; i < ProxyOwners.Count(); i++)
			{
				/* if our C4 is in the C4 list and the cPlayer is equal to the owner cPlayer */
				if ((ProxyOwners[i].C4 == C4) && (ProxyOwners[i].cPlayer == Find_Player(ID)))
				{
					/* Restore owner */
					C4->Set_Owner(obj);
					C4->Set_Player_Data(0);
				}
			}
		}
	}
}

/* Empty our C4 ownership vector */
void NoC4DefuseOnLeave::Level_Loaded_Event()
{
	ProxyOwners.Delete_All();
}


class C4DisarmTimer : public DAGameObjObserverClass {
public:

	C4DisarmTimer(int Time) { t = (float)Time; }
	C4DisarmTimer(float Time) { t = Time; }
	float t;

	virtual const char *Get_Name() {
		return "C4DisarmTimer";
	}

	virtual void Init() {
		Start_Timer(1, t);
		ProxyMessageSent = false;
	}

	virtual void Timer_Expired(GameObject *obj, int Number)
	{
		if (!Get_C4_Planter(Get_Owner())) {
			Disarm_C4(Get_Owner());
			if (!ProxyMessageSent) {
				ProxyMessageSent = true;
				DA::Team_Color_Message_With_Team_Color(Get_Player_Type(Get_Owner()), "Unowned Proximity Mines where Disarmed check the Base!");
			}
			Set_Delete_Pending();
		}
		else
			Set_Delete_Pending();
	}
};


/* Loop over all C4GameObjs and add the ones to our vector that have the same owner
as the player that left. Then set their owner and player data to NULL */
void  NoC4DefuseOnLeave::Player_Leave_Event(cPlayer *Player)
{
	int PlayerID = Player->Get_Id();
	SpawnedOnce[PlayerID] = false;
	/* Go over all Proxy C4 in the world */
	for (auto c4_node = GameObjManager::C4GameObjList.Head(); c4_node; c4_node = c4_node->Next())
	{
		GameObject *PlayerObj = Player->Get_GameObj();
		C4GameObj* C4 = (C4GameObj *)c4_node->Data();

		const AmmoDefinitionClass* ammo = C4->Get_Ammo_Def();
		int C4Team = Commands->Get_Player_Type((GameObject*)C4);

		/* Check if AmmoType is that of a Proxy C4 and if the c4 team is the same as the player's */
		if (((int)ammo->AmmoType == 3) && (C4Team == Get_Team(PlayerID)))
		{
			/* Check if C4 is a valid pointer and the C4's owner is the same as the player */
			if (C4 && (Get_C4_Planter((GameObject*)C4) == PlayerObj))
			{
				/* Set owner to NULL */
				C4->Set_Owner(0);
				/* Set PlayerDataClass to NULL */
				C4->Set_Player_Data(0);

				if ( Disarm )
					C4->Add_Observer(new C4DisarmTimer(DisarmTime));
			}
		}
	}
}

/* Restore ownership of Proxy C4 in our ProxyC4OwnerMap to PlayerID,
then remove the entry for his Proxy C4's. */
void NoC4DefuseOnLeave::Restore_Proxy_C4_After_Join(int PlayerID)
{
	/* Don't do anything if our ProxyOwners vector is empty */
	if (ProxyOwners.Count() == 0) { return; }

	/* Go over all Proxy C4 in the world */
	for (auto c4_node = GameObjManager::C4GameObjList.Head(); c4_node; c4_node = c4_node->Next())
	{		
		C4GameObj* C4 = (C4GameObj *)c4_node->Data();

		const AmmoDefinitionClass* ammo = C4->Get_Ammo_Def();
		int C4Team = Commands->Get_Player_Type((GameObject*)C4);

		/* Check if AmmoType is that of a Proxy C4 and if the c4 team is the same as the player's */
		if ( ((int)ammo->AmmoType == 3) && (C4Team == Get_Team(PlayerID)) )
		{
			/* Check if C4 is a valid pointer and that it doesn't have an owner */
			if (C4 && (Get_C4_Planter((GameObject*)C4) == NULL))
			{
				cPlayer* cP = NULL;

				for (int i = 0; i < ProxyOwners.Count(); i++)
				{
					if (ProxyOwners[i].C4 == C4)
					{
						/* Assign cPlayer*, so entry exists */
						cP = ProxyOwners[i].cPlayer;
						/* Remove entry in the ProxyOwners vector */
						ProxyOwners.Delete(i);
						break;
					}
				}

				/* if an entry exists, i.e. cp != NULL */
				if (cP)
				{
					/* Restore the owner of the Proxy C4 */
					GameObject *PlayerObj = Get_GameObj(PlayerID);
					C4->Set_Owner(PlayerObj);
					/* Set PlayerDataClass to NULL */
					C4->Set_Player_Data(0);
				}
			}
		}
	}
}

void  NoC4DefuseOnLeave::Player_Join_Event(cPlayer *Player)
{
	SpawnedOnce[Player->Get_Id()] = false;
}

void Set_ProxyOwner(C4GameObj *C4, cPlayer *cp)
{
	for (int i = 0; i < ProxyOwners.Count(); i++)
	{
		if (ProxyOwners[i].C4 == C4)
		{
			//Set the owner of the c4
			ProxyOwners[i].cPlayer = cp;
			break;
		}
	}
}


void Delete_ProxyOwner(C4GameObj* C4)
{
	for (int i = 0; i < ProxyOwners.Count(); i++)
	{
		if (ProxyOwners[i].C4 == C4)
		{
			/* Remove entry in the ProxyOwners then break because we're done */
			ProxyOwners.Delete(i);
			break;
		}
	}
}

///////////////////////////
// C4ObserverClass
///////////////////////////

void NoC4DefuseOnLeave::Damage_Event(DamageableGameObj *Victim, ArmedGameObj *Damager, float Damage, unsigned int Warhead, float Scale, DADamageType::Type Type) {
	if ( Victim->As_PhysicalGameObj()->As_C4GameObj() )
	{
		//Console_Output("Damage C4\n");
	}
}

//Send fake damage event to Damage_Received if Friendly Fire disabled
bool C4ObserverClass::Damage_Received_Request(ArmedGameObj *Damager, float &Damage, unsigned int &Warhead, float Scale, DADamageType::Type Type)
{
	cGameDataCnc *Game = The_Cnc_Game();
	if (Game->Is_Friendly_Fire_Permitted())
		return true;

	if (!Is_Player(Damager))
		return true;

	if (Get_Owner()->As_PhysicalGameObj()->As_C4GameObj() && Type == DADamageType::REPAIR)
	{
		C4GameObj* C4 = (C4GameObj *)Get_Owner();
		// Own player c4 or c4 unowned
		if (Get_C4_Planter(C4) == Damager->As_SoldierGameObj() || !Get_C4_Planter(C4))
		{
			Damage = fabsf(Damage); // float neg to positive
			C4ObserverClass::Damage_Received(Damager, Damage, Warhead, Scale, Type);
			return false;
		}
	}
	return true;
}

//Set C4 Health instead of actually damaging if Friendly fire off.
void C4ObserverClass::Damage_Received(ArmedGameObj *Damager, float Damage, unsigned int Warhead, float Scale, DADamageType::Type Type) {

	int PlayerID = Get_Player_ID(Damager);
	if (!PlayerID || PlayerID == -1)
		return;

	C4GameObj* C4 = (C4GameObj *)Get_Owner();
	float Health = Commands->Get_Health(Get_Owner());
	const AmmoDefinitionClass* ammo = C4->Get_Ammo_Def();
	int C4Team = Commands->Get_Player_Type((GameObject*)C4);

	if (((int)ammo->AmmoType == 3) && (C4Team == Get_Team(PlayerID)))
	{
		GameObject *PlayerObj = Get_GameObj(PlayerID);
		float NewHealth = Health - Damage;
		cGameDataCnc *Game = The_Cnc_Game();

		// unowned c4
		if ( !Get_C4_Planter(Get_Owner()) )
		{
			if (!AttackOwnership && !AttackDisarm)
				return;

			if (Damage >= Health && AttackOwnership && !AttackDisarm )
			{
				DA::Private_Color_Message(PlayerID, COLORGREEN, "You have claimed unowned C4 as your's, Good Job!");
				GameObject *PlayerObj = Get_GameObj(PlayerID);
				C4->Set_Owner(PlayerObj);
				C4->Set_Player_Data(0);
				float Max_Health = Commands->Get_Max_Health(C4);
				Commands->Set_Health(C4, Max_Health);
				Set_ProxyOwner(C4, Find_Player(PlayerID));
			}
			else
			{
				if ( Health > 5.0 ) 
				{
					if (!Game->Is_Friendly_Fire_Permitted())
						Commands->Set_Health(C4, NewHealth);
				}
				else
					Disarm_C4(C4);
			}
		}

		// Player own C4
		else if ( Get_C4_Planter(C4) == PlayerObj )
		{
			if (OwnerProxyDisarm) {
				if (Health > 5.0) {
					if (!Game->Is_Friendly_Fire_Permitted()) {
						Commands->Set_Health(C4, NewHealth);
					}
				}
				else
					Disarm_C4(C4);
			}
		}
	}
}

void C4ObserverClass::Poked(GameObject *obj, GameObject *Poker) {

	C4GameObj* C4 = (C4GameObj *)Get_Owner();
	const AmmoDefinitionClass* ammo = C4->Get_Ammo_Def();
	int C4Team = Commands->Get_Player_Type(Get_Owner());
	int PlayerID = Get_Player_ID(Poker);
	if (!PlayerID || PlayerID == -1)
		return;

	if (((int)ammo->AmmoType == 3) && (C4Team == Get_Team(PlayerID)))
	{
		GameObject *PlayerObj = Get_GameObj(PlayerID);
		/* Check if C4 is a valid pointer and that it doesn't have an owner */
		if (C4 && (Get_C4_Planter((GameObject*)C4) == NULL))
		{
			if (!PokeOwnership && !PokeDisarm)
				return;

			if (PokeOwnership && !PokeDisarm ) {
				DA::Private_Color_Message(Poker, COLORGREEN, "This C4 is unowned and is now your's.");
				C4->Set_Owner(PlayerObj);
				C4->Set_Player_Data(0);
				float Max_Health = Commands->Get_Max_Health(C4);
				Commands->Set_Health(C4, Max_Health);
				Set_ProxyOwner(C4, Find_Player(PlayerID));
			}

			if (PokeDisarm) {
				Disarm_C4(C4);
				DA::Private_Color_Message(Poker, COLORGREEN, "Unowned C4 has been Disarmed.");
			}
		}
		else if (Get_C4_Planter(C4) == PlayerObj )
		{
			if (OwnerProxyDisarm) {
				Disarm_C4(C4);
				DA::Private_Color_Message(Poker, COLORGREEN, "Your C4 has been Disarmed.");
			}
		}
	}
}

/*
StringClass DamagerName;
if (Find_Player(PlayerID) )
DamagerName = Damager->As_SoldierGameObj()->Get_Player()->Get_Name();
StringClass ProxyOwnerName;
if (Get_C4_Planter(Get_Owner()))
ProxyOwnerName = Get_C4_Planter(Get_Owner())->As_SoldierGameObj()->Get_Player()->Get_Name();
else if (strcmp(ProxyOwnerName, DamagerName) == 0)
*/

void C4ObserverClass::Kill_Received(ArmedGameObj *Killer, float Damage, unsigned int Warhead, float Scale, DADamageType::Type Type) {

	C4ObserverClass::Destroyed();
}

void C4ObserverClass::Destroyed()
{
	C4GameObj* C4 = (C4GameObj*)Get_Owner();
	Delete_ProxyOwner(C4);
	Set_Delete_Pending();
}

Register_Game_Feature(NoC4DefuseOnLeave, "NoC4DefuseOnLeave", "EnableNoC4DefuseOnLeave", 0);