namespace Fusion.Addons.KCC
{
	using UnityEngine;
	using Fusion;
	/// 
	/// This component serves as an AoI position proxy. It is useful for objects which don't need their own NetworkTransform component, but still need to be filtered by AoI.
	/// Example: Platform processor is a separate NetworkObject with KCCInterestProxy component. Upon spawn the processor is parented usually under the player object with KCC.
	/// There is no need to synchronize position of platform processor because it is driven locally. But if a player runs out of AoI, we want to stop sending data.
	/// 
	[DisallowMultipleComponent]
	[DefaultExecutionOrder(10000)]
	public sealed unsafe class KCCInterestProxy : NetworkTRSP
	{
		// PUBLIC MEMBERS
		public Transform PositionSource => _positionSource;
		// PRIVATE MEMBERS
		[SerializeField]
		private Transform _positionSource;
		private Transform _defaultPositionSource;
		private bool      _hasExplicitPositionSource;
		private bool      _isSpawned;
		// PUBLIC METHODS
		public void SetPosition(Vector3 position)
		{
			if (_isSpawned == true)
			{
				State.Position = position;
			}
		}
		public void SetPositionSource(Transform positionSource)
		{
			_positionSource = positionSource;
			_hasExplicitPositionSource = true;
			Synchronize();
		}
		public void ResetPositionSource()
		{
			_positionSource = _defaultPositionSource;
			_hasExplicitPositionSource = false;
			Synchronize();
		}
		public void FindPositionSourceInParent()
		{
			FindPositionSourceInParent(true);
			Synchronize();
		}
		public void Synchronize()
		{
			if (_isSpawned == true && _positionSource != null)
			{
				State.Position = _positionSource.position;
			}
		}
		// NetworkBehaviour INTERFACE
		public override void Spawned()
		{
			_isSpawned = true;
			// Don't synchronize this component at all, it is needed on server only.
			ReplicateToAll(false);
			if (_hasExplicitPositionSource == false && _positionSource == null)
			{
				State.Position = transform.position;
				FindPositionSourceInParent(false);
			}
			Synchronize();
		}
		public override void Despawned(NetworkRunner runner, bool hasState)
		{
			_isSpawned = false;
			ResetPositionSource();
		}
		public override void FixedUpdateNetwork()
		{
			Synchronize();
		}
		// MonoBehaviour INTERFACE
		private void Awake()
		{
			_defaultPositionSource = _positionSource;
		}
		// PRIVATE METHODS
		private void FindPositionSourceInParent(bool isExplicit)
		{
			Transform parentTransform = transform.parent;
			while (parentTransform != null)
			{
				NetworkObject networkObject = parentTransform.GetComponent();
				if (networkObject != null)
				{
					_positionSource = parentTransform;
					_hasExplicitPositionSource = isExplicit;
					break;
				}
				parentTransform = parentTransform.parent;
			}
		}
	}
}