using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.Command;
using Dalamud.Game.NativeWrapper;
using Dalamud.Hooking;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using InteropGenerator.Runtime;

namespace WorldMapFixer;

public sealed class Plugin : IDalamudPlugin, IDisposable
{
	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
	public delegate void FireCallbackDelegate(nint atkUnitBase, uint valueCount, nint values, bool close);

	public delegate void AreaMapReceiveEventDelegate(long p1, long p2, uint p3, long p4, nint p5);

	private const string commandName = "/wme";

	public bool alertedDebugWindow;

	private bool areaMapVisible;

	private IKeyState keyState;

	private nint fireCallbackAddress;

	private FireCallbackDelegate fireCallback;

	public string Name => "World Map Enhancer";

	private IDalamudPluginInterface PluginInterface { get; init; }

	private ICommandManager CommandManager { get; init; }

	private Configuration Configuration { get; init; }

	private PluginUI PluginUi { get; init; }

	public Hook<AreaMapReceiveEventDelegate> MouseDelegateHook { get; private set; }

	public long rcStartTime { get; private set; }

	public Plugin(IDalamudPluginInterface pluginInterface)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Expected O, but got Unknown
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Expected O, but got Unknown
		DalamudApi.Initialize(pluginInterface);
		PluginInterface = pluginInterface;
		CommandManager = DalamudApi.CommandManager ?? throw new ArgumentNullException("CommandManager");
		pluginInterface.Create<Service>(Array.Empty<object>());
		DalamudApi.Framework.Update += new OnUpdateDelegate(Framework_UpdateNew);
		Configuration = (PluginInterface.GetPluginConfig() as Configuration) ?? new Configuration();
		Configuration.Initialize(PluginInterface);
		PluginUi = new PluginUI(Configuration);
		keyState = DalamudApi.KeyState;
		CommandManager.AddHandler("/wme", new CommandInfo(new HandlerDelegate(OnCommand))
		{
			HelpMessage = "Configuration for the right click behaviour"
		});
		PluginInterface.UiBuilder.Draw += DrawUI;
		PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI;
		string text = "E8 ?? ?? ?? ?? 0F B6 E8 8B 44 24 20";
		fireCallbackAddress = DalamudApi.SigScanner.ScanText(text);
		fireCallback = (FireCallbackDelegate)Marshal.GetDelegateForFunctionPointer(fireCallbackAddress, typeof(FireCallbackDelegate));
		InstallMouseHook();
	}

	private void Framework_UpdateNew(IFramework framework)
	{
		if (Configuration != null && Configuration.Enabled && DalamudApi.ClientState != null && DalamudApi.ClientState.IsLoggedIn)
		{
			CheckForMap();
		}
	}

	private void InstallMouseHook()
	{
		string text = "40 55 56 57 48 8B EC 48 83 EC 70 0F B7 C2";
		nint num = DalamudApi.SigScanner.ScanText(text);
		if (num != IntPtr.Zero)
		{
			DalamudApi.PluginLog.Debug("Hooking!", Array.Empty<object>());
			MouseDelegateHook = DalamudApi.Hooks.HookFromAddress<AreaMapReceiveEventDelegate>((IntPtr)num, (AreaMapReceiveEventDelegate)Detour, (HookBackend)0);
			MouseDelegateHook?.Enable();
		}
	}

	private void Detour(long p1, long p2, uint p3, long p4, nint p5)
	{
		Marshal.ReadIntPtr(p5);
		byte b = Marshal.ReadByte(p5, 6);
		byte b2 = Marshal.ReadByte(p5, 7);
		_ = DalamudApi.KeyState[(VirtualKey)162];
		if (p2 == 4 && p3 == 16 && b == 1 && b2 != 1)
		{
			ZoomOutMap();
		}
		MouseDelegateHook.Original(p1, p2, p3, p4, p5);
	}

	public unsafe Vector4 GetMapParams()
	{
		if (areaMapVisible)
		{
			AtkUnitBase* areaMapPointer = GetAreaMapPointer();
			return new Vector4(((AtkResNode)((AtkUnitBase)areaMapPointer).RootNode).X, ((AtkResNode)((AtkUnitBase)areaMapPointer).RootNode).Y, (int)((AtkResNode)((AtkUnitBase)areaMapPointer).RootNode).GetWidth(), (int)((AtkResNode)((AtkUnitBase)areaMapPointer).RootNode).GetHeight());
		}
		return new Vector4(float.MinValue, float.MinValue, float.MinValue, float.MinValue);
	}

	public unsafe static AtkValue* CreateAtkValueArray(params object[] values)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		AtkValue* ptr = (AtkValue*)Marshal.AllocHGlobal(values.Length * Unsafe.SizeOf<AtkValue>());
		if (ptr == null)
		{
			return null;
		}
		try
		{
			for (int i = 0; i < values.Length; i++)
			{
				object obj = values[i];
				if (!(obj is uint uInt))
				{
					if (!(obj is int num))
					{
						if (!(obj is float num2))
						{
							if (!(obj is bool flag))
							{
								if (!(obj is string s))
								{
									throw new ArgumentException($"Unable to convert type {obj.GetType()} to AtkValue");
								}
								Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type, (ValueType)8);
								byte[] bytes = Encoding.UTF8.GetBytes(s);
								nint num3 = Marshal.AllocHGlobal(bytes.Length + 1);
								Marshal.Copy(bytes, 0, num3, bytes.Length);
								Marshal.WriteByte(num3, bytes.Length, 0);
								Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).String, CStringPointer.op_Implicit((byte*)num3));
							}
							else
							{
								Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type, (ValueType)2);
								((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Byte = (flag ? ((byte)1) : ((byte)0));
							}
						}
						else
						{
							Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type, (ValueType)7);
							((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Float = num2;
						}
					}
					else
					{
						Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type, (ValueType)3);
						((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Int = num;
					}
				}
				else
				{
					Unsafe.Write(&((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type, (ValueType)5);
					((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).UInt = uInt;
				}
			}
			return ptr;
		}
		catch
		{
			return null;
		}
	}

	public unsafe static bool GetUnitBase(string name, out AtkUnitBase* unitBase, int index = 1)
	{
		unitBase = GetUnitBase(name, index);
		return unitBase != null;
	}

	public unsafe static AtkUnitBase* GetUnitBase(string name, int index = 1)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		return (AtkUnitBase*)Service.GameGui.GetAddonByName(name, index).Address;
	}

	public unsafe static void GenerateCallback(AtkUnitBase* unitBase, params object[] values)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Invalid comparison between Unknown and I4
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		AtkValue* ptr = CreateAtkValueArray(values);
		if (ptr == null)
		{
			return;
		}
		try
		{
			((AtkUnitBase)unitBase).FireCallback((uint)values.Length, ptr, false);
		}
		finally
		{
			for (int i = 0; i < values.Length; i++)
			{
				if ((int)((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).Type == 8)
				{
					Marshal.FreeHGlobal(new IntPtr(CStringPointer.op_Implicit(((AtkValue)((byte*)ptr + (nint)i * (nint)Unsafe.SizeOf<AtkValue>())).String)));
				}
			}
			Marshal.FreeHGlobal(new IntPtr(ptr));
		}
	}

	private unsafe AtkUnitBase* GetAreaMapPointer()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		AtkUnitBasePtr addonByName = DalamudApi.GameGui.GetAddonByName("AreaMap", 1);
		if (addonByName.Address != (IntPtr)IntPtr.Zero)
		{
			AtkUnitBase* address = (AtkUnitBase*)addonByName.Address;
			if (((AtkUnitBase)address).RootNode != null)
			{
				DalamudApi.PluginLog.Debug("PTR: " + (long)((AtkUnitBase)address).RootNode, Array.Empty<object>());
				return address;
			}
			DalamudApi.PluginLog.Debug("Null pointer.", Array.Empty<object>());
			return null;
		}
		DalamudApi.PluginLog.Debug("Null pointer.", Array.Empty<object>());
		return null;
	}

	private unsafe void CheckForMap()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		AtkUnitBasePtr addonByName = DalamudApi.GameGui.GetAddonByName("AreaMap", 1);
		if (addonByName.Address == (IntPtr)IntPtr.Zero)
		{
			return;
		}
		AtkUnitBase* address = (AtkUnitBase*)addonByName.Address;
		if (((AtkUnitBase)address).RootNode == null)
		{
			return;
		}
		if (((AtkUnitBase)address).IsVisible)
		{
			if (!areaMapVisible)
			{
				areaMapVisible = true;
			}
		}
		else if (areaMapVisible)
		{
			areaMapVisible = false;
		}
	}

	private void Framework_Update()
	{
		if (Configuration.Enabled && DalamudApi.ClientState.IsLoggedIn)
		{
			CheckForMap();
		}
	}

	public void Dispose()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		PluginUi.Dispose();
		CommandManager.RemoveHandler("/wme");
		DalamudApi.Framework.Update -= new OnUpdateDelegate(Framework_UpdateNew);
		MouseDelegateHook.Dispose();
	}

	private void OnCommand(string command, string args)
	{
		PluginUi.SettingsVisible = true;
	}

	private void DrawUI()
	{
		PluginUi.Draw();
	}

	private unsafe void ZoomOutMap()
	{
		AtkUnitBase* unitBase = GetUnitBase("AreaMap");
		if (unitBase == null)
		{
			DalamudApi.PluginLog.Debug("Null pointer to areamap. Going away", Array.Empty<object>());
			return;
		}
		uint valueCount = 1u;
		AtkValue* values = CreateAtkValueArray(5);
		fireCallback((nint)unitBase, valueCount, (nint)values, close: false);
	}

	private void DrawConfigUI()
	{
		PluginUi.SettingsVisible = true;
	}
}
