using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.NativeWrapper;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;

namespace OmicronMountMusicFixer;

public class Plugin : IDalamudPlugin, IDisposable
{
	private static class Signatures
	{
		internal const string GetSpecialMode = "48 89 5C 24 ?? 57 48 83 EC 20 8B 41 10 33 DB";
	}

	private unsafe delegate int GetSpecialMode(void* unused, byte specialModeType);

	private unsafe delegate void* AgentShow(void* a1);

	private Hook<GetSpecialMode> getSpecialModeHook;

	private bool isPlayingReplacement;

	private const int ResourceDataPointerOffset = 176;

	private const int MusicManagerStreamingOffset = 50;

	private const string commandName = "/omm";

	private HttpClient httpCl;

	public int currentSpecialMode;

	public bool currentCastHandled;

	public bool modEnabled;

	public long enabledTimestamp;

	private bool swapNextSong;

	private List<Payload> songEchoPayload;

	private readonly Dictionary<long, int> mountIDToSongIDMap = new Dictionary<long, int>();

	private bool wasMounted;

	private uint lastMountId;

	private Hook<GetSpecialMode>? GetResourceSyncHook { get; set; }

	public string Name => "Omicron mount fixer";

	private IDalamudPluginInterface PluginInterface { get; init; }

	private ICommandManager CommandManager { get; init; }

	private Configuration Configuration { get; init; }

	private PluginUI PluginUi { get; init; }

	public long rcStartTime { get; private set; }

	private Hook<AgentShow> DutyFinderHook { get; set; }

	public unsafe AtkUnitBase* dutyFinderAtkUnitBase { get; private set; }

	public string DataPath { get; }

	private nint NoSoundPtr { get; }

	private nint InfoPtr { get; }

	public bool WasStreamingEnabled { get; private set; }

	public bool Streaming { get; private set; }

	private ConcurrentDictionary<nint, string> Scds { get; } = new ConcurrentDictionary<nint, string>();

	internal ConcurrentQueue<string> Recent { get; } = new ConcurrentQueue<string>();

	public uint lastSpellBeingCast { get; private set; }

	public long lastSpellCast { get; private set; }

	public Plugin(IDalamudPluginInterface pluginInterface, ICommandManager commandManager)
	{
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Expected O, but got Unknown
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Expected O, but got Unknown
		DalamudApi.Initialize(pluginInterface);
		PluginInterface = pluginInterface;
		CommandManager = commandManager;
		pluginInterface.Create<Service>(Array.Empty<object>());
		Configuration = (PluginInterface.GetPluginConfig() as Configuration) ?? new Configuration();
		Configuration.Initialize(PluginInterface);
		PluginUi = new PluginUI(Configuration, DataPath);
		PluginInterface.UiBuilder.Draw += DrawUI;
		PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI;
		httpCl = new HttpClient();
		BGMAddressResolver.Init();
		BGMManager.OnSongChanged += HandleSongChanged;
		Service.Framework.Update += new OnUpdateDelegate(Framework_Update);
		Service.ClientState.Logout += new LogoutDelegate(ClientState_Logout);
		mountIDToSongIDMap.Add(298L, 929);
		mountIDToSongIDMap.Add(287L, 906);
		mountIDToSongIDMap.Add(343L, 20047);
		mountIDToSongIDMap.Add(331L, 501);
		mountIDToSongIDMap.Add(235L, 796);
	}

	private void ClientState_Logout(int type, int code)
	{
		BGMManager.Play(0);
	}

	private void ClientState_Login()
	{
	}

	private void HandleSongChanged(int oldSong, int newSong, int oldSecondSong, int newSecondSong, bool nocare, bool playedByPlugin)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		DalamudApi.PluginLog.Debug($"HandleSongChanged called: {oldSong} -> {newSong}, playedByPlugin: {playedByPlugin}", Array.Empty<object>());
		if (DalamudApi.ClientState.IsPvP)
		{
			DalamudApi.PluginLog.Debug("In PvP, ignoring song change", Array.Empty<object>());
			return;
		}
		if (playedByPlugin)
		{
			DalamudApi.PluginLog.Debug("Song changed by plugin, ignoring", Array.Empty<object>());
			return;
		}
		if (Service.ObjectTable.LocalPlayer == null)
		{
			DalamudApi.PluginLog.Debug("No mount.", Array.Empty<object>());
			return;
		}
		uint num = (((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.HasValue ? ((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.Value.RowId : 0u);
		DalamudApi.PluginLog.Debug("Mount ID: " + num, Array.Empty<object>());
		byte value = (byte)Marshal.ReadIntPtr((nint)((IGameObject)Service.ObjectTable.LocalPlayer).Address + 9044);
		DalamudApi.PluginLog.Debug($"MountID: {num:X} eventState: {value:X}. Addresses: {(nint)((IGameObject)Service.ObjectTable.LocalPlayer).Address + 1736:X} and {(nint)((IGameObject)Service.ObjectTable.LocalPlayer).Address + 8844:X}", Array.Empty<object>());
		if (((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.HasValue)
		{
			DalamudApi.PluginLog.Debug("We are mounted, lets check if there is a replacement for our song. Current mount: " + num, Array.Empty<object>());
			if (mountIDToSongIDMap.ContainsKey(num))
			{
				DalamudApi.PluginLog.Debug("We do indeed have a replacement for this song. Lets play it then.", Array.Empty<object>());
				PlaySong(mountIDToSongIDMap[num]);
			}
		}
		else if (!((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.HasValue)
		{
			DalamudApi.PluginLog.Debug("We have dismounted. Lets stop music?", Array.Empty<object>());
			StopSong();
		}
	}

	public void StopSong()
	{
		DalamudApi.PluginLog.Debug($"StopSong called - Playing: {BGMManager.PlayingSongId}, Current: {BGMManager.CurrentSongId}", Array.Empty<object>());
		IPluginLog pluginLog = DalamudApi.PluginLog;
		DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(26, 1);
		defaultInterpolatedStringHandler.AppendLiteral("LocalPlayer CastActionId: ");
		IPlayerCharacter localPlayer = Service.ObjectTable.LocalPlayer;
		defaultInterpolatedStringHandler.AppendFormatted((localPlayer != null) ? new uint?(((IBattleChara)localPlayer).CastActionId) : ((uint?)null));
		pluginLog.Debug(defaultInterpolatedStringHandler.ToStringAndClear(), Array.Empty<object>());
		IPlayerCharacter localPlayer2 = Service.ObjectTable.LocalPlayer;
		if (localPlayer2 != null && ((IBattleChara)localPlayer2).CastActionId == 298)
		{
			DalamudApi.PluginLog.Debug($"Song ID {BGMManager.CurrentSongId} has a replacement of {929}...", Array.Empty<object>());
			if (929 != BGMManager.PlayingSongId)
			{
				PlaySong(929, isReplacement: true);
				return;
			}
			DalamudApi.PluginLog.Debug($"But that's the song we're playing [{BGMManager.PlayingSongId}], so let's stop", Array.Empty<object>());
		}
		IPlayerCharacter localPlayer3 = Service.ObjectTable.LocalPlayer;
		if (localPlayer3 != null && ((IBattleChara)localPlayer3).CastActionId == 287)
		{
			DalamudApi.PluginLog.Debug($"Song ID {BGMManager.CurrentSongId} has a replacement of {906}...", Array.Empty<object>());
			if (906 != BGMManager.PlayingSongId)
			{
				PlaySong(906, isReplacement: true);
				return;
			}
			DalamudApi.PluginLog.Debug($"But that's the song we're playing [{BGMManager.PlayingSongId}], so let's stop", Array.Empty<object>());
		}
		DalamudApi.PluginLog.Debug("Calling BGMManager.Play(0) to stop music", Array.Empty<object>());
		BGMManager.Play(0);
	}

	public void PlaySong(int songId, bool isReplacement = false)
	{
		DalamudApi.PluginLog.Debug($"Playing {songId}", Array.Empty<object>());
		isPlayingReplacement = isReplacement;
		BGMManager.Play(songId);
	}

	private unsafe void installSpecialModeHook()
	{
		string text = "48 89 5C 24 ?? 57 48 83 EC 20 8B 41 10 33 DB";
		nint num = DalamudApi.SigScanner.ScanText(text);
		DalamudApi.PluginLog.Debug("MCA: " + (IntPtr)num, Array.Empty<object>());
		getSpecialModeHook = DalamudApi.Hooks.HookFromAddress<GetSpecialMode>((IntPtr)num, (GetSpecialMode)SpecialModeDetour, (HookBackend)0);
		getSpecialModeHook?.Enable();
	}

	private unsafe int SpecialModeDetour(void* unused, byte specialModeType)
	{
		currentSpecialMode = specialModeType;
		return getSpecialModeHook.Original(unused, specialModeType);
	}

	private bool IsLoadingScreen()
	{
		//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_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		AtkUnitBasePtr addonByName = Service.GameGui.GetAddonByName("_LocationTitle", 1);
		AtkUnitBasePtr addonByName2 = Service.GameGui.GetAddonByName("FadeMiddle", 1);
		if (((AtkUnitBasePtr)(ref addonByName)).IsNull || !((AtkUnitBase)(nint)addonByName.Address).IsVisible)
		{
			if (!((AtkUnitBasePtr)(ref addonByName2)).IsNull)
			{
				return ((AtkUnitBase)(nint)addonByName2.Address).IsVisible;
			}
			return false;
		}
		return true;
	}

	private void Framework_Update(IFramework framework)
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		if (Configuration.Enabled && Service.ClientState.IsLoggedIn)
		{
			CheckForCastBar();
		}
		if (IsLoadingScreen())
		{
			return;
		}
		if (Service.ClientState.IsLoggedIn && Service.ObjectTable.LocalPlayer != null)
		{
			bool hasValue = ((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.HasValue;
			uint num = ((hasValue && ((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.HasValue) ? ((ICharacter)Service.ObjectTable.LocalPlayer).CurrentMount.Value.RowId : 0u);
			if (wasMounted && !hasValue)
			{
				DalamudApi.PluginLog.Debug($"Dismounted detected! Was on mount {lastMountId}, now dismounted", Array.Empty<object>());
				if (mountIDToSongIDMap.ContainsKey(lastMountId))
				{
					DalamudApi.PluginLog.Debug("Had replacement music, stopping it now", Array.Empty<object>());
					StopSong();
				}
			}
			wasMounted = hasValue;
			lastMountId = num;
		}
		BGMManager.Update(null);
		songEchoPayload = null;
	}

	private void CheckForCastBar()
	{
		if (!IsLoadingScreen() && Service.ClientState != null && Service.ClientState.IsLoggedIn && !Service.ClientState.IsPvP && ((IBattleChara)Service.ObjectTable.LocalPlayer).IsCasting)
		{
			lastSpellBeingCast = ((IBattleChara)Service.ObjectTable.LocalPlayer).CastActionId;
			lastSpellCast = long.Parse(Convert.ToString((int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds));
		}
	}

	public void Dispose()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		PluginUi.Dispose();
		CommandManager.RemoveHandler("/omm");
		Service.Framework.Update -= new OnUpdateDelegate(Framework_Update);
		Service.ClientState.Login -= ClientState_Login;
		Service.ClientState.Logout -= new LogoutDelegate(ClientState_Logout);
		BGMManager.Play(0);
		BGMManager.Dispose();
	}

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

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

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