#include "General.h"
#include "CustomKillMessages.h"
#include "ValuesDefines.h"
#include "weaponmgr.h"
#include "DefenseObjectClass.h"
#include "ArmorWarheadManager.h"
#include "gmlog.h"
#include "engine_tt.h"
#include "engine_io.h"
#include "gmgame.h"

// See ValuesDefines.h for a shitload of defines 

DamageValuesStruct* DamageValues[40];
LastDamageDataStruct* LastDamage[128];
StringClass LastVehicle[128];
SimpleDynVecClass<StringClass> KilledStringList;
SimpleDynVecClass<StringClass> SquishStringList;

uint HeadBoneHash = StringClass("HEAD").GetHash();
uint NeckBoneHash = StringClass("NECK").GetHash();

void Console(const char *Format, ...)
{
	char buffer[256];
	va_list va;
	_crt_va_start(va, Format);
	vsnprintf(buffer, 256, Format, va);
	va_end(va);
	Console_Input(buffer);
}

StringClass Get_Random_Killed_String()
{
	LARGE_INTEGER cicles;

	QueryPerformanceCounter(&cicles);
	srand((unsigned int)cicles.QuadPart);
	int i = rand() % KilledStringList.Count();
	return KilledStringList[i];
}

StringClass Get_Random_Squish_String()
{
	LARGE_INTEGER cicles;

	QueryPerformanceCounter(&cicles);
	srand((unsigned int)cicles.QuadPart);
	int i = rand() % SquishStringList.Count();
	return SquishStringList[i];
}

const char* Get_Kill_Weapon(int Warhead, float Damage, int PlayerID)
{	
	// TODO HANDLE GRENADIER AND ROCKET LAUNCHER SPLASH PROPERLY BY KEEPING LIST OF LAST USED WEAPONS
	// TODO HANDLE BURN DAMAGE BY KEEPING LIST OF LAST USED WEAPONS
	for (int i = 1; i < 37; i++)
	{ // check warhead and damage used with our list of damage values
		if (DamageValues[i]->Warhead == Warhead)
		{
			if ((DamageValues[i]->BodyShot == Damage) || ((DamageValues[i]->BodyShot*3) == Damage)
				|| ((DamageValues[i]->BodyShot*5) == Damage))
			{
				if ((DamageValues[i]->Warhead == 1) && (strcmp(DamageValues[i]->Weapon, "Apache") == 0))
				{
					return Get_Team(PlayerID) ? "Orca" : "Apache";
				}
				return DamageValues[i]->Weapon;
			}
		}
	} 

	if (LastVehicle[PlayerID] != NULL) // else return the last vehicle the player used if there is one
	{ 
		return LastVehicle[PlayerID];
	}

	if (Warhead == 16) // else return Volt Rifle warhead
	{
		return "Volt Auto Rifle";
	}

	if ((Warhead == 9) && (Damage == 5.0f)) // else return Tib Auto Rifle splash
	{
		 return "Tiberium Auto Rifle";
	}
	if (Warhead == 4) // else Explosive, check for rocket launcher splash
	{
		return "Rocket Launcher";
	}
	// TODO: Handle splash damage, check for splash weapons and ammmo count
	return "ERROR";
}
/*	if ((Damage < 0.8f) && 
		(ArmorWarheadManager::Get_Special_Damage_Type(Warhead) != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE))
	{
		int SpecialType = ArmorWarheadManager::Get_Special_Damage_Type(Warhead);
		if (SpecialType = ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_ELECTRIC)
		{
			return "Volt Auto Rifle";
		}
	} */

const char* Get_Head_Neck_Shot_String(int Warhead, float Damage)
{
	for (int i = 1; i < 37; i++)
	{
		if (DamageValues[i]->Warhead == Warhead)
		{
			if (DamageValues[i]->BodyShot == Damage)
			{
				return "";
			}
			else if ((DamageValues[i]->BodyShot*3.0f) == Damage)
			{
				return "with a NECK SHOT!";
			}
			else if ((DamageValues[i]->BodyShot*5.0f) == Damage)
			{
				return "with a HEAD SHOT!";
			}
		}
	}
	return "";
}

