using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using EFT.UI;
using stupid.solutions.Utils;
using UnityEngine;

namespace stupid.solutions.Features.ESP;

public class TracerESP : MonoBehaviour
{
	private class HitMarker
	{
		public Vector3 position;

		public float displayTime;

		public float damage;

		public Vector3 floatingOffset;

		public bool isHeadshot;

		public int playerId;

		public HitMarker(Vector3 position, float displayTime, float damage, bool isHeadshot, int playerId)
		{
			this.position = position;
			this.displayTime = displayTime;
			this.damage = damage;
			floatingOffset = Vector3.zero;
			this.isHeadshot = isHeadshot;
			this.playerId = playerId;
		}
	}

	private class Tracer
	{
		public List<Vector3> positions = new List<Vector3>();

		public int fireIndex;

		public int fragmentIndex;

		public int randomSeed;

		public float startTime;

		public bool hitTarget;

		public bool hitMarkerDrawn;

		public Vector3? startPosition;

		public bool startUsed;

		public Tracer(int fireIndex, int fragmentIndex, int randomSeed, Vector3? startPosition = null)
		{
			this.fireIndex = fireIndex;
			this.fragmentIndex = fragmentIndex;
			this.randomSeed = randomSeed;
			this.startPosition = startPosition;
			startUsed = false;
			startTime = Time.time;
			hitTarget = false;
			hitMarkerDrawn = false;
		}

		public void AddPosition(Vector3 position)
		{
			if (!hitTarget)
			{
				positions.Add(position);
			}
		}
	}

	public bool hitmarkerloaded;

	private static Dictionary<int, Tracer> validProjectiles = new Dictionary<int, Tracer>();

	private static List<HitMarker> activeHitMarkers = new List<HitMarker>();

	private Dictionary<int, bool> playerAliveStatus = new Dictionary<int, bool>();

	private static Dictionary<int, float> damageTimers = new Dictionary<int, float>();

	private HashSet<int> blacklistedSeeds = new HashSet<int>();

	private bool clearalivestatus;

	private float hitMarkerDuration = 1f;

	private static float hitMarkerVolume = 0.2f;

	private float damageAccumulationTime = 0.5f;

	private static Texture2D hitMarkerTexture;

	private bool isReactionOnShotHooked;

	public static TestHook ReactionOnShotHook;

	private static MethodInfo originalMethod;

	public void Start()
	{
		ConsoleScreen.Log("Init GL material....");
		GameUtils.InitMaterial();
	}

