#include "common.h"
#include "patcher.h"
#include "main.h"
#include "Darkel.h"
#include "Timer.h"
#include "DMAudio.h"
#include "Population.h"
#include "Weapon.h"
#include "World.h"
#include "PlayerPed.h"
#include "Stats.h"
#include "Font.h"
#include "Text.h"

int32 &CDarkel::TimeLimit = *(int32*)0x885BAC;
int32 &CDarkel::PreviousTime = *(int32*)0x885B00;
int32 &CDarkel::TimeOfFrenzyStart = *(int32*)0x9430D8;
int32 &CDarkel::WeaponType = *(int32*)0x9430F0;
int32 &CDarkel::AmmoInterruptedWeapon = *(int32*)0x8E29C8;
int32 &CDarkel::KillsNeeded = *(int32*)0x8F1AB8;
int8 &CDarkel::InterruptedWeapon = *(int8*)0x95CD60;
int8 &CDarkel::bStandardSoundAndMessages = *(int8*)0x95CDB6;
int8 &CDarkel::bNeedHeadShot = *(int8*)0x95CDCA;
int8 &CDarkel::bProperKillFrenzy = *(int8*)0x95CD98;
eKillFrenzyStatus &CDarkel::Status = *(eKillFrenzyStatus*)0x95CCB4;
int16 *CDarkel::RegisteredKills = (int16*)0x6EDBE0;
int32 &CDarkel::ModelToKill = *(int32*)0x8F2C78;
int32 &CDarkel::ModelToKill2 = *(int32*)0x885B40;
int32 &CDarkel::ModelToKill3 = *(int32*)0x885B3C;
int32 &CDarkel::ModelToKill4 = *(int32*)0x885B34;
wchar *CDarkel::pStartMessage = (wchar*)0x8F2C08;

int32 CDarkel::CalcFade(uint32 time, int32 start, uint32 end) {
	if (time >= start && time <= end) {
		if (time >= start + 500) {
			if (time <= end - 500)
				return 255;
			else
				return 255 * (end - time) / 500;
		}
		else
			return 255 * (time - start) / 500;
	}
	else
		return 0;
}

// This function has been cleaned up from unused stuff.
#if 0
WRAPPER void CDarkel::DrawMessages(void) { EAXJMP(0x420920); }
#else
void CDarkel::DrawMessages()
{
	bool DisplayTimers = false;

	switch (Status) {
	case KILLFRENZY_ONGOING:
		assert(pStartMessage != nil);
		DisplayTimers = true;
		break;
	case KILLFRENZY_PASSED:
	case KILLFRENZY_FAILED:
		DisplayTimers = false;
		break;
	};

	if (DisplayTimers) {
		CFont::SetPropOn();
		CFont::SetBackgroundOff();
		CFont::SetBackGroundOnlyTextOn();
		CFont::SetAlignment(ALIGN_RIGHT);
		CFont::SetRightJustifyWrap(-SCREEN_WIDTH);
		CFont::SetFontStyle(FONT_HEADING);
		CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f));

		float AlignToHUD = SCREEN_SCALE_X(-10.0f);
		int32 a = (TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart));
		if (CTimer::GetFrameCounter() & 8 || a > 4000) {
			sprintf(gString, "%d:%02d", a / 60000, a % 60000 / 1000);
			AsciiToUnicode(gString, gUString);
			CFont::SetColor(CRGBA(0, 0, 0, 255));
			CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(25.0f) + AlignToHUD, SCREEN_SCALE_Y(112.0f), gUString);

			CFont::SetColor(CRGBA(150, 100, 255, 255));
			CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(27.0f) + AlignToHUD, SCREEN_SCALE_Y(110.0f), gUString);
		}

		if (KillsNeeded <= 0)
			KillsNeeded = 0;

		sprintf((char *)gString, "%d", KillsNeeded);
		AsciiToUnicode(gString, gUString);

		CFont::SetColor(CRGBA(0, 0, 0, 255));
		CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(25.0f) + AlignToHUD, SCREEN_SCALE_Y(134.0f), gUString);

		CFont::SetColor(CRGBA(255, 128, 128, 255));
		CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(27.0f) + AlignToHUD, SCREEN_SCALE_Y(132.0f), gUString);
	}
}
#endif

void CDarkel::Init()
{
	Status = KILLFRENZY_NONE;
}

int16 CDarkel::QueryModelsKilledByPlayer(int32 modelId)
{
	return RegisteredKills[modelId];
}


bool CDarkel::FrenzyOnGoing()
{
	return Status == KILLFRENZY_ONGOING;
}


eKillFrenzyStatus CDarkel::ReadStatus()
{
	return Status;
}

#if 1
WRAPPER void CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle) { EAXJMP(0x421070); }
#else
int32 CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle)
{
	return 0;
}
#endif

#if 1
WRAPPER void CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapontype, bool headshot) { EAXJMP(0x420F60); }
#else
void CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapontype, bool headshot)
{

	
}
#endif

void CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype)
{
	++CStats::NumberKillFrenziesPassed;
}

void CDarkel::ResetModelsKilledByPlayer()
{
	for (int i = 0; i < 200; i++)
		RegisteredKills[i] = 0;
}

#if 0
WRAPPER void CDarkel::ResetOnPlayerDeath() { EAXJMP(0x420E70); }
#else
void CDarkel::ResetOnPlayerDeath()
{
	if (Status != KILLFRENZY_ONGOING)
		return;

	CPopulation::m_AllRandomPedsThisType = -1;
	Status = KILLFRENZY_FAILED;
	TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();

	if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
		WeaponType = WEAPONTYPE_UZI;

	if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
		FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
		CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
	}

	if (FindPlayerVehicle()) {
		FindPlayerPed()->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(FindPlayerPed()->GetWeapon()->m_eWeaponType)->m_nModelId);
		FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
		FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_currentWeapon);
	}


	CPopulation::m_AllRandomPedsThisType = -1;
	Status = KILLFRENZY_FAILED;
	TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();

	if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
		WeaponType = WEAPONTYPE_UZI;

	if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
		FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
		CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
	}

	if (FindPlayerVehicle()) {
		FindPlayerPed()->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(FindPlayerPed()->GetWeapon()->m_eWeaponType)->m_nModelId);
		FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
		FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_currentWeapon);
	}
}
#endif

#if 0
WRAPPER void CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, int16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot) { EAXJMP(0x4210E0); }
#else
void CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, int16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot)
{
	if (weaponType == WEAPONTYPE_UZI_DRIVEBY)
		weaponType = WEAPONTYPE_UZI;

	WeaponType = weaponType;
	Status = KILLFRENZY_ONGOING;
	KillsNeeded = kill;
	ModelToKill = modelId0;
	ModelToKill2 = modelId2;
	ModelToKill3 = modelId3;
	ModelToKill4 = modelId4;
	pStartMessage = text;

	if (text == TheText.Get("PAGE_00")) {
		CDarkel::bProperKillFrenzy = 1;
		CDarkel::pStartMessage = 0;
	}
	else
		bProperKillFrenzy = 0;
	
	bStandardSoundAndMessages = standardSound;
	bNeedHeadShot = needHeadShot;
	TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();
	TimeLimit = time;
	PreviousTime = time / 1000;

	if (weaponType < WEAPONTYPE_TOTALWEAPONS) {
		InterruptedWeapon = FindPlayerPed()->m_currentWeapon;
		FindPlayerPed()->GiveWeapon(weaponType, 0);
		AmmoInterruptedWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal;
		FindPlayerPed()->GiveWeapon(weaponType, 30000);
		FindPlayerPed()->m_nSelectedWepSlot = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
		FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_nSelectedWepSlot);

		if (FindPlayerVehicle()) {
			FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
			if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal <= CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoInClip)
				CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoInClip = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal;

			FindPlayerPed()->ClearWeaponTarget();
		}
	}
	if (CDarkel::bStandardSoundAndMessages)
		DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_START, 0);
}
#endif

void CDarkel::Update()
{
	if (Status != KILLFRENZY_ONGOING)
		return;

	int32 FrameTime = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart);
	if ((TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart)) > 0 || TimeLimit < 0) {
		DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_ONGOING, FrameTime);

		int32 PrevTime = FrameTime / 1000;

		if (PrevTime != PreviousTime) {
			if (PreviousTime < 12)
				DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, PrevTime);
			PreviousTime = PrevTime;
		}

	}
	else {
		CPopulation::m_AllRandomPedsThisType = -1;
		Status = KILLFRENZY_FAILED;
		TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();

		if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
			WeaponType = WEAPONTYPE_UZI;

		if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
			FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
			CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
		}

		if (FindPlayerVehicle()) {
			FindPlayerPed()->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(FindPlayerPed()->GetWeapon()->m_eWeaponType)->m_nModelId);
			FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
			FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_currentWeapon);
		}

		if (bStandardSoundAndMessages)
			DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_FAILED, 0);
	}

	if (KillsNeeded <= 0) {
		CPopulation::m_AllRandomPedsThisType = -1;
		Status = KILLFRENZY_PASSED;

		if (bProperKillFrenzy)
			CStats::AnotherKillFrenzyPassed();

		TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();

		FindPlayerPed()->m_pWanted->SetWantedLevel(0);

		if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
			WeaponType = WEAPONTYPE_UZI;

		if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
			FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
			CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
		}

		if (FindPlayerVehicle()) {
			FindPlayerPed()->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(FindPlayerPed()->GetWeapon()->m_eWeaponType)->m_nModelId);
			FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
			FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_currentWeapon);
		}

		if (bStandardSoundAndMessages)
			DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_PASSED, 0);
	}
}

STARTPATCHES
	/*InjectHook(0x421380, CDarkel::CalcFade, PATCH_JUMP);
	InjectHook(0x420920, CDarkel::DrawMessages, PATCH_JUMP);
	InjectHook(0x420E60, CDarkel::FrenzyOnGoing, PATCH_JUMP);
	InjectHook(0x420650, CDarkel::Init, PATCH_JUMP);
	InjectHook(0x421370, CDarkel::QueryModelsKilledByPlayer, PATCH_JUMP);
	InjectHook(0x420E50, CDarkel::ReadStatus, PATCH_JUMP);
	InjectHook(0x421070, CDarkel::RegisterCarBlownUpByPlayer, PATCH_JUMP);
	InjectHook(0x420F60, CDarkel::RegisterKillByPlayer, PATCH_JUMP);
	InjectHook(0x421060, CDarkel::RegisterKillNotByPlayer, PATCH_JUMP);
	InjectHook(0x421310, CDarkel::ResetModelsKilledByPlayer, PATCH_JUMP);
	InjectHook(0x420E70, CDarkel::ResetOnPlayerDeath, PATCH_JUMP);
	InjectHook(0x4210E0, CDarkel::StartFrenzy, PATCH_JUMP);
	InjectHook(0x420660, CDarkel::Update, PATCH_JUMP);*/
ENDPATCHES