using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using EFT;
using EFT.UI;
using stupid.solutions.Data;
using stupid.solutions.enums;
using stupid.solutions.Utils;
using UnityEngine;

namespace stupid.solutions.Features.ESP;

public class Crosshairetc : MonoBehaviour
{
	public float lineLength = 20f;

	public float lineThickness = 2f;

	private Texture2D crosshairTexture;

	public static float skeletonLineThickness = 1.7f;

	public static float headCircleThickness = 1.7f;

	private static Stopwatch memoryCheckTimer = Stopwatch.StartNew();

	private static long lastLoggedMemory = 0L;

	private static int logInterval = 5000;

	private static Dictionary<Player, (Dictionary<HumanBones, Vector3> bones, float lastUpdateTime)> GamePlayerBoneCache = new Dictionary<Player, (Dictionary<HumanBones, Vector3>, float)>();

	private static Dictionary<Player, (Dictionary<ZombieBones, Vector3> bones, float lastUpdateTime)> zombieBoneCache = new Dictionary<Player, (Dictionary<ZombieBones, Vector3>, float)>();

	private bool clearcache;

	private static float cacheExpirationInterval = Settings.cachedelayskelet;

	private Color cachedcrosshaircolor;

	private static readonly List<ZombieBones> NeededZombieBones = new List<ZombieBones>
	{
		ZombieBones.Base_HumanPelvis,
		ZombieBones.Base_HumanLThigh1,
		ZombieBones.Base_HumanLCalf,
		ZombieBones.Base_HumanLFoot,
		ZombieBones.Base_HumanRThigh1,
		ZombieBones.Base_HumanRCalf,
		ZombieBones.Base_HumanRFoot,
		ZombieBones.Base_HumanSpine2,
		ZombieBones.Base_HumanSpine3,
		ZombieBones.Base_HumanNeck,
		ZombieBones.Base_HumanHead,
		ZombieBones.Base_HumanLCollarbone,
		ZombieBones.Base_HumanLUpperarm,
		ZombieBones.Base_HumanLForearm1,
		ZombieBones.Base_HumanLForearm2,
		ZombieBones.Base_HumanLPalm,
		ZombieBones.Base_HumanRCollarbone,
		ZombieBones.Base_HumanRUpperarm,
		ZombieBones.Base_HumanRForearm1,
		ZombieBones.Base_HumanRForearm2,
		ZombieBones.Base_HumanRPalm
	};

	private static readonly List<HumanBones> NeededBones = new List<HumanBones>
	{
		HumanBones.HumanPelvis,
		HumanBones.HumanLThigh1,
		HumanBones.HumanLThigh2,
		HumanBones.HumanLCalf,
		HumanBones.HumanLFoot,
		HumanBones.HumanLToe,
		HumanBones.HumanPelvis,
		HumanBones.HumanRThigh1,
		HumanBones.HumanRThigh2,
		HumanBones.HumanRCalf,
		HumanBones.HumanRFoot,
		HumanBones.HumanRToe,
		HumanBones.HumanSpine1,
		HumanBones.HumanSpine2,
		HumanBones.HumanSpine3,
		HumanBones.HumanNeck,
		HumanBones.HumanHead,
		HumanBones.HumanLCollarbone,
		HumanBones.HumanLForearm1,
		HumanBones.HumanLForearm2,
		HumanBones.HumanLForearm3,
		HumanBones.HumanLPalm,
		HumanBones.HumanLDigit11,
		HumanBones.HumanLDigit12,
		HumanBones.HumanLDigit13,
		HumanBones.HumanRCollarbone,
		HumanBones.HumanRForearm1,
		HumanBones.HumanRForearm2,
		HumanBones.HumanRForearm3,
		HumanBones.HumanRPalm,
		HumanBones.HumanRDigit11,
		HumanBones.HumanRDigit12,
		HumanBones.HumanRDigit13
	};

	private void Update()
	{
		if (Main.GameWorld != null && Main.MainCamera != null)
		{
			clearcache = true;
		}
		if (Main.GameWorld == null && clearcache)
		{
			zombieBoneCache.Clear();
			GamePlayerBoneCache.Clear();
		}
		if (cachedcrosshaircolor != Settings.CrosshairColor)
		{
			ReapplyCrosshairColour();
		}
	}

	private void LogMemoryUsage()
	{
		long totalMemory = GC.GetTotalMemory(forceFullCollection: false);
		long num = totalMemory - lastLoggedMemory;
		ConsoleScreen.Log($"Memory Usage: {totalMemory / 1048576} MB (+{num / 1048576} MB since last log)");
		if (Main.GameWorld != null)
		{
			ConsoleScreen.Log($"Registered Players: {Main.GameWorld.RegisteredPlayers.Count}");
			ConsoleScreen.Log($"Loot Items: {Main.GameWorld.LootItems.Count}");
		}
		lastLoggedMemory = totalMemory;
	}

	private void Start()
	{
		cachedcrosshaircolor = Settings.CrosshairColor;
		crosshairTexture = new Texture2D(1, 1);
		crosshairTexture.SetPixel(0, 0, Settings.CrosshairColor);
		crosshairTexture.Apply();
	}

	public void ReapplyCrosshairColour()
	{
		cachedcrosshaircolor = Settings.CrosshairColor;
		crosshairTexture = new Texture2D(1, 1);
		crosshairTexture.SetPixel(0, 0, Settings.CrosshairColor);
		crosshairTexture.Apply();
	}

	private static bool IsZombie(Player player)
	{
		WildSpawnType role = player.Profile.Info.Settings.Role;
		if (role != WildSpawnType.infectedAssault && role != WildSpawnType.infectedCivil && role != WildSpawnType.infectedLaborant)
		{
			return role == WildSpawnType.infectedPmc;
		}
		return true;
	}

	public void OnGUI()
	{
		if (Settings.AimbotDrawFOV && Menu2.showmenu)
		{
			Render.DrawCircle(new Vector2(Screen.width / 2, Screen.height / 2), Settings.AimbotFOV * 15.7f, 32, Settings.AimFOVColor);
		}
		if (Settings.Crosshair && Menu2.showmenu)
		{
			float num = Screen.width / 2;
			float num2 = Screen.height / 2;
			GUI.DrawTexture(new Rect(num - lineLength / 2f, num2 - lineThickness / 2f, lineLength, lineThickness), crosshairTexture);
			GUI.DrawTexture(new Rect(num - lineThickness / 2f, num2 - lineLength / 2f, lineThickness, lineLength), crosshairTexture);
		}
		foreach (GamePlayer gamePlayer in Main.GamePlayers)
		{
			if (!gamePlayer.IsOnScreen || gamePlayer.Distance > Settings.DrawPlayersDistance || gamePlayer.Player == Main.LocalPlayer)
			{
				continue;
			}
			if (Settings.DrawPlayers && Settings.EnableSkeleton)
			{
				if (IsZombie(gamePlayer.Player))
				{
					DrawZombies(gamePlayer.Player);
				}
				else
				{
					DrawSkeleton(gamePlayer.Player);
				}
			}
			if (Settings.drawaimpos && Settings.DrawPlayers)
			{
				DrawAim(gamePlayer);
			}
		}
	}

	private static void DrawAim(GamePlayer gamePlayer)
	{
		Vector3 vector = GameUtils.WorldPointToScreenPoint(RayCast.BarrelRayCast(gamePlayer.Player));
		Render.DrawLine(GameUtils.WorldPointToScreenPoint(gamePlayer.Player.Fireport.position), vector, 1f, Color.red);
	}

	private static Dictionary<HumanBones, Vector3> GetBones(Player gamePlayer)
	{
		float time = Time.time;
		if (GamePlayerBoneCache.TryGetValue(gamePlayer, out (Dictionary<HumanBones, Vector3>, float) value) && time - value.Item2 < cacheExpirationInterval)
		{
			return value.Item1;
		}
		Dictionary<HumanBones, Vector3> dictionary = new Dictionary<HumanBones, Vector3>();
		if (gamePlayer.PlayerBody == null || gamePlayer.PlayerBody.SkeletonRootJoint == null)
		{
			return dictionary;
		}
		List<Transform> list = gamePlayer.PlayerBody.SkeletonRootJoint.Bones.Values.ToList();
		for (int i = 0; i < list.Count; i++)
		{
			Transform transform = list[i];
			if (transform != null && NeededBones.Contains((HumanBones)i))
			{
				Vector3 position = transform.position;
				if (GameUtils.IsScreenPointVisible(Main.ActiveCamera.WorldToScreenPoint(position)))
				{
					dictionary[(HumanBones)i] = position;
				}
			}
		}
		GamePlayerBoneCache[gamePlayer] = (dictionary, time);
		return dictionary;
	}

	private static void DrawSkeleton(Player gamePlayer)
	{
		Dictionary<HumanBones, Vector3> bones = GetBones(gamePlayer);
		if (bones.Count != 0)
		{
			GL.PushMatrix();
			GameUtils.DrawMaterial.SetPass(0);
			GL.LoadProjectionMatrix(Main.ActiveCamera.projectionMatrix);
			GL.modelview = Main.ActiveCamera.worldToCameraMatrix;
			GL.Begin(1);
			ConnectBonesGL(bones, HumanBones.HumanPelvis, HumanBones.HumanLThigh1, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLThigh1, HumanBones.HumanLThigh2, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLThigh2, HumanBones.HumanLCalf, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLCalf, HumanBones.HumanLFoot, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLFoot, HumanBones.HumanLToe, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanPelvis, HumanBones.HumanRThigh1, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRThigh1, HumanBones.HumanRThigh2, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRThigh2, HumanBones.HumanRCalf, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRCalf, HumanBones.HumanRFoot, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRFoot, HumanBones.HumanRToe, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanPelvis, HumanBones.HumanSpine1, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanSpine1, HumanBones.HumanSpine2, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanSpine2, HumanBones.HumanSpine3, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanSpine3, HumanBones.HumanNeck, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanNeck, HumanBones.HumanHead, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanSpine3, HumanBones.HumanLCollarbone, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLCollarbone, HumanBones.HumanLForearm1, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLForearm1, HumanBones.HumanLForearm2, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLForearm2, HumanBones.HumanLForearm3, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanLForearm3, HumanBones.HumanLPalm, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanSpine3, HumanBones.HumanRCollarbone, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRCollarbone, HumanBones.HumanRForearm1, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRForearm1, HumanBones.HumanRForearm2, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRForearm2, HumanBones.HumanRForearm3, gamePlayer);
			ConnectBonesGL(bones, HumanBones.HumanRForearm3, HumanBones.HumanRPalm, gamePlayer);
			GL.End();
			GL.PopMatrix();
			if (bones.ContainsKey(HumanBones.HumanHead) && bones.ContainsKey(HumanBones.HumanNeck))
			{
				Vector3 vector = bones[HumanBones.HumanHead];
				Vector3 b = bones[HumanBones.HumanNeck];
				float num = Vector3.Distance(vector, b);
				float num2 = Vector3.Distance(Main.LocalPlayer.Transform.position, gamePlayer.Transform.position);
				float thickness = headCircleThickness / (num2 * 0.1f);
				int segments = ((num2 < 20f) ? 128 : 32);
				DrawHeadCircleGL(vector, num * 1.15f, segments, Settings.SkeletonColor, thickness);
			}
		}
	}

	private static void ConnectBonesGL(Dictionary<HumanBones, Vector3> bones, HumanBones start, HumanBones stop, Player gamePlayer)
	{
		if (bones.ContainsKey(start) && bones.ContainsKey(stop))
		{
			Vector3 v = bones[start];
			Vector3 v2 = bones[stop];
			GL.Color(Settings.SkeletonColor);
			GL.Vertex(v);
			GL.Vertex(v2);
		}
	}

	private static void DrawHeadCircleGL(Vector3 center, float radius, int segments, Color color, float thickness)
	{
		GL.PushMatrix();
		GameUtils.DrawMaterial.SetPass(0);
		GL.LoadProjectionMatrix(Main.ActiveCamera.projectionMatrix);
		GL.modelview = Main.ActiveCamera.worldToCameraMatrix;
		GL.Begin(1);
		GL.Color(color);
		float num = 360f / (float)segments;
		for (int i = 0; i < segments; i++)
		{
			float f = (float)Math.PI / 180f * ((float)i * num);
			float f2 = (float)Math.PI / 180f * ((float)(i + 1) * num);
			Vector3 v = center + new Vector3(Mathf.Cos(f) * radius, Mathf.Sin(f) * radius, 0f);
			Vector3 v2 = center + new Vector3(Mathf.Cos(f2) * radius, Mathf.Sin(f2) * radius, 0f);
			GL.Vertex(v);
			GL.Vertex(v2);
		}
		GL.End();
		GL.PopMatrix();
	}

