#define IMGUI_DEFINE_MATH_OPERATORS
#include "../sdk/interfaces/interfaces.hpp"
#include "../api/hook/hook.hpp"
//#include "hook.hpp"
#include "menu/menu.hpp"
#include "movement/movement.hpp"
#include "esp/esp.hpp"
#include "chams/chams.hpp"
#include <intrin.h>
#include "world/world.hpp"
#include "external_aim/aim.h"
#include "skins/knife.h"

static void set_color(void* data, int r, int g, int b, int a)
{
	*(byte*)((uintptr_t)data + 0x40) = r;
	*(byte*)((uintptr_t)data + 0x41) = g;
	*(byte*)((uintptr_t)data + 0x42) = b;
	*(byte*)((uintptr_t)data + 0x43) = a;
}
static CHook<bool __fastcall(void*)> g_mouseInputEnabled;
static bool __fastcall hkMouseInputEnabled(void* rcx)
{
	if (menu::IsOpen())
		return false;

	return g_mouseInputEnabled(rcx);
}

static CHook<void* __fastcall(void*, bool)> oIsRelativeMouseMode;
static void* IsRelativeMouseMode(void* pThisptr, bool bActive)
{
	//MENU::bMainActive = bActive;

	if (menu::IsOpen())
		return oIsRelativeMouseMode(pThisptr, false);

	return oIsRelativeMouseMode(pThisptr, bActive);
}

static CHook<void* __fastcall(void*, CEntityInstance*, CHandle)> g_onAddEntity;
static void* __fastcall hkOnAddEntity(void* rcx, CEntityInstance* pInstance,
	CHandle hHandle)
{
	visuals::OnAddEntity(pInstance, hHandle);

	return g_onAddEntity(rcx, pInstance, hHandle);
}
static CHook<void* __fastcall(void*, CEntityInstance*, CHandle)>
g_onRemoveEntity;
static void* __fastcall hkOnRemoveEntity(void* rcx, CEntityInstance* pInstance,
	CHandle hHandle)
{
	visuals::OnRemoveEntity(pInstance, hHandle);
	return g_onRemoveEntity(rcx, pInstance, hHandle);
}
static CHook<void __fastcall(void*, void*, VMatrix*, VMatrix*, VMatrix*,
	VMatrix*)>
	g_getMatricesForView;
static void __fastcall hkGetMatricesForView(void* rcx, void* view,
	VMatrix* pWorldToView,
	VMatrix* pViewToProjection,
	VMatrix* pWorldToProjection,
	VMatrix* pWorldToPixels)
{
	g_getMatricesForView(rcx, view, pWorldToView, pViewToProjection,
		pWorldToProjection, pWorldToPixels);

	math::UpdateViewMatrix(pWorldToProjection);
	visuals::CalculateBoundingBoxes();
}

//bool spottedCheck(C_CSPlayerPawn pawn, C_CSPlayerPawn localPlayer) {
	//if (pawn.getEntitySpotted() & (1 << (localPlayer.GetOwnerHandleIndex())) || localPlayer.getEntitySpotted() & (1 << (pawn.GetOwnerHandleIndex()))) return 1;
	//return 0;
//}

static CHook<bool __fastcall(CCSGOInput*, int, CUserCmd*)> g_CreateMove;
static bool __fastcall hkCreateMove(CCSGOInput* pInput, int nSlot, CUserCmd* pCmd)
{
	const bool result = g_CreateMove(pInput, nSlot, pCmd);
	if (!interfaces::pEngine->IsInGame() || !interfaces::pEngine->IsConnected())
		return result;

	if (!pCmd)
		return result;

	auto basecmd = pCmd->pBase.pBase;
	if (!basecmd)
		return result;

	CCSPlayerController* LocalController = CGameEntitySystem::GetInstance()->GetLocalPlayerController();
	if (LocalController == nullptr)
		return result;

	C_CSPlayerPawn* LocalPawn = LocalController->m_hPawn().Get<C_CSPlayerPawn>();
	if (LocalPawn == nullptr)
		return result;

	CGameEntitySystem* pEntitySystem = CGameEntitySystem::GetInstance();
	if (!pEntitySystem)
		return result;

	if (aim::rcs &&!menu::IsOpen())
		aim::recoilControl(true, LocalPawn);

	for (int i = 1; i <= 64; ++i)
	{
		C_BaseEntity* pEntity = pEntitySystem->GetBaseEntity(i);
		if (!pEntity)
			continue;

		SchemaClassInfoData_t* pClassInfo = pEntity->Schema_DynamicBinding();
		if (pClassInfo == nullptr)
			continue;

		if (std::string_view{ pClassInfo->GetName() }.find("CCSPlayerController") == std::string::npos)
			continue;

		CCSPlayerController* pPlayerController = reinterpret_cast<CCSPlayerController*>(pEntity);
		if (!pPlayerController->m_bPawnIsAlive())
			continue;

		C_CSPlayerPawn* pPawn = pPlayerController->m_hPawn().Get<C_CSPlayerPawn>();
		if (!pPawn)
			continue;

		const bool isLocalPlayer = pPlayerController->m_bIsLocalPlayerController();
		const bool isEnemy = pPawn->IsEnemyWithTeam(LocalController->m_iTeamNum());
		if (LocalController->m_iTeamNum() == pPawn->m_iTeamNum() || !isEnemy)
			continue;

		if (aim::lockedPlayer == LocalPawn && (LocalPawn->m_iHealth() <= 0 || (aim::checkSpotted && !LocalPawn->getEntitySpotted()))) aim::lockedPlayer = 0;
		if ((LocalPawn->m_iHealth() <= 0 || LocalPawn->m_iHealth() > 100) || strcmp(LocalController->m_sSanitizedPlayerName(), "DemoRecorder") == 0) continue;

		auto gameSceneNode = pPawn->m_pGameSceneNode();
		if (!gameSceneNode)
			continue;
		//if (isEnemy) continue;

		if (aim::state && !menu::IsOpen()) {
			aim::aimBot(pPawn, gameSceneNode->getBoneArray(), pCmd, basecmd);
		}
	}
	return true;
}

static CMaterial2* visible = nullptr;
static CMaterial2* visible_black = nullptr;
static CMaterial2* invisible_black = nullptr;
static CMaterial2* invisible = nullptr;
static CMaterial2* visible_bloom = nullptr;

void __fastcall hkDrawObject(void* pAnimatableSceneObjectDesc, void* pDx11,
	CMaterial2_Data_t* arrMeshDraw, int nDataCount,
	void* pSceneView, void* pSceneLayer, void* pUnk,
	void* pUnk2)
{
	if (!interfaces::pEngine->IsConnected() || !interfaces::pEngine->IsInGame())
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw,
			nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	if (!arrMeshDraw || !arrMeshDraw->m_material || !arrMeshDraw->m_pSceneAnimatableObject)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw,
			nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	CHandle hOwner = arrMeshDraw->m_pSceneAnimatableObject->hOwner;

	C_BaseEntity* pEntity = hOwner.Get();
	if (!pEntity)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	SchemaClassInfoData_t* pClassInfo = pEntity->Schema_DynamicBinding();
	if (pClassInfo == nullptr)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	CCSPlayerController* LocalController = CGameEntitySystem::Get().GetLocalPlayerController();
	if (!LocalController)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	C_CSPlayerPawn* LocalPawn = LocalController->m_hPawn().Get<C_CSPlayerPawn>();
	if (!LocalPawn)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	if (pEntity->m_iTeamNum() == LocalPawn->m_iTeamNum())
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	if (std::strcmp(arrMeshDraw->m_material->GetName(), "materials/dev/glowproperty.vmat") == 0)
		return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);

	if (!visible)
		visible = chams::create_visible("visible");
	if (!visible_black)
		visible_black = chams::create_visible_for_black("visible_for_black");
	if (!invisible_black)
		invisible_black = chams::create_invisible_for_black("invisible_for_black");
	if (!invisible)
		invisible = chams::create_invisible("invisible");
	if (!visible_bloom)
		visible_bloom = chams::create_bloom("bloom");

	// weapon
	//if (std::string_view{ pClassInfo->GetName() }.find("C_CSGOViewModel") != std::string_view::npos && chams::weapon)
	//{
	//	arrMeshDraw->m_material = visible;

	//	uint8_t new_color = chams::weapon_value;

	//	arrMeshDraw->m_color().c = new_color;
	//	arrMeshDraw->m_color().m = new_color;
	//	arrMeshDraw->m_color().y = new_color;
	//	arrMeshDraw->m_color().k = 255;


	//	g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
	//}
	// player
	else if (std::string_view{ pClassInfo->GetName() }.find("C_CSPlayerPawn") != std::string_view::npos && chams::player)
	{
		if (chams::invisible)
		{
			if (chams::invisible_value > 30) {

				arrMeshDraw->m_material = invisible;

				uint8_t new_color = chams::invisible_value;

				arrMeshDraw->m_color().c = new_color;
				arrMeshDraw->m_color().m = new_color;
				arrMeshDraw->m_color().y = new_color;
				arrMeshDraw->m_color().k = 255;
			}
			else {
				arrMeshDraw->m_material = invisible_black;

				uint8_t new_color = chams::invisible_value;

				arrMeshDraw->m_color().c = new_color;
				arrMeshDraw->m_color().m = new_color;
				arrMeshDraw->m_color().y = new_color;
				arrMeshDraw->m_color().k = 255;
			}

			g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
		}

		// bloom
		if (chams::bloom)
		{
			arrMeshDraw->m_material = visible_bloom;

			uint8_t new_color = chams::bloom_value;

			arrMeshDraw->m_color().c = new_color;
			arrMeshDraw->m_color().m = new_color;
			arrMeshDraw->m_color().y = new_color;
			arrMeshDraw->m_color().k = 255;

			//arrMeshDraw->m_color = color(1.f, 1.f, chams::bloom_value / 255.f, 0.100000);

			g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
		}
		else // visible
		{
			if (chams::visible_value > 30) {

				arrMeshDraw->m_material = visible;

				uint8_t new_color = chams::visible_value;

				arrMeshDraw->m_object_info();

				arrMeshDraw->m_color().c = new_color;
				arrMeshDraw->m_color().m = new_color;
				arrMeshDraw->m_color().y = new_color;
				arrMeshDraw->m_color().k = 255;
			}
			else {
				arrMeshDraw->m_material = visible_black;

				uint8_t new_color = chams::visible_value;

				arrMeshDraw->m_object_info();

				arrMeshDraw->m_color().c = new_color;
				arrMeshDraw->m_color().m = new_color;
				arrMeshDraw->m_color().y = new_color;
				arrMeshDraw->m_color().k = 255;
			}

			g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
		}
	}
	// hands
	//else if (std::string_view{ pClassInfo->GetName() }.find("C_ViewmodelAttachmentModel") != std::string_view::npos && chams::hands)
	//{
	//	arrMeshDraw->m_material = visible;

	//	uint8_t new_color = chams::hands_value;

	//	arrMeshDraw->m_color().c = new_color;
	//	arrMeshDraw->m_color().m = new_color;
	//	arrMeshDraw->m_color().y = new_color;
	//	arrMeshDraw->m_color().k = 255;


	//	g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
	//}

	return g_DrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
}

static CHook<void* __fastcall(CGlowProperty*)> g_Glow;
void* __fastcall hkGlow(CGlowProperty* GlowProperty)
{
	if (!other::glow)
		return g_Glow(GlowProperty);

	if (!GlowProperty)
		return g_Glow(GlowProperty);

	if (!GlowProperty->m_hOwner)
		return g_Glow(GlowProperty);

	CEntityInstance* owner = GlowProperty->m_hOwner;
	if (!owner)
		return g_Glow(GlowProperty);

	auto PlayerPawn = interfaces::pGameResourceService->GetGameEntitySystem()->GetBaseEntity<C_CSPlayerPawn>(owner->GetRefEHandle().GetEntryIndex());

	if (!PlayerPawn || PlayerPawn->m_iHealth() < 1)
		return g_Glow(GlowProperty);

	CCSPlayerController* LocalController = CGameEntitySystem::Get().GetLocalPlayerController();
	if (!LocalController)
		return g_Glow(GlowProperty);

	C_CSPlayerPawn* LocalPawn = LocalController->m_hPawn().Get<C_CSPlayerPawn>();
	if (!LocalPawn)
		return g_Glow(GlowProperty);

	if ((LocalPawn == PlayerPawn) || (LocalPawn->m_iTeamNum() == PlayerPawn->m_iTeamNum()))
		return g_Glow(GlowProperty);

	Color_t glowCol = Color_t(other::intesivity, other::intesivity, other::intesivity);

	GlowProperty->Glowing() = true;
	GlowProperty->GlowColorOverride() = glowCol;

	return g_Glow(GlowProperty);
}

