context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Security.Cryptography;
  using UnityEngine;
  using Mirror;
  using Systems.Disposals;
  using AddressableReferences;
  using Random = UnityEngine.Random;
  using Messages.Server.SoundMessages;
  using Systems.Atmospherics;
  
  namespace Objects.Disposals
  {
  	public enum BinState
  	{
  		Disconnected,
  		Off,
  		Ready,
  		Flushing,
  		Recharging
  	}
  
  	public enum BinSprite
  	{
  		Upright = 0,
  		Sideways = 1,
  		Flushing = 2
  	}
  
  	public enum BinOverlaySprite
  	{
  		Ready = 0,
  		Charging = 1,
  		Full = 2,
  		Handle = 3
  	}
  
  	public class DisposalBin : DisposalMachine, IExaminable, ICheckedInteractable<MouseDrop>, IEscapable
  	{
  		private const int CHARGED_PRESSURE = 600; // kPa
  		private const int AUTO_FLUSH_DELAY = 2;
  		private const float ANIMATION_TIME = 1.3f; // As per sprite sheet JSON file.
  
  		[SerializeField]
  		private AddressableAudioSource rechargeSFX = null;
  		[SerializeField]
  		private AddressableAudioSource AirFlapSound = null;
  		[SerializeField]
  		private AddressableAudioSource FlushSound = null;
  		[SerializeField]
  		[Tooltip("The sound when throwing things in the bin.")]
  		private List<AddressableAudioSource> trashDunkSounds = null;
  		[SerializeField]
  		[Tooltip("The sound when the item doesn't fall into the trash can.")]
  		private AddressableAudioSource trashDunkMissSound = null;
  
  		private string runLoopGUID = "";
  
  		private HasNetworkTab netTab;
  		private SpriteHandler overlaysSpriteHandler;
  
  		private Coroutine autoFlushCoroutine;
  		private Coroutine rechargeCoroutine;
  
  		// GUI instances can listen to this, to update UI state.
  		public event Action BinStateUpdated;
  
  		// We sync binState so that the client knows when to play or stop the recharging SFX.
  		[SyncVar(hook = nameof(OnSyncBinState))]
  		private BinState binState = BinState.Disconnected;
  		[SyncVar]
  		private float chargePressure = 0;
  
  		public BinState BinState => binState;
  		public bool PowerDisconnected => binState == BinState.Disconnected;
  		public bool PowerOff => binState == BinState.Off;
  		public bool BinReady => binState == BinState.Ready;
  		public bool BinFlushing => binState == BinState.Flushing;
  		public bool BinCharging => binState == BinState.Recharging;
  		public override bool MachineWeldable => base.MachineWeldable && PowerDisconnected;
  		/// <summary>
  		/// If the bin is already connected to power, it is only screwdriverable if it is set to off.
  		/// This allows the screwdriver to be disposed of during normal operations.
  		/// </summary>
  		public bool Screwdriverable => MachineSecured && (PowerDisconnected || PowerOff);
  		public float ChargePressure => chargePressure;
  		public bool BinCharged => chargePressure >= CHARGED_PRESSURE;
  
  		private float RandomDunkPitch => Random.Range(0.7f, 1.2f);
  
  		#region Lifecycle
  
  		protected override void Awake()
  		{
  			base.Awake();
  			netTab = GetComponent<HasNetworkTab>();
  			overlaysSpriteHandler = transform.GetChild(1).GetComponent<SpriteHandler>();
  		}
  
  		public override void OnSpawnServer(SpawnInfo info)
  		{
  			// Assume bin starts unanchored and therefore UI is inaccessable.
  			netTab.enabled = false;
  			UpdateSpriteBinState();
  
  			base.OnSpawnServer(info);
  		}
  
  		protected override void SpawnMachineAsInstalled()
  		{
  			base.SpawnMachineAsInstalled();
  
  			chargePressure = CHARGED_PRESSURE;
  			SetBinState(BinState.Ready);
  		}
  
  		#endregion Lifecycle
  
  		#region Sync
  
  		private void OnSyncBinState(BinState oldState, BinState newState)
  		{
  			binState = newState;
  
  			if (BinCharging)
  			{
  				runLoopGUID = Guid.NewGuid().ToString();
  				SoundManager.PlayAtPositionAttached(rechargeSFX, registerObject.WorldPosition, gameObject, runLoopGUID);
  			}
  			else
  			{
  				SoundManager.Stop(runLoopGUID);
  			}
  		}
  
  		#endregion Sync
  
  		private void SetBinState(BinState newState)
  		{
  			binState = newState;
  			UpdateSpriteBinState();
  		}
  
  		#region Sprites
  
  		protected override void UpdateSpriteConstructionState()
  		{
  			UpdateSpriteBinState();
  		}
  
  		private void UpdateSpriteBinState()
  		{
  			if (MachineUnattached)
  			{
  				baseSpriteHandler.ChangeSprite((int) BinSprite.Sideways);
  				overlaysSpriteHandler.PushClear();
  				return;
  			}
  
  			switch (binState)
  			{
  				case BinState.Disconnected:
  					baseSpriteHandler.ChangeSprite((int) BinSprite.Upright);
  					overlaysSpriteHandler.PushClear();
  					break;
  				case BinState.Off:
  					baseSpriteHandler.ChangeSprite((int) BinSprite.Upright);
  					overlaysSpriteHandler.PushClear();
  					break;
  				case BinState.Ready:
  					baseSpriteHandler.ChangeSprite((int) BinSprite.Upright);
  					overlaysSpriteHandler.ChangeSprite((int) BinOverlaySprite.Ready);
  					break;
  				case BinState.Flushing:
  					baseSpriteHandler.ChangeSprite((int) BinSprite.Flushing);
  					overlaysSpriteHandler.PushClear();
  					break;
  				case BinState.Recharging:
  					baseSpriteHandler.ChangeSprite((int) BinSprite.Upright);
  					overlaysSpriteHandler.ChangeSprite((int) BinOverlaySprite.Charging);
  					break;
  			}
  
  			BinStateUpdated?.Invoke();
  		}
  
  		#endregion Sprites
  
  		#region Interactions
  
  		// Click on disposal bin
  		public override bool WillInteract(PositionalHandApply interaction, NetworkSide side)
  		{
  			if (DefaultWillInteract.Default(interaction, side) == false) return false;
  			if (interaction.HandObject == null) return false;
  
  			if (base.WillInteract(interaction, side)) return true;
  			// Bin accepts all items for disposal.
  			return MachineSecured;
  		}
  
  		// Click on disposal bin
  		public override void ServerPerformInteraction(PositionalHandApply interaction)
  		{
  			currentInteraction = interaction;
  
  			if (Validations.HasUsedItemTrait(interaction, CommonTraits.Instance.Wrench) && MachineWrenchable)
  			{
  				TryUseWrench();
  			}
  			else if (Validations.HasUsedActiveWelder(interaction) && MachineWeldable)
  			{
  				TryUseWelder();
  			}
  			else if (Validations.HasUsedItemTrait(interaction, CommonTraits.Instance.Screwdriver) && Screwdriverable)
  			{
  				TryUseScrewdriver();
  			}
  			else if (MachineSecured)
  			{
  				Inventory.ServerDrop(interaction.HandSlot, interaction.TargetVector);
  				StoreItem(interaction.UsedObject);
  			}
  		}
  
  		// Drag something and drop on disposal bin
  		public bool WillInteract(MouseDrop interaction, NetworkSide side)
  		{
  			if (DefaultWillInteract.Default(interaction, side) == false) return false;
  
  			if (interaction.TargetObject == null) return false;
  
  			if (Validations.IsReachableByRegisterTiles(
  					interaction.Performer.RegisterTile(),
  					interaction.UsedObject.RegisterTile(),
  					side == NetworkSide.Server) == false) return false;
  
  			if (Validations.IsReachableByRegisterTiles(
  				interaction.Performer.RegisterTile(),
  				interaction.TargetObject.RegisterTile(),
  				side == NetworkSide.Server) == false) return false;
  
  			return true;
  		}
  
  		// Drag something and drop on disposal bin
  		public void ServerPerformInteraction(MouseDrop interaction)
  		{
  			if (interaction.UsedObject == null) return;
  			if (interaction.UsedObject.TryGetComponent<PlayerScript>(out _) == false) return; // Test to see if player
  
  			// Don't store player unless secured.
  
gt: 			if (MachineSecured == false) return;