using HarmonyLib;
using QSB.Patches;
using UnityEngine;

namespace QSB.DeathSync.Patches
{
	[HarmonyPatch]
	internal class MapPatches : QSBPatch
	{
		public override QSBPatchTypes Type => QSBPatchTypes.RespawnTime;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(MapController), nameof(MapController.EnterMapView))]
		public static bool MapController_EnterMapView(
			MapController __instance,
			ref bool ____isMapMode,
			OWAudioSource ____audioSource,
			MapMarkerManager ____mapMarkerManager,
			OWCamera ____mapCamera,
			OWCamera ____activeCam,
			MeshRenderer ____gridRenderer,
			ref Transform ____targetTransform,
			ref bool ____lockedToTargetTransform,
			ref Vector3 ____position,
			ref float ____yaw,
			ref float ____pitch,
			ref float ____zoom,
			ref float ____targetZoom,
			ref bool ____interpPosition,
			ref bool ____interpPitch,
			ref bool ____interpZoom,
			ref bool ____framingPlayer,
			ref float ____lockTimer,
			float ____defaultYawAngle,
			float ____initialPitchAngle,
			float ____initialZoomDist,
			float ____defaultZoomDist,
			float ____lockOnMoveLength,
			ref float ____gridOverrideSize,
			ref bool ____gridOverride,
			ref float ____gridTimer,
			ref float ____revealLength,
			ReferenceFrame ____currentRFrame,
			float ____gridLockOnLength,
			ref float ____revealTimer
			)
		{
			if (____isMapMode)
			{
				return false;
			}

			____mapMarkerManager.SetVisible(true);
			GlobalMessenger.FireEvent("EnterMapView");
			GlobalMessenger<OWCamera>.FireEvent("SwitchActiveCamera", ____mapCamera);
			if (____audioSource.isPlaying)
			{
				____audioSource.Stop();
				____audioSource.SetLocalVolume(1f);
				____audioSource.Play();
			}
			else
			{
				____audioSource.SetLocalVolume(1f);
				____audioSource.Play();
			}

			Locator.GetAudioMixer().MixMap();
			____activeCam.enabled = false;
			____mapCamera.enabled = true;
			____gridRenderer.enabled = false;
			____targetTransform = null;
			____lockedToTargetTransform = false;
			____position = RespawnOnDeath.Instance.DeathPositionWorld - Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();
			____position.y = 0f;
			____yaw = ____defaultYawAngle;
			____pitch = ____initialPitchAngle;
			____zoom = ____initialZoomDist;
			____targetZoom = ____defaultZoomDist;
			__instance.transform.rotation = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, RespawnOnDeath.Instance.DeathPlayerForwardVector);
			__instance.transform.position = RespawnOnDeath.Instance.DeathPositionWorld;
			____interpPosition = true;
			____interpPitch = true;
			____interpZoom = true;
			____framingPlayer = ____lockedToTargetTransform;
			____lockTimer = ____lockOnMoveLength;
			____gridOverrideSize = (____currentRFrame == null) ? 0f : ____currentRFrame.GetAutopilotArrivalDistance();
			____gridOverride = ____gridOverrideSize > 0f;
			____gridTimer = (!____gridOverride) ? 0f : ____gridLockOnLength;
			____revealLength = 20f;
			____revealTimer = 0f;
			____isMapMode = true;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(MapController), nameof(MapController.LateUpdate))]
		public static bool MapController_LateUpdate(
			MapController __instance,
			ref float ____observatoryRevealTwist,
			ref float ____defaultPitchAngle,
			ref float ____initialPitchAngle,
			OWCamera ____mapCamera,
			ref float ____lockTimer,
			ref float ____revealTimer,
			float ____lockOnMoveLength,
			float ____revealLength,
			ref bool ____screenPromptsVisible,
			bool ____isPaused,
			ScreenPrompt ____closePrompt,
			ScreenPrompt ____panPrompt,
			ScreenPrompt ____rotatePrompt,
			ScreenPrompt ____zoomPrompt,
			ref bool ____lockedToTargetTransform,
			ref bool ____interpPosition,
			ref bool ____interpPitch,
			ref bool ____interpZoom,
			OWCamera ____activeCam,
			ref Vector3 ____position,
			float ____panSpeed,
			ref float ____zoom,
			float ____maxPanDistance,
			float ____yawSpeed,
			ref float ____yaw,
			float ____pitchSpeed,
			ref float ____pitch,
			float ____minPitchAngle,
			float ____maxPitchAngle,
			ref float ____targetZoom,
			float ____minZoomDistance,
			float ____maxZoomDistance,
			float ____initialZoomDist,
			float ____zoomSpeed,
			float ____observatoryRevealDist
			)
		{
			____lockTimer = Mathf.Min(____lockTimer + Time.deltaTime, ____lockOnMoveLength);
			____revealTimer = Mathf.Min(____revealTimer + Time.deltaTime, ____revealLength);

			var revealFraction = Mathf.Clamp01(____revealTimer / ____revealLength);
			var smoothedRevealFraction = Mathf.SmoothStep(0f, 1f, revealFraction);

			var canInteractWith = ____revealTimer > 18f;

			if (____screenPromptsVisible && ____isPaused)
			{
				____closePrompt.SetVisibility(false);
				____panPrompt.SetVisibility(false);
				____rotatePrompt.SetVisibility(false);
				____zoomPrompt.SetVisibility(false);
				____screenPromptsVisible = false;
			}
			else if (!____screenPromptsVisible && canInteractWith && !____isPaused)
			{
				____closePrompt.SetVisibility(false);
				____panPrompt.SetVisibility(true);
				____rotatePrompt.SetVisibility(true);
				____zoomPrompt.SetVisibility(true);
				____screenPromptsVisible = true;
			}

			var XZinput = Vector2.zero;
			var lookInput = Vector2.zero;
			var zoomInput = 0f;
			if (canInteractWith)
			{
				XZinput = OWInput.GetAxisValue(InputLibrary.moveXZ, InputMode.All);
				lookInput = InputLibrary.look.GetAxisValue(false);
				zoomInput = OWInput.GetValue(InputLibrary.mapZoomIn, InputMode.All) - OWInput.GetValue(InputLibrary.mapZoomOut, InputMode.All);
				lookInput.y *= -1f;
				zoomInput *= -1f;
			}

			____lockedToTargetTransform &= XZinput.sqrMagnitude < 0.01f;
			____interpPosition &= XZinput.sqrMagnitude < 0.01f;
			____interpPitch &= Mathf.Abs(lookInput.y) < 0.1f;
			____interpZoom &= Mathf.Abs(zoomInput) < 0.1f;

			if (____interpPosition)
			{
				var a = ____activeCam.transform.position - Locator.GetCenterOfTheUniverse().GetOffsetPosition();
				var b = Vector3.zero;
				____position = Vector3.Lerp(a, b, smoothedRevealFraction);
			}
			else
			{
				var normalized = Vector3.Scale(__instance.transform.forward + __instance.transform.up, new Vector3(1f, 0f, 1f)).normalized;
				var a2 = (__instance.transform.right * XZinput.x) + (normalized * XZinput.y);
				____position += a2 * ____panSpeed * ____zoom * Time.deltaTime;
				____position.y = 0f;
				if (____position.sqrMagnitude > ____maxPanDistance * ____maxPanDistance)
				{
					____position = ____position.normalized * ____maxPanDistance;
				}
			}

			____yaw += lookInput.x * ____yawSpeed * Time.deltaTime;
			____yaw = OWMath.WrapAngle(____yaw);
			if (____interpPitch)
			{
				____pitch = Mathf.Lerp(____initialPitchAngle, ____defaultPitchAngle, smoothedRevealFraction);
			}
			else
			{
				____pitch += lookInput.y * ____pitchSpeed * Time.deltaTime;
				____pitch = Mathf.Clamp(____pitch, ____minPitchAngle, ____maxPitchAngle);
			}

			if (____interpZoom)
			{
				____zoom = Mathf.Lerp(____initialZoomDist, ____targetZoom, smoothedRevealFraction);
			}
			else
			{
				____zoom += zoomInput * ____zoomSpeed * Time.deltaTime;
				____zoom = Mathf.Clamp(____zoom, ____minZoomDistance, ____maxZoomDistance);
			}

			____mapCamera.nearClipPlane = Mathf.Lerp(0.1f, 1f, smoothedRevealFraction);

			var finalRotation = Quaternion.Euler(____pitch, ____yaw, 0f);

			var num4 = revealFraction * (2f - revealFraction);

			var num5 = Mathf.SmoothStep(0f, 1f, num4);

			// Create rotation that's looking down at the player from above
			var lookingDownAtPlayer = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, Vector3.up);

			// Get starting position - distance above player
			var startingPosition = RespawnOnDeath.Instance.DeathPositionWorld;
			startingPosition += RespawnOnDeath.Instance.DeathPlayerUpVector * num5 * ____observatoryRevealDist;

			// Lerp to final rotation
			__instance.transform.rotation = Quaternion.Lerp(lookingDownAtPlayer, finalRotation, num5);

			// Lerp reveal twist
			__instance.transform.rotation *= Quaternion.AngleAxis(Mathf.Lerp(____observatoryRevealTwist, 0f, num4), Vector3.forward);

			var endPosition = ____position + (-__instance.transform.forward * ____zoom) + Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();

			// Lerp to final position
			__instance.transform.position = Vector3.Lerp(startingPosition, endPosition, num5);

			return false;
		}
	}
}