void Damage_Check(PhysicalGameObj* damager, PhysicalGameObj* target, int Warhead, float Damage)
{
	GameObject* Attacker = (GameObject*)damager;
	int DamagerID = Get_Player_ID(Attacker);
	
	if (Damage > 0.0f)
	{
		LastDamage[DamagerID]->Damage = Damage;
		LastDamage[DamagerID]->Warhead = Warhead;
		LastDamage[DamagerID]->KillsFromVehicle = Find_Player(DamagerID)->KillsFromVehicle;
		LastDamage[DamagerID]->LastDamageTime = (int)time(NULL);
	}
}

CustomKillMessages::CustomKillMessages()
{
	for( int i = 1; i < 128; i++)
	{
		LastDamageDataStruct* p = new LastDamageDataStruct;
		p->Damage = 0.0f;
		p->KillsFromVehicle = 0;
		p->Warhead = 0;
		p->KillDetected = false;
		p->LastDamageTime = 0;
		LastDamage[i] = p;
	}

	Load_All_Damage_Values();
	Load_Last_Vehicle();

	RegisterEvent(EVENT_GLOBAL_INI,this);
	RegisterEvent(EVENT_OBJECT_CREATE_HOOK,this);
	RegisterEvent(EVENT_LOAD_LEVEL_HOOK,this);
	RegisterEvent(EVENT_STOCK_DAMAGE_HOOK,this);
	RegisterEvent(EVENT_TT_DAMAGE_HOOK,this);
	RegisterEvent(EVENT_PLAYER_LEAVE_HOOK,this);
}

CustomKillMessages::~CustomKillMessages()
{
	UnregisterEvent(EVENT_GLOBAL_INI,this);
	UnregisterEvent(EVENT_OBJECT_CREATE_HOOK,this);
	UnregisterEvent(EVENT_LOAD_LEVEL_HOOK,this);
	UnregisterEvent(EVENT_STOCK_DAMAGE_HOOK,this);
	UnregisterEvent(EVENT_TT_DAMAGE_HOOK,this);
	UnregisterEvent(EVENT_PLAYER_LEAVE_HOOK,this);
}

void CustomKillMessages::OnPlayerLeave(int PlayerID)
{
	LastDamage[PlayerID]->Damage = 0.0f;
	LastDamage[PlayerID]->KillsFromVehicle = 0;
	LastDamage[PlayerID]->Warhead = 0;
	LastDamage[PlayerID]->KillDetected = false;
	LastDamage[PlayerID]->LastDamageTime = 0;
}

bool CustomKillMessages::OnTtDamage(PhysicalGameObj* damager, PhysicalGameObj* target, const AmmoDefinitionClass* ammo, const char* bone)
{
	if ((!Commands->Is_A_Star((GameObject*)damager)) || !Commands->Is_A_Star((GameObject*)target)) 
		if (!Get_Vehicle_Driver((GameObject*)damager)) return true; 

	float Damage = (float)ammo->Damage;

	StringClass Stripped = bone;
	Stripped.TruncateLeft(Stripped.Get_Length()-4);
	uint BoneHash = StringClass(Stripped).GetHash();

	if (BoneHash == HeadBoneHash)
	{
		Damage *= 5.0f;
	}
	else if (BoneHash == NeckBoneHash)
	{
		Damage *= 3.0f;
	}

	Damage_Check(damager, target, (int)ammo->Warhead, Damage); 
	return true;
}
bool CustomKillMessages::OnStockDamage(PhysicalGameObj* damager, PhysicalGameObj* target, float damage, uint warheadId)
{
	if ((!Commands->Is_A_Star((GameObject*)damager)) || !Commands->Is_A_Star((GameObject*)target)) 
		if (!Get_Vehicle_Driver((GameObject*)damager)) return true; 	

	Damage_Check(damager, target, (int)warheadId, damage);
	return true;
}

void CustomKillMessages::OnLoadGlobalINISettings(INIClass *SSGMIni)
{
	int Count = SSGMIni->Entry_Count("CustomKillMessages_KilledStrings");
	for (int i = 0; i < Count; i++)
	{
		const char *Entry = SSGMIni->Get_Entry("CustomKillMessages_KilledStrings", i);
		StringClass Insert;
		SSGMIni->Get_String(Insert, "CustomKillMessages_KilledStrings", Entry);
		KilledStringList.Add(Insert);
	}
	
	Count = SSGMIni->Entry_Count("CustomKillMessages_SquishStrings");
	for (int i = 0; i < Count; i++)
	{
		const char *Entry = SSGMIni->Get_Entry("CustomKillMessages_SquishStrings", i);
		StringClass Insert;
		SSGMIni->Get_String(Insert, "CustomKillMessages_SquishStrings", Entry);
		SquishStringList.Add(Insert);
	}
}

void CustomKillMessages::OnObjectCreate(void *data,GameObject *obj)
{
	if (Commands->Is_A_Star(obj))
	{
		Attach_Script_Once(obj,"Iran_Detect_Player_Kill","");
	}
}

void CustomKillMessages::OnLoadLevel()
{
	for( int i = 1; i < 128; i++)
	{
		LastDamage[i]->Damage = 0.0f;
		LastDamage[i]->KillsFromVehicle = 0;
		LastDamage[i]->Warhead = 0;
		LastDamage[i]->LastDamageTime = 0;
		LastDamage[i]->KillDetected = false;
	}
}

void Iran_Detect_Player_Kill::Created(GameObject *obj)
{
	Commands->Start_Timer(obj,this,5.0f, 1);
	int PlayerID = Get_Player_ID(obj);
	LastDamage[PlayerID]->KillsFromVehicle = Find_Player(PlayerID)->KillsFromVehicle;
}

void Iran_Detect_Player_Kill::Killed(GameObject *obj, GameObject *Killer)
{
	Explosion = GetExplosionObj();
	if (obj == Killer) return;
	if (!Commands->Is_A_Star(Killer)) return;

	KillerObj = Killer;
	VictimObj = obj;
	WasSquish = false;

	if (Explosion)
	{
		if (Is_Beacon(Explosion))
		{
			const char* Beacon = Get_Translated_Preset_Name(Explosion);
			KillWeapon = Beacon;
			delete []Beacon;
		}
		else if (Is_C4(Explosion))
		{
			int C4Mode = Get_C4_Mode(Explosion);
			if (C4Mode == 1)
			{
			KillWeapon = "Remote C4";
			}
			else if (C4Mode == 2)
			{
				KillWeapon = "Timed C4";
			}
			else if (C4Mode == 3)
			{
				KillWeapon = "Proxy C4";
			}
		}
	}

	if ( (int)time(NULL) == LastDamage[Get_Player_ID(Killer)]->LastDamageTime)
	{
		LastDamage[Get_Player_ID(Killer)]->KillDetected = true;
	}
	Commands->Start_Timer(obj,this,.1f, 2);
}

