﻿using UnityEngine;

namespace AC.CombatExample
{

	public class CustomInventory : MonoBehaviour
	{

		// A 3D Inventory system, where each item has a prefab that is spawned in when the Inventory menu is turned on


		#region Variables

		[SerializeField] private string scrollInputName = "Horizontal";
		[SerializeField] private AudioClip scrollClip = null;
		[SerializeField] private int ammoIDPropertyID = 2;
		[SerializeField] private float separationDistance = 0.3f;
		[SerializeField] private float moveSpeed = 10f;
		[SerializeField] private int combineIconID = 5;
		[SerializeField] private int reloadIconID = 7;
		[SerializeField] private int weaponCategoryID = 1;
		[SerializeField] private int ammoInChamberPropertyID = 3;
		[SerializeField] private int ammoCapacityPropertyID = 0;
		[SerializeField] [Range (0.01f, 1f)] private float scrollInputThreshold = 0.2f;
		[SerializeField] private bool autoShowInteractions = false;

		private bool ignoreInputThisFrame;
		private Menu inventoryMenu;
		private Menu interactionMenu;

		private MenuInventoryBox inventoryBox;
		private InventoryItemSpin[] spawnedItems = new InventoryItemSpin[0];

		private bool isOn;
		private bool released = true;

		#endregion


		#region UnityStandards

		private void OnEnable ()
		{
			EventManager.OnMenuTurnOn += OnMenuTurnOn;
			EventManager.OnMenuTurnOff += OnMenuTurnOff;
			EventManager.OnInventoryInteract_Alt += OnInventoryInteract;
			EventManager.OnInventoryCombine_Alt += OnInventoryCombine;
			EventManager.OnBeforeLoading += OnBeforeLoading;
		}


		private void OnDisable ()
		{
			EventManager.OnMenuTurnOn -= OnMenuTurnOn;
			EventManager.OnMenuTurnOff -= OnMenuTurnOff;
			EventManager.OnInventoryInteract_Alt -= OnInventoryInteract;
			EventManager.OnInventoryCombine_Alt -= OnInventoryCombine;
			EventManager.OnBeforeLoading -= OnBeforeLoading;
		}

		
		private void Start ()
		{
			inventoryMenu = PlayerMenus.GetMenuWithName ("Inventory");
			inventoryBox = inventoryMenu.GetElementWithName ("InventoryBox") as MenuInventoryBox;
			interactionMenu = PlayerMenus.GetMenuWithName ("Interaction");
		}


		private void Update ()
		{
			if (ignoreInputThisFrame)
			{
				ignoreInputThisFrame = false;
				return;
			}

			if (isOn && KickStarter.stateHandler.IsInGameplay ())
			{
				bool canScroll = interactionMenu.IsOff ();
				if (!interactionMenu.IsOff () && (!interactionMenu.CanCurrentlyKeyboardControl (KickStarter.stateHandler.gameState) || KickStarter.menuManager.horizontalInputAxis != scrollInputName))
				{
					canScroll = true;
				}

				// If the Inventory is open, support scrolling between the various items
				if (canScroll && KickStarter.playerInput.InputGetAxis (scrollInputName) < -scrollInputThreshold)
				{
					if (released)
					{
						ShiftInventory (AC_ShiftInventory.ShiftPrevious);
					}
				}
				else if (canScroll && KickStarter.playerInput.InputGetAxis (scrollInputName) > scrollInputThreshold)
				{
					if (released)
					{
						ShiftInventory (AC_ShiftInventory.ShiftNext);
					}
				}
				else
				{
					released = true;
				}

				if (interactionMenu.IsOff ())
				{
					// Select an item
					if (KickStarter.playerInput.InputGetButtonDown ("InteractionA"))
					{
						if (InvInstance.IsValid (KickStarter.runtimeInventory.SelectedInstance))
						{
							// Another item is already selected, so combine them
							inventoryBox.GetInstance (0).Combine (KickStarter.runtimeInventory.SelectedInstance);
						}
						else
						{
							ShowInteractions ();
						}
					}
				}
			}
			
			// Move the 3D items so that the selected one is in view
			if (isOn)
			{
				int offset = inventoryBox.GetOffset ();
				for (int i=0; i<spawnedItems.Length; i++)
				{
					if (spawnedItems[i].transform == null) continue;
					spawnedItems[i].DoSpin = (i == offset);

					Vector3 newItemPosition = Vector3.right * separationDistance * (i - offset);
					if (moveSpeed > 0f)
					{
						spawnedItems[i].transform.position = Vector3.Lerp (spawnedItems[i].transform.position, newItemPosition, Time.unscaledDeltaTime * moveSpeed);
					}
					else
					{
						spawnedItems[i].transform.position = newItemPosition;
					}
				}
			}
		}

		#endregion


		#region CustomEvents

		private void OnMenuTurnOn (Menu menu, bool isInstant)
		{ 
			// When the Inventory menu is on, prevent Player movement and spawn the 3D item prefabs

			if (menu == inventoryMenu)
			{
				KickStarter.runtimeInventory.SetNull ();

				KickStarter.player.upMovementLocked = true;
				KickStarter.player.downMovementLocked = true;
				KickStarter.player.leftMovementLocked = true;
				KickStarter.player.rightMovementLocked = true;
				KickStarter.player.freeAimLocked = true;

				released = true;
				isOn = true;

				KickStarter.cursorManager.inventoryHandling = InventoryHandling.ChangeCursorAndHotspotLabel;

				foreach (InvInstance invInstance in KickStarter.player.Inventory.InvInstances)
				{
					UpdateReloadInteractionState (invInstance);
				}

				SpawnItems ();

				if (autoShowInteractions)
				{
					Invoke (nameof (ShowInteractions), 0.1f);
				}
			}
		}

		
		private void OnMenuTurnOff (Menu menu, bool isInstant)
		{
			// When the Inventory menu is turned off, allow Player movement and remove the 3D items

			if (menu == inventoryMenu)
			{
				KickStarter.player.upMovementLocked = false;
				KickStarter.player.downMovementLocked = false;
				KickStarter.player.leftMovementLocked = false;
				KickStarter.player.rightMovementLocked = false;
				KickStarter.player.freeAimLocked = false;

				isOn = false;

				KickStarter.cursorManager.inventoryHandling = InventoryHandling.ChangeCursor;

				foreach (InventoryItemSpin spawnedItem in spawnedItems)
				{
					Destroy (spawnedItem.gameObject);
				}

				interactionMenu.TurnOff ();
			}
		}


		private void OnInventoryInteract (InvInstance invInstance, int iconID)
		{
			ignoreInputThisFrame = true;

			if (iconID == combineIconID)
			{
				// Combine the item	
				invInstance.Select ();
			}
			else if (iconID == reloadIconID)
			{
				// Reload the item
				Reload (invInstance);
			}
		}


		private void OnInventoryCombine (InvInstance invInstanceA, InvInstance invInstanceB)
		{
			if (invInstanceA.InvItem.binID == weaponCategoryID && invInstanceB.InvItem.id == invInstanceA.GetProperty (ammoIDPropertyID).IntegerValue)
			{
				Reload (invInstanceA);
			}
			else if (invInstanceB.InvItem.binID == weaponCategoryID && invInstanceA.InvItem.id == invInstanceB.GetProperty (ammoIDPropertyID).IntegerValue)
			{
				Reload (invInstanceB);
			}
		}


		private void OnBeforeLoading (SaveFile saveFile)
		{
			spawnedItems = new InventoryItemSpin[0];
		}

		#endregion


		#region PrivateFunctions

		private void ShowInteractions ()
		{
			if (isOn && interactionMenu.IsOff () && KickStarter.stateHandler.IsInGameplay ())
			{
				if (!InvInstance.IsValid (KickStarter.runtimeInventory.SelectedInstance))
				{
					// No other item is selected, so bring up interaction menu
					interactionMenu.MatchInteractions (inventoryBox.GetInstance (0), false);
					interactionMenu.TurnOn ();
				}
			}
		}


		private void Reload (InvInstance invInstance)
		{
			PlayerCombat.Instance.Reload (invInstance);
			UpdateReloadInteractionState (invInstance);
		}


		private void ShiftInventory (AC_ShiftInventory shiftType)
		{
			if (!inventoryBox.CanBeShifted (shiftType))
			{
				return;
			}

			interactionMenu.TurnOff ();
			
			released = false;
			inventoryBox.Shift (shiftType, 1);

			if (KickStarter.sceneSettings.defaultSound)
			{
				KickStarter.sceneSettings.defaultSound.audioSource.clip = scrollClip;
				KickStarter.sceneSettings.defaultSound.Play ();
			}

			if (autoShowInteractions)
			{
				Invoke (nameof (ShowInteractions), 0.3f);
			}
		}


		private void SpawnItems ()
		{
			// For each item held by the Player, spawn its "Linked prefab" and arrange in a row

			spawnedItems = new InventoryItemSpin [KickStarter.runtimeInventory.localItems.Count];
			for (int i=0; i<spawnedItems.Length; i++)
			{
				if (KickStarter.runtimeInventory.localItems[i].linkedPrefab)
				{
					GameObject spawnedItem = Instantiate (KickStarter.runtimeInventory.localItems[i].linkedPrefab, Vector3.right * separationDistance * i, Quaternion.identity);
					spawnedItems[i] = spawnedItem.GetComponent <InventoryItemSpin>();
				}
				else
				{
					GameObject emptyItem = new GameObject ();
					emptyItem.transform.position = Vector3.right * separationDistance * i;
					spawnedItems[i] = emptyItem.AddComponent<InventoryItemSpin> ();
				}
			}

			int offset = inventoryBox.GetOffset ();
			for (int i = 0; i < spawnedItems.Length; i++)
			{
				spawnedItems[i].DoSpin = (i == offset);

				Vector3 newItemPosition = Vector3.right * separationDistance * (i - offset);
				spawnedItems[i].transform.position = newItemPosition;
			}
		}


		private void UpdateReloadInteractionState (InvInstance invInstance)
		{
			if (InvInstance.IsValid (invInstance) && invInstance.InvItem.binID == weaponCategoryID)
			{
				int ammoID = invInstance.GetProperty (ammoIDPropertyID).IntegerValue;
				int ammoLeft = KickStarter.runtimeInventory.PlayerInvCollection.GetCount (ammoID);
				int ammoCapacity = invInstance.GetProperty (ammoCapacityPropertyID).IntegerValue;
				int ammoInChamber = invInstance.GetProperty (ammoInChamberPropertyID).IntegerValue;
				invInstance.SetInteractionState (reloadIconID, ammoLeft > 0 && ammoCapacity > ammoInChamber, true);
			}
		}

		#endregion

	}

}