context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using Systems.Clearance;
  using UnityEngine;
  using Mirror;
  using NaughtyAttributes;
  using Random = UnityEngine.Random;
  using UI.Action;
  using AddressableReferences;
  using UI.Items.PDA;
  
  namespace Items.PDA
  {
  	[RequireComponent(typeof(ItemStorage))]
  	[RequireComponent(typeof(ItemLightControl))]
  	[RequireComponent(typeof(ItemAttributesV2))]
  	[RequireComponent(typeof(PDANotesNetworkHandler))]
  	public class PDALogic : NetworkBehaviour,
  		ICheckedInteractable<HandApply>,
  		ICheckedInteractable<InventoryApply>,
  		IServerInventoryMove,
  		IClearanceProvider
  	{
  		// TODO: consider moving uplink code into its own class (perhaps compatible with pen, headset uplinks)
  
  		#region Inspector
  		[Tooltip("The cartridge prefab to be spawned for this PDA")]
  		[SerializeField, BoxGroup("Settings")]
  		private GameObject cartridgePrefab = default;
  
  		[Tooltip("The default ringtone to play")]
  		[SerializeField, BoxGroup("Settings")]
  		private AddressableAudioSource defaultRingtone;
  
  		[Tooltip("A list of all available ringtones")]
  		[SerializeField, BoxGroup("Settings")]
  		private List<AddressableAudioSource> ringtones;
  
  		[Tooltip("The denial sound")]
  		[SerializeField, BoxGroup("Settings")]
  		private AddressableAudioSource denialSound;
  
  		[Tooltip("Reset registered name on FactoryReset?")]
  		[SerializeField, BoxGroup("Settings")]
  		private bool willResetName = false;
  
  		[Tooltip("Tint of the main background in the GUI")]
  		[BoxGroup("GUI")]
  		public Color UIBG;
  
  		[Tooltip("Tint of the patterned overlay in the GUI")]
  		[BoxGroup("GUI")]
  		public Color UIOVER;
  
  		[Tooltip("The overlay to be used in the GUI")]
  		[BoxGroup("GUI")]
  		public int OVERLAY;
  
  		public GUI_PDA PDAGui;
  
  		[Tooltip("How long the delay before the owner is informed of the uplink code " +
  			"(intedned to reduce information overload - likely just received objectives)")]
  		[SerializeField, BoxGroup("Uplink"), Range(0, 60)]
  		private float informUplinkCodeDelay = 10;
  
  		private bool isNukeOps = false;
  		public bool IsNukeOps => isNukeOps;
  
  		[SerializeField]
  		private ItemTrait telecrystalTrait;
  
  		[SerializeField, BoxGroup("Uplink")]
  		private bool debugUplink = false;
  
  		#endregion Inspector
  
  		// GameObject attached components
  		private Pickupable pickupable;
  		public ItemStorage storage {get; private set;}
  		private ItemLightControl flashlight;
  		private ItemActionButton actionButton;
  
  		/// <summary> The IDCard that is currently inserted into the PDA </summary>
  		private IDCard IDCard;
  
  		/// <summary> The name of the currently registered player (since the last PDA reset) </summary>
  		public string RegisteredPlayerName { get; private set; }
  		public AddressableAudioSource Ringtone { get; private set; }
  		/// <summary> The string that must be entered into the ringtone slot for the uplink </summary>
  		public string UplinkUnlockCode { get; private set; }
  		public bool IsUplinkCapable { get; private set; } = false;
  		public bool IsUplinkLocked { get; private set; } = true;
  		/// <summary> The count of how many telecrystals this PDA has </summary>
  		public int UplinkTC { get; set; }
  
  		public bool FlashlightOn => flashlight.IsOn;
  
  		public Action registeredPlayerUpdated;
  		public Action idSlotUpdated;
  		public Action cartridgeSlotUpdated;
  
  		private ItemSlot IDSlot = default;
  		private ItemSlot CartridgeSlot = default;
  
  		//The actual list of access allowed set via the server and synced to all clients
  		private readonly SyncList<int> accessSyncList = new SyncList<int>();
  
  		#region Lifecycle
  
  		private void Awake()
  		{
  			pickupable = GetComponent<Pickupable>();
  			storage = GetComponent<ItemStorage>();
  			flashlight = GetComponent<ItemLightControl>();
  			actionButton = GetComponent<ItemActionButton>();
  		}
  
  		private void OnEnable()
  		{
  			actionButton.ServerActionClicked += ToggleFlashlight;
  		}
  
  		private void OnDisable()
  		{
  			actionButton.ServerActionClicked -= ToggleFlashlight;
  		}
  
  		private void Start()
  		{
  			SetRingtone(defaultRingtone);
  
  			IDSlot = storage.GetIndexedItemSlot(0);
  			CartridgeSlot = storage.GetIndexedItemSlot(1);
  
  			IDSlot.OnSlotContentsChangeServer.AddListener(OnIDSlotChanged);
  
  			if (CustomNetworkManager.IsServer && cartridgePrefab != null)
  			{
  				var cartridge = Spawn.ServerPrefab(cartridgePrefab).GameObject;
  				Inventory.ServerAdd(cartridge, CartridgeSlot);
  			}
  		}
  
  		#endregion Lifecycle
  
  		public void OnInventoryMoveServer(InventoryMove info)
  		{
  			// This is also triggered when the PDA spawns as part of the player's inventory populator
  			// and added to their inventory, when they spawn.
  
  			if (RegisteredPlayerName != default) return; // PDA already registered to someone
  			if (info.ToRootPlayer == null) return; // PDA was not added to player
  
  			ConnectedPlayer pickedUpBy = info.ToRootPlayer.gameObject.Player();
  			RegisterTo(pickedUpBy);
  
  			if (debugUplink)
  			{
  				InstallUplink(pickedUpBy, 80, true);
  			}
  		}
  
  		private void RegisterTo(ConnectedPlayer player)
  		{
  			RegisteredPlayerName = player.Script.playerName;
  			gameObject.name = $"{player.Script.playerName}'s PDA ({player.Script.mind.occupation.DisplayName})";
  			gameObject.Item().ServerSetArticleName(gameObject.name);
  		}
  
  		private void RegisterTo(string playerName)
  		{
  			RegisteredPlayerName = playerName;
  			gameObject.name = playerName == default ? gameObject.Item().InitialName : $"{playerName}'s PDA";
  			gameObject.Item().ServerSetArticleName(gameObject.name);
  		}
  
  		public void ToggleFlashlight()
  		{
  			flashlight.Toggle(!flashlight.IsOn);
  		}
  
  		/// <summary>
  		/// Resets PDA and tells the MessengerManager to set PDA to unknown
  		/// </summary>
  		public void ResetPDA()
  		{
  			if (RegisteredPlayerName == default ||
  					IDSlot.IsOccupied && GetIDCard().RegisteredName == RegisteredPlayerName)
  			{
  				if (willResetName)
  				{
  					RegisterTo(default(string));
  				}
  
  				LockUplink();
  				SetRingtone(defaultRingtone);
  				PlayRingtone();
  
  				registeredPlayerUpdated?.Invoke();
  			}
  
  			else
  			{
  				PlayDenyTone();
  			}
  		}
  
  		private ConnectedPlayer GetPlayerByParentInventory()
  		{
  			if (pickupable.ItemSlot == null) return default;
  
  			return pickupable.ItemSlot.RootPlayer().gameObject.Player();
  		}
  
  		#region Sounds
  
  		public void SetRingtone(AddressableAudioSource newRingtone)
  		{
  			Ringtone = newRingtone;
  		}
  
  		public void SetRingtone(string newRingtone)
  		{
  			AddressableAudioSource toSend = ringtones.Find(x => x.AssetAddress.Contains("/" + newRingtone + ".prefab"));
  
  			if(toSend != default(AddressableAudioSource))
  				SetRingtone(toSend);
  		}
  
  		public void PlayRingtone()
  		{
  			PlaySound(Ringtone);
  		}
  
  		public void PlayDenyTone()
  		{
  			PlaySound(denialSound);
  		}
  
  		public void PlaySound(AddressableAudioSource soundName)
  		{
  			GameObject sourceObject = gameObject;
  
  			ConnectedPlayer player = GetPlayerByParentInventory();
  			if (player != null)
  			{
  				sourceObject = player.GameObject;
  			}
  
  			SoundManager.PlayNetworkedAtPos(soundName, sourceObject.AssumedWorldPosServer(), sourceObj: sourceObject);
  		}
  
  		public void PlaySoundPrivate(AddressableAudioSource soundName)
  		{
  			var player = GetPlayerByParentInventory();
  			if (player == null) return;
  
  			_ = SoundManager.PlayNetworkedForPlayerAtPos(player.GameObject, player.Script.WorldPos, soundName, sourceObj: player.GameObject);
  		}
  
  		#endregion Sounds
  
  		#region Interaction
  		// HandApply for clicking on the PDA while it is on the ground.
  		// InventoryApply for clicking on the PDA while in inventory.
  
  		public bool WillInteract(HandApply interaction, NetworkSide side)
  		{
  			if (!DefaultWillInteract.Default(interaction, side)) return false;
  			if (interaction.IsAltClick && IDSlot.IsOccupied) return true;
  
  			return WillInsert(interaction.UsedObject, side);
  		}
  
  		public void ServerPerformInteraction(HandApply interaction)
  		{
  			if (interaction.IsAltClick && IDSlot.IsOccupied)
  			{
  				EjectIDCard();
  				Chat.AddActionMsgToChat(gameObject, "You eject the ID from the PDA.",
  					$"{interaction.Performer.ExpensiveName()} ejects an ID from the {gameObject.ExpensiveName()}");
  				return;
  			}
  
  			ServerInsertItem(interaction.UsedObject, interaction.HandSlot, interaction.Performer);
  			Chat.AddActionMsgToChat(gameObject, "You insert the ID inside the PDA.", $"{interaction.Performer.ExpensiveName()} inserts an ID into the {gameObject.ExpensiveName()}");
  		}
  
  		public bool WillInteract(InventoryApply interaction, NetworkSide side)
  		{
  			if (!DefaultWillInteract.Default(interaction, side)) return false;
  			if (interaction.IsAltClick && IDSlot.IsOccupied) return true;
  
  			return WillInsert(interaction.UsedObject, side);
  		}
  
  		public void ServerPerformInteraction(InventoryApply interaction)
  		{
  			if (interaction.IsAltClick && IDSlot.IsOccupied)
  			{
  				EjectIDCard();
  				return;
  			}
  
  			ServerInsertItem(interaction.UsedObject, interaction.FromSlot , interaction.Performer);
  		}
  
  		private bool WillInsert(GameObject item, NetworkSide side)
  		{
  			if (item == null) return false;
  
  			if (item.TryGetComponent(out IDCard card) && Validations.CanFit(IDSlot, item, side, true))
  			{
  				return true;
  			}
  
  			if (item.TryGetComponent(out PDACartridge cartridge) && Validations.CanFit(CartridgeSlot, item, side, true))
  			{
  				return true;
  			}
  
  			if (Validations.HasItemTrait(item, telecrystalTrait))
  			{
  				return true;
  			}
  
  			return false;
  		}
  
  		private void ServerInsertItem(GameObject item, ItemSlot fromSlot, GameObject player)
  		{
  			if (item.TryGetComponent(out IDCard card))
  			{
  				if (IDSlot.IsOccupied)
  				{
  					EjectIDCard();
  				}
  
  				Inventory.ServerTransfer(fromSlot, IDSlot);
  				IDCard = card;
  			}
  
  
gt: 			else if (item.TryGetComponent(out PDACartridge cartridge))