using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;

namespace EscapeRoomEngine.Portal.Runtime
{
    /// <summary>
    /// A portal driver is any object that can be teleported by a portal like the player, their hands or objects.
    /// </summary>
    [RequireComponent(typeof(Collider), typeof(Rigidbody))]
    public class PortalDriver : MonoBehaviour
    {
        /// <summary>
        /// The object that will be transported through the portal. Usually either this object or a parent offset object.
        /// If left empty, it will default to this object.
        /// </summary>
        public Transform traveller;
        /// <summary>
        /// Whether this portal driver has a clone mirroring it at other portals. Disable this for the player.
        /// </summary>
        public bool hasClone = true;
        /// <summary>
        /// Whether this portal driver is the player. Used by portals to tell when the player has passed to another space.
        /// </summary>
        public bool player;

        /// <summary>
        /// The side of the portal this became tracked on.
        /// </summary>
        [HideInInspector] public int entrySide;
        /// <summary>
        /// The clone that is used to mirror this portal driver at another portal.
        /// </summary>
        [HideInInspector] public PortalDriverClone clone;

        private Rigidbody _rigidbody;

        private void Awake()
        {
            // check whether the traveller is set
            if (!traveller) traveller = transform;
            
            // get the rigidbody if there is one
            _rigidbody = GetComponent<Rigidbody>();
        }

        protected void Start()
        {
            if (hasClone)
            {
                clone = PortalDriverClone.Create(this);
            }
        }

        private void Update()
        {
            if (hasClone && clone.gameObject.activeSelf)
            {
                clone.UpdatePosition(transform);
            }
        }

        public void EnableClone(Portal at)
        {
            if (hasClone)
            {
                Logger.Log($"Enabled {clone} at {at}", LogType.Portals);
                
                clone.portal = at;
                clone.gameObject.SetActive(true);
            }
        }

        public void DisableClone(Portal at)
        {
            if (hasClone && at.Equals(clone.portal)) // don't disable clones that are already at a different portal
            {
                Logger.Log($"Disabled {clone} at {clone.portal}", LogType.Portals);
                
                clone.portal = null;
                clone.gameObject.SetActive(false);
            }
        }

        public void Teleport(Portal from, Portal to)
        {
            Logger.Log($"Teleported {this} from {from} to {to}", LogType.Portals);
            
            var m = to.portalTransform.localToWorldMatrix * Portal.HalfRotation *
                    from.portalTransform.worldToLocalMatrix * traveller.localToWorldMatrix;
            traveller.SetPositionAndRotation(m.GetPosition(), m.rotation);
            if (_rigidbody && !_rigidbody.isKinematic)
            {
                _rigidbody.velocity = to.portalTransform.TransformDirection(
                    Portal.HalfRotation.rotation * from.portalTransform.InverseTransformDirection(_rigidbody.velocity));
            }
        }
    }
}
