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

[RequireComponent(typeof(Collider))]
public class PressurePlateButton : MonoBehaviour
{
    [Header("Detection")]
    [Tooltip("Layers that can activate the plate. Keep default to allow everything.")]
    [SerializeField] private LayerMask detectionMask = ~0;

    [Tooltip("If true, rigidbody objects can activate the plate.")]
    [SerializeField] private bool allowRigidbodies = true;

    [Tooltip("If true, the Player can activate the plate (tag Player or PlayerMovement component).")]
    [SerializeField] private bool allowPlayer = true;

    [Header("Events")]
    public UnityEvent OnPressed;
    public UnityEvent OnReleased;

    private readonly HashSet<Collider> m_validCollidersOnPlate = new HashSet<Collider>();
    private readonly HashSet<Collider> m_stayedThisPhysicsFrame = new HashSet<Collider>();
    private bool m_isPressed;

    private void Reset()
    {
        Collider col = GetComponent<Collider>();
        col.isTrigger = true;
    }

    private void OnTriggerEnter(Collider other)
    {
        if (!IsValidActivator(other))
            return;

        m_validCollidersOnPlate.Add(other);

        if (!m_isPressed)
        {
            m_isPressed = true;
            OnPressed?.Invoke();
        }
    }

    private void OnTriggerStay(Collider other)
    {
        if (IsValidActivator(other))
            m_stayedThisPhysicsFrame.Add(other);
    }

    private void OnTriggerExit(Collider other)
    {
        m_stayedThisPhysicsFrame.Remove(other);

        if (!m_validCollidersOnPlate.Remove(other))
            return;

        if (m_validCollidersOnPlate.Count == 0 && m_isPressed)
        {
            m_isPressed = false;
            OnReleased?.Invoke();
        }
    }

    private void FixedUpdate()
    {
        if (m_validCollidersOnPlate.Count == 0)
        {
            m_stayedThisPhysicsFrame.Clear();
            return;
        }

        m_validCollidersOnPlate.RemoveWhere(c => c == null || !c.enabled || !c.gameObject.activeInHierarchy);

        m_validCollidersOnPlate.IntersectWith(m_stayedThisPhysicsFrame);
        m_stayedThisPhysicsFrame.Clear();

        if (m_validCollidersOnPlate.Count == 0 && m_isPressed)
        {
            m_isPressed = false;
            OnReleased?.Invoke();
        }
    }

    private bool IsValidActivator(Collider other)
    {
        if (((1 << other.gameObject.layer) & detectionMask) == 0)
            return false;

        if (allowPlayer)
        {
            if (other.CompareTag("Player"))
                return true;

            if (other.GetComponentInParent<PlayerMovement>() != null)
                return true;
        }

        if (allowRigidbodies && other.attachedRigidbody != null)
            return true;

        return false;
    }
}