static CHook<void __fastcall(void*, int)> g_FrameStageNotify;
void __fastcall hkFrameStageNotify(void* rcx, int stage)
{
	switch (stage) {
	case 6:
		knife_changer::Run();
		break;
	}

	//world::SkyBoxChanger();
	//switch (stage) 
	//{
	   // case 6:
		   // break;
	//}

	//if (stage == 6)
	//{
	//    visuals::Render();
	//}

	g_FrameStageNotify(rcx, stage);
}

static CHook<void* __fastcall(void*)>g_3DBox;
void* __fastcall UpdateSky3DParams(void* a1)
{
	if (world::skybox)
		return nullptr;

	return g_3DBox(a1);
}

static CHook<Color_t __fastcall(PVOID, float*)> g_ApplyColorSO;
Color_t ApplyColorToSceneObject(PVOID a1, float* a2)
{
	if (world::nightmode)
		return Color_t(40, 40, 40, 255);

	return g_ApplyColorSO(a1, a2);
}

float g_lighting_color[3] = { 255.f / 255.f, 255.f / 255.f, 255.f / 255.f };
float g_lighting_brightness = 6.f;
static CHook<void __fastcall(__int64, __int64)> g_SetupLight;
void __fastcall setup_lighting(__int64 a1, __int64 a2)
{
	g_SetupLight(a1, a2);

	if (world::nightmode)
	{
		for (int i = 0; i < 3; i++)
			reinterpret_cast<float*>(a1 + 4)[i] = g_lighting_color[i] * g_lighting_brightness;
	}
}

static CHook<PVOID __fastcall(C_EnvSky*)> g_UpdateSkybox;
PVOID UpdateSkybox(C_EnvSky* thisptr)
{
	static uint8_t nOldColor[3] =
	{
		thisptr->m_vTintColor()[0],
		thisptr->m_vTintColor()[1],
		thisptr->m_vTintColor()[2]
	};

	thisptr->m_vTintColor() = world::nightmode ? Color_t(0, 0, 0) : Color_t(nOldColor[0], nOldColor[1], nOldColor[2]);
	thisptr->m_flBrightnessScale() = 0.f;
	thisptr->PostDataUpdate(1);

	return g_UpdateSkybox(thisptr);
}

void HookGameFunctions()
{
	g_mouseInputEnabled.Hook(memory::fnMouseInputEnabled, HOOK_FUNCTION(hkMouseInputEnabled));
	oIsRelativeMouseMode.HookVirtual(interfaces::pInputSystem, 76, HOOK_FUNCTION(IsRelativeMouseMode));
	// g_onAddEntity.HookVirtual(CGameEntitySystem::GetInstance(), 15, HOOK_FUNCTION(hkOnAddEntity));
   //  g_onRemoveEntity.HookVirtual(CGameEntitySystem::GetInstance(), 16, HOOK_FUNCTION(hkOnRemoveEntity));
	g_getMatricesForView.Hook(memory::fnGetMatricesForView, HOOK_FUNCTION(hkGetMatricesForView));
	g_CreateMove.Hook(memory::fnCreatMove, HOOK_FUNCTION(hkCreateMove));
	g_DrawObject.Hook(fnDrawObject, HOOK_FUNCTION(hkDrawObject));
	g_FrameStageNotify.Hook(memory::fnFrameStage, HOOK_FUNCTION(hkFrameStageNotify));
	//g_3DBox.Hook(memory::hkUpdateSky3DParams, HOOK_FUNCTION(UpdateSky3DParams));
	g_ApplyColorSO.Hook(memory::hkApplyColorToSceneObject, HOOK_FUNCTION(ApplyColorToSceneObject));
	g_SetupLight.Hook(memory::hksetup_lighting, HOOK_FUNCTION(setup_lighting));
	g_UpdateSkybox.Hook(memory::UpdateSkyboxColor, HOOK_FUNCTION(UpdateSkybox));
	g_Glow.Hook(memory::GlowFunction, HOOK_FUNCTION(hkGlow));

	visuals::CacheCurrentEntities();
}
