using System;
using System.Collections.Generic;
using UnityEngine;

namespace Escape_Room_Engine.Portal
{
    [RequireComponent(typeof(Collider))]
    public class Portal : MonoBehaviour
    {
        public static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0));
        
        /// <summary>
        /// The portal that is connected with this one.
        /// </summary>
        public Portal linkedPortal;
        /// <summary>
        /// The camera that will draw the view for this portal.
        /// </summary>
        public PortalCamera portalCamera;

        private readonly List<PortalDriver> _closePortalDrivers = new();

        private void Awake()
        {
            // check whether the other portal is set up
            if (!linkedPortal || linkedPortal.linkedPortal != this) throw new Exception("Other portal not set up correctly.");
            
            // check whether the collider is set up correctly
            if (!GetComponent<Collider>().isTrigger) throw new Exception("Collider must be a trigger.");
        }

        private void FixedUpdate()
        {
            for (var i = 0; i < _closePortalDrivers.Count; i++)
            {
                var portalDriver = _closePortalDrivers[i];
                var side = CalculateSide(portalDriver.transform);
                if (portalDriver.entrySide < 0 && side >= 0) // must have entered from the front and exited the back
                {
                    _closePortalDrivers.Remove(portalDriver);
                    linkedPortal._closePortalDrivers.Add(portalDriver);
                    portalDriver.entrySide = -1;
                    portalDriver.Teleport(this, linkedPortal);
                    i--; // decrease the loop counter because the list is one element smaller now
                }
            }
        }

        private void OnTriggerEnter(Collider other)
        {
            var portalDriver = other.GetComponent<PortalDriver>();
            if (portalDriver && !_closePortalDrivers.Contains(portalDriver))
            {
                _closePortalDrivers.Add(portalDriver);
                portalDriver.entrySide = CalculateSide(portalDriver.transform);
            }
        }

        private void OnTriggerExit(Collider other)
        {
            var portalDriver = other.GetComponent<PortalDriver>();
            if (portalDriver)
            {
                _closePortalDrivers.Remove(portalDriver);
            }
        }

        private int CalculateSide(Transform portalDriverTransform)
        {
            var t = transform;
            return Math.Sign(Vector3.Dot(t.forward, portalDriverTransform.position - t.position));
        }
    }
}