	private static void ConnectZombieBones(Dictionary<ZombieBones, Vector3> bones, ZombieBones start, ZombieBones stop, Player gamePlayer)
	{
		if (bones.ContainsKey(start) && bones.ContainsKey(stop))
		{
			Vector3 v = bones[start];
			Vector3 v2 = bones[stop];
			GL.Color(Settings.SkeletonColor);
			GL.Vertex(v);
			GL.Vertex(v2);
		}
	}

	public static void DrawZombies(Player gamePlayer)
	{
		Dictionary<ZombieBones, Vector3> zombieBones = GetZombieBones(gamePlayer);
		if (zombieBones.Count != 0)
		{
			GL.PushMatrix();
			GameUtils.DrawMaterial.SetPass(0);
			GL.LoadProjectionMatrix(Main.ActiveCamera.projectionMatrix);
			GL.modelview = Main.ActiveCamera.worldToCameraMatrix;
			GL.Begin(1);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanPelvis, ZombieBones.Base_HumanLThigh1, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanPelvis, ZombieBones.Base_HumanRThigh1, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLThigh1, ZombieBones.Base_HumanLCalf, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLCalf, ZombieBones.Base_HumanLFoot, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRThigh1, ZombieBones.Base_HumanRCalf, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRCalf, ZombieBones.Base_HumanRFoot, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanPelvis, ZombieBones.Base_HumanSpine2, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanSpine2, ZombieBones.Base_HumanSpine3, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanSpine3, ZombieBones.Base_HumanNeck, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanNeck, ZombieBones.Base_HumanHead, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanSpine3, ZombieBones.Base_HumanLCollarbone, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLCollarbone, ZombieBones.Base_HumanLUpperarm, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLUpperarm, ZombieBones.Base_HumanLForearm1, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLForearm1, ZombieBones.Base_HumanLForearm2, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanLForearm2, ZombieBones.Base_HumanLPalm, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanSpine3, ZombieBones.Base_HumanRCollarbone, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRCollarbone, ZombieBones.Base_HumanRUpperarm, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRUpperarm, ZombieBones.Base_HumanRForearm1, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRForearm1, ZombieBones.Base_HumanRForearm2, gamePlayer);
			ConnectZombieBones(zombieBones, ZombieBones.Base_HumanRForearm2, ZombieBones.Base_HumanRPalm, gamePlayer);
			GL.End();
			GL.PopMatrix();
			if (zombieBones.ContainsKey(ZombieBones.Base_HumanHead) && zombieBones.ContainsKey(ZombieBones.Base_HumanNeck))
			{
				Vector3 vector = zombieBones[ZombieBones.Base_HumanHead];
				Vector3 b = zombieBones[ZombieBones.Base_HumanNeck];
				float num = Vector3.Distance(vector, b);
				float num2 = Vector3.Distance(Main.LocalPlayer.Transform.position, gamePlayer.Transform.position);
				float thickness = headCircleThickness / (num2 * 0.1f);
				int segments = ((num2 < 20f) ? 128 : 32);
				DrawHeadCircleGL(vector, num * 1.15f, segments, Settings.SkeletonColor, thickness);
			}
		}
	}

	private static Dictionary<ZombieBones, Vector3> GetZombieBones(Player gamePlayer)
	{
		float time = Time.time;
		if (zombieBoneCache.TryGetValue(gamePlayer, out (Dictionary<ZombieBones, Vector3>, float) value) && time - value.Item2 < cacheExpirationInterval)
		{
			return value.Item1;
		}
		Dictionary<ZombieBones, Vector3> dictionary = new Dictionary<ZombieBones, Vector3>();
		if (gamePlayer.PlayerBody == null || gamePlayer.PlayerBody.SkeletonRootJoint == null)
		{
			return dictionary;
		}
		List<Transform> list = gamePlayer.PlayerBody.SkeletonRootJoint.Bones.Values.ToList();
		for (int i = 0; i < list.Count; i++)
		{
			Transform transform = list[i];
			if (transform != null && NeededZombieBones.Contains((ZombieBones)i))
			{
				Vector3 position = transform.position;
				if (GameUtils.IsScreenPointVisible(Main.ActiveCamera.WorldToScreenPoint(position)))
				{
					dictionary[(ZombieBones)i] = position;
				}
			}
		}
		zombieBoneCache[gamePlayer] = (dictionary, time);
		return dictionary;
	}
}