void Iran_Detect_Player_Kill::Timer_Expired(GameObject *obj, int number)
{
	if (number == 1)
	{
		if (Get_Vehicle(obj))
		{
			const char* Preset = Get_Translated_Preset_Name(Get_Vehicle(obj));
			LastVehicle[Get_Player_ID(obj)] = Preset;
			delete []Preset;
		}
		Commands->Start_Timer(obj,this,5.0f, 1);
	}

	else if (number == 2)
	{
		int KillerID = Get_Player_ID(KillerObj);
		
		if (!LastDamage[KillerID]->KillDetected && Find_Player(KillerID)->KillsFromVehicle > LastDamage[KillerID]->KillsFromVehicle)
		{
			WasSquish = true;
			LastDamage[KillerID]->KillsFromVehicle = Find_Player(KillerID)->KillsFromVehicle;
		}
		LastDamage[KillerID]->KillDetected = false;
		
		if (KillWeapon.Is_Empty())
		{
			if (WasSquish)
			{
				const char* KillVehicle = Get_Translated_Preset_Name(Get_Vehicle(KillerObj));
				KillWeapon = KillVehicle;
				delete []KillVehicle;
			}	
			else
			{
				KillWeapon = Get_Kill_Weapon(LastDamage[KillerID]->Warhead, LastDamage[KillerID]->Damage, KillerID);
			}
		}
		StringClass Append = "";

		if (WasSquish)
		{
			Append = Get_Random_Squish_String();
		}
		else
		{
			Append = Get_Head_Neck_Shot_String(LastDamage[KillerID]->Warhead, LastDamage[KillerID]->Damage);
		} 

		StringClass KilledString = Get_Random_Killed_String();

		unsigned int Red = 0,Blue = 0,Green = 0;
		Get_Team_Color(Get_Team(KillerID),&Red,&Green,&Blue);

		const char* VictimName = Get_Player_Name(VictimObj); // ALLOCATE VictimName
		const char* KillerName = Get_Player_Name(KillerObj); // ALLOCATE KillerName
		const char* KillerPreset = Get_Translated_Preset_Name(KillerObj); // ALLOCATE KillerPreset
		const char* VictimWeapon = Get_Translated_Preset_Name(obj); // ALLOCATE VictimWeapon
		const char* VictimPreset = Get_Current_Translated_Weapon(obj); // ALLOCATE VictimPreset

/*		Console("CMSG %d,%d,%d %s (%s/%s) %s %s (%s/%s)%s", Red, Green, Blue, KillerName,
			KillerPreset, KillWeapon, KilledString, 
			VictimName, Get_Translated_Preset_Name(obj), Get_Current_Translated_Weapon(obj),
			Append); */
		SSGMGameLog::Log_RenLog(TT_FORMAT(512, "[IRANKILL]%s (%s/%s) %s %s (%s/%s) %s [LD: %.0f]", KillerName,
			KillerPreset, KillWeapon, KilledString,  VictimName, VictimPreset, 
			VictimWeapon, Append, LastDamage[KillerID]->Damage));

		Console("CMSG %d,%d,%d %s %s %s %s", Red, Green, Blue,
			KillerName, KilledString, VictimName, Append);

		Console("sndp %d %s", KillerID, "correction_3.wav"); // Need to manually play the boink sound

		delete []KillerPreset; // DEALLOCATE KillerPreset
		delete []VictimName; // DEALLOCATE VictimName
		delete []KillerName; // DEALLOCATE KillerName
		delete [] VictimWeapon; // DEALLOCATE VictimWeapon
		delete []VictimPreset; // DEALLOCATE VictimPreset
	}
}

void Load_Last_Vehicle()
{
	for (int i = 0; i < 128; i++)
	{
		LastVehicle[i] = "";
	}
}
void Load_Damage_Value(int Value, const char* Weapon, int Warhead, float BodyShot, bool VehicleWeapon)
{
	DamageValuesStruct* p = new DamageValuesStruct;
	p->Weapon = Weapon;
	p->Warhead = Warhead;
	p->BodyShot = BodyShot;
	p->VehicleWeapon = VehicleWeapon;
	DamageValues[Value] = p;
}

void Load_All_Damage_Values()
{
	Load_Damage_Value(DAMAGEVALUES_APACHEMG, "Apache", 1, 20.0f, true); // 1
	Load_Damage_Value(DAMAGEVALUES_APACHEROCKET, "Apache", 6, 30.0f, true); // 2
	Load_Damage_Value(DAMAGEVALUES_APC, "APC", 1, 12.0f, true); // 3
	Load_Damage_Value(DAMAGEVALUES_GDIAUTORIFLE, "GDI AutoRifle", 3, 7.0f, false); // 4
	Load_Damage_Value(DAMAGEVALUES_NODAUTORIFLE, "Nod AutoRifle", 3, 5.0f, false); //5 
	Load_Damage_Value(DAMAGEVALUES_BUGGY, "Buggy", 1, 12.0f, true); // 6
	Load_Damage_Value(DAMAGEVALUES_CANNONEMPLACEMENT, "Cannon Emplacement", 6, 30.0f, true); // 7
	Load_Damage_Value(DAMAGEVALUES_CHAINGUN, "Chaingun", 1, 5.0f, false); // 8
	Load_Damage_Value(DAMAGEVALUES_CHEMSPRAYER, "Chem Sprayer", 28, 2.25f, false); // 9
	Load_Damage_Value(DAMAGEVALUES_FLAMETANK, "Flame Tank", 27, 13.0f, true); //10
	Load_Damage_Value(DAMAGEVALUES_FLAMETHROWER, "Flame Thrower", 27, 2.0f, false); // 11
	Load_Damage_Value(DAMAGEVALUES_GRENADELAUNCHER, "Grenade Launcher", 1, 15.0f, false); // 12
	Load_Damage_Value(DAMAGEVALUES_GUNEMPLACEMENT, "Gun Emplacement", 1, 5.0f, true); // 13
	Load_Damage_Value(DAMAGEVALUES_HUMMVEE, "Hummvee", 1, 12.0f, true); // 14
	Load_Damage_Value(DAMAGEVALUES_LASERCHAINGUN, "LCG", 13, 8.0f, false); // 15
	Load_Damage_Value(DAMAGEVALUES_LASERRIFLE, "Laser Rifle", 13, 10.0f, false); // 16
	Load_Damage_Value(DAMAGEVALUES_LIGHTTANK, "Light Tank", 6, 60.0f, true); // 17
	Load_Damage_Value(DAMAGEVALUES_MAMMOTHTANKCANNON, "Mammoth Tank", 6, 75.0f, true); // 18
	Load_Damage_Value(DAMAGEVALUES_MAMMOTHTANKROCKET, "Mammoth Tank", 6, 15.0f, true); // 19
	Load_Damage_Value(DAMAGEVALUES_MEDIUMTANK, "Medium Tank", 6, 80.0f, true); // 20
	Load_Damage_Value(DAMAGEVALUES_MOBILEARTILLERY, "Mobile Artillery", 6, 110.0f, true); // 21
	Load_Damage_Value(DAMAGEVALUES_MRLS, "MRLS", 6, 18.0f, true); // 22
	Load_Damage_Value(DAMAGEVALUES_ORCAMG, "Orca", 1, 20.0f, true); // 23
	Load_Damage_Value(DAMAGEVALUES_ORCAROCKET, "Orca", 6, 30.0f, true); // 24
	Load_Damage_Value(DAMAGEVALUES_PERSONALIONCANNON, "Personal Ion Cannon", 16, 200.0f, false); // 25
	Load_Damage_Value(DAMAGEVALUES_PISTOL, "Automatic Pistol", 3, 10.0f, false); // 26
	Load_Damage_Value(DAMAGEVALUES_RAILGUN, "Rail Gun", 13, 200.0f, false); // 27
	Load_Damage_Value(DAMAGEVALUES_RAMJETRIFLE, "Ramjet Rifle", 3, 200.0f, false); // 28
	Load_Damage_Value(DAMAGEVALUES_RECONBIKE, "Recon Bike", 6, 10.0f, true); // 29
	Load_Damage_Value(DAMAGEVALUES_ROCKETEMPLACEMENT, "Rocket Emplacement", 6, 0.0f, true); // 30
	Load_Damage_Value(DAMAGEVALUES_ROCKETLAUNCHERSTRONG, "Rocket Launcher (Strong)", 1, 40.0f, false); // 31
	Load_Damage_Value(DAMAGEVALUES_ROCKETLAUNCHERWEAK, "Rocket Launcher (Weak)", 1, 35.0f, false); // 32
	Load_Damage_Value(DAMAGEVALUES_SHOTGUN, "Shotgun", 3, 15.0f, false); // 33
	Load_Damage_Value(DAMAGEVALUES_SNIPERRIFLE, "Sniper Rifle", 3, 100.0f, false); // 34
	Load_Damage_Value(DAMAGEVALUES_STEALTHTANK, "Stealth Tank", 6, 30.0f, true); // 35
	Load_Damage_Value(DAMAGEVALUES_TIBERIUMAUTORIFLE, "Tiberium Auto Rifle", 10, 8.0f, false); // 36
	Load_Damage_Value(DAMAGEVALUES_TIBERIUMFLECHETTEGUN, "Tiberium Flechette Gun", 11, 10.0f, false); // 37
}

ScriptRegistrant<Iran_Detect_Player_Kill> Iran_Detect_Player_Kill_Registrant("Iran_Detect_Player_Kill","");

CustomKillMessages customKillMessages;

extern "C" __declspec(dllexport) Plugin* Plugin_Init()
{
	return &customKillMessages;
}