	public void Update()
	{
		if (!Settings.tracers && !Settings.HitMarkers)
		{
			return;
		}
		if (Main.GameWorld == null && clearalivestatus)
		{
			playerAliveStatus.Clear();
			clearalivestatus = false;
		}
		else if (Main.GameWorld != null && !clearalivestatus)
		{
			clearalivestatus = true;
		}
		if (Main.GameWorld == null)
		{
			return;
		}
		if (!hitmarkerloaded && Main.bundle != null)
		{
			hitMarkerTexture = Main.bundle.LoadAsset<Texture2D>("hitmarkerImage");
			hitmarkerloaded = hitMarkerTexture != null;
			ConsoleScreen.Log((hitMarkerTexture != null) ? "Hitmarker image loaded successfully!" : "Failed to load hitmarker image!");
		}
		if (hitMarkerTexture == null)
		{
			return;
		}
		if (Main.OnlineGamePlayers.Count > 0)
		{
			if (!isReactionOnShotHooked && Settings.HitMarkers)
			{
				isReactionOnShotHooked = true;
			}
			_F163 obj = Main.GameWorld?.ClientBallisticCalculator;
			int activeShotsCount = obj.ActiveShotsCount;
			if (obj == null)
			{
				return;
			}
			for (int i = 0; i < activeShotsCount; i++)
			{
				_F16A activeShot = obj.GetActiveShot(i);
				if (activeShot == null)
				{
					continue;
				}
				if (activeShot.PlayerProfileID == Main.LocalPlayer.ProfileId && !validProjectiles.ContainsKey(activeShot.RandomSeed))
				{
					Tracer value = new Tracer(activeShot.FireIndex, activeShot.FragmentIndex, activeShot.RandomSeed, activeShot.StartPosition);
					validProjectiles[activeShot.RandomSeed] = value;
				}
				if (validProjectiles.ContainsKey(activeShot.RandomSeed))
				{
					Tracer tracer = validProjectiles[activeShot.RandomSeed];
					if (tracer.startPosition.HasValue && !tracer.startUsed)
					{
						tracer.positions.Add(tracer.startPosition.Value);
						tracer.startUsed = true;
					}
					tracer.AddPosition(activeShot.CurrentPosition);
				}
			}
			activeHitMarkers.RemoveAll((HitMarker marker) => Time.time - marker.displayTime > hitMarkerDuration);
			validProjectiles = validProjectiles.Where((KeyValuePair<int, Tracer> keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Key, (KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Value);
			return;
		}
		_F163 obj2 = Main.GameWorld?.SharedBallisticsCalculator;
		int activeShotsCount2 = obj2.ActiveShotsCount;
		if (obj2 == null)
		{
			return;
		}
		for (int num = 0; num < activeShotsCount2; num++)
		{
			_F16A activeShot2 = obj2.GetActiveShot(num);
			if (activeShot2 == null)
			{
				continue;
			}
			if (activeShot2.PlayerProfileID == Main.LocalPlayer.ProfileId && !validProjectiles.ContainsKey(activeShot2.RandomSeed))
			{
				Tracer value2 = new Tracer(activeShot2.FireIndex, activeShot2.FragmentIndex, activeShot2.RandomSeed, activeShot2.StartPosition);
				validProjectiles[activeShot2.RandomSeed] = value2;
			}
			if (!validProjectiles.ContainsKey(activeShot2.RandomSeed))
			{
				continue;
			}
			Tracer tracer2 = validProjectiles[activeShot2.RandomSeed];
			if (tracer2.startPosition.HasValue && !tracer2.startUsed)
			{
				tracer2.positions.Add(tracer2.startPosition.Value);
				tracer2.startUsed = true;
			}
			tracer2.AddPosition(activeShot2.CurrentPosition);
			if (Main.OnlineGamePlayers.Count >= 1 || !activeShot2.HasAchievedTarget || tracer2.hitMarkerDrawn)
			{
				continue;
			}
			tracer2.hitTarget = true;
			tracer2.hitMarkerDrawn = true;
			if (!(activeShot2.HittedBallisticCollider is BodyPartCollider { Player: var player } bodyPartCollider))
			{
				continue;
			}
			bool flag = bodyPartCollider.BodyPartType == EBodyPart.Head;
			if (player == null)
			{
				continue;
			}
			int playerId = player.ProfileId.GetHashCode();
			if (playerAliveStatus.ContainsKey(playerId) && !playerAliveStatus[playerId])
			{
				continue;
			}
			float current = player.HealthController.GetBodyPartHealth(EBodyPart.Common).Current;
			float num2 = player.HealthController.GetBodyPartHealth(EBodyPart.Common).Maximum - current;
			if (damageTimers.ContainsKey(playerId) && Time.time - damageTimers[playerId] < damageAccumulationTime)
			{
				HitMarker hitMarker = activeHitMarkers.FirstOrDefault((HitMarker m) => m.playerId == playerId);
				if (hitMarker != null)
				{
					hitMarker.damage += num2;
					hitMarker.displayTime = Time.time;
				}
			}
			else
			{
				activeHitMarkers.Add(new HitMarker(activeShot2.HitPoint, Time.time, num2, flag, playerId));
				damageTimers[playerId] = Time.time;
			}
			if (Settings.HitMarkers)
			{
				if (flag && Main.headshotsound != null)
				{
					Main.audioSourcehit.clip = Main.headshotsound;
					Main.audioSourcehit.volume = hitMarkerVolume;
					Main.audioSourcehit.Play();
				}
				else if (Main.hitMarkerSound != null)
				{
					Main.audioSourcehit.clip = Main.hitMarkerSound;
					Main.audioSourcehit.volume = hitMarkerVolume;
					Main.audioSourcehit.Play();
				}
			}
			bool isAlive = player.HealthController.IsAlive;
			playerAliveStatus[playerId] = isAlive;
			if (!isAlive)
			{
				damageTimers.Remove(playerId);
			}
		}
		activeHitMarkers.RemoveAll((HitMarker marker) => Time.time - marker.displayTime > hitMarkerDuration);
		validProjectiles = validProjectiles.Where((KeyValuePair<int, Tracer> keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Key, (KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Value);
	}

	private static void ProcessOnlineHitMarker(Vector3 hitPosition, bool isHeadshot)
	{
		Vector3 position = GameUtils.WorldPointToScreenPoint(hitPosition);
		ConsoleScreen.Log($"Drawing Hitmarker at : {hitPosition} Headshot ? {isHeadshot}");
		if (position.z > 0f)
		{
			DrawHitMarker(position, 32f, isHeadshot);
		}
	}

	private void Tracers()
	{
		if (!Settings.tracers)
		{
			return;
		}
		if (GameUtils.DrawMaterial == null)
		{
			GameUtils.InitMaterial();
		}
		Camera mainCamera = Main.MainCamera;
		if (mainCamera == null)
		{
			return;
		}
		GameUtils.DrawMaterial.SetPass(0);
		GL.PushMatrix();
		GL.LoadProjectionMatrix(mainCamera.projectionMatrix);
		GL.modelview = mainCamera.worldToCameraMatrix;
		GL.Begin(1);
		foreach (Tracer value in validProjectiles.Values)
		{
			if (value.positions.Count >= 1)
			{
				for (int i = 0; i < value.positions.Count - 1; i++)
				{
					Vector3 v = value.positions[i];
					Vector3 v2 = value.positions[i + 1];
					GL.Color(Settings.TracerColor);
					GL.Vertex(v);
					GL.Vertex(v2);
				}
			}
		}
		GL.End();
		GL.PopMatrix();
	}

	private void OnGUI()
	{
		Tracers();
		if (!Settings.HitMarkers)
		{
			return;
		}
		foreach (HitMarker activeHitMarker in activeHitMarkers)
		{
			Vector3 vector = GameUtils.WorldPointToScreenPoint(activeHitMarker.position);
			if (vector.z > 0f && vector.x >= 0f && vector.x <= (float)Screen.width && vector.y >= 0f && vector.y <= (float)Screen.height)
			{
				DrawDamageNumbers(activeHitMarker, vector);
				DrawHitMarker(vector, 32f, activeHitMarker.isHeadshot);
			}
		}
	}

	private void DrawDamageNumbers(HitMarker hitMarker, Vector3 screenPosition)
	{
		hitMarker.floatingOffset.y += Time.deltaTime * 20f;
		Vector3 vector = screenPosition + hitMarker.floatingOffset;
		vector.y = (float)Screen.height - vector.y;
		if (hitMarker.isHeadshot)
		{
			string text = "HEADSHOT";
			GUI.color = new Color(1f, 0f, 0f, 1f);
			GUI.Label(new Rect(vector.x - 10f, vector.y - 40f, 100f, 20f), text);
		}
		string text2 = "";
		if (Main.GamePlayers.Count > 0)
		{
			text2 = "- " + hitMarker.damage.ToString("F0") + " hp ";
		}
		GUI.color = Settings.HitmarkerHeadShotColor;
		GUI.Label(new Rect(vector.x - 10f, vector.y - 20f, 100f, 20f), text2);
		GUI.color = Color.white;
	}

	private static void DrawHitMarker(Vector3 position, float size, bool isHeadshot)
	{
		if (!(hitMarkerTexture == null))
		{
			GUI.color = (isHeadshot ? Settings.HitmarkerHeadShotColor : Settings.HitmarkerHitColor);
			GUI.DrawTexture(new Rect(position.x - size / 2f, position.y - size / 2f, size, size), hitMarkerTexture);
			GUI.color = Color.white;
		}
	}
}
