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

public class Detection : MonoBehaviour
{
    [SerializeField]
    private Entity _entityLinked;

    private Vector2 detectionRange;
    private BoxCollider2D _collider;
    // List to store all detected objects trough triggerStay2D
    private List<Entity> detectedEntities = new List<Entity>();

    // If it's a projectile damage > 0
    private int _projectileDamage = 0;

    private bool _hasStartBeenCalled = false;

    protected virtual void Start()
    {
        _collider = GetComponent<BoxCollider2D>();
        detectionRange = _collider.size;

        StartCoroutine(C_DetectCoroutine());

        _hasStartBeenCalled = true;
    }

    /* 
     * Checks if Start ran once already.
     * onEnable runs by default before Start so not everything is initialized
     * and I don't want to reinitialize the collider everytime I re-enable a unit's detection.
    */
    protected void OnEnable()
    {
        if (_hasStartBeenCalled)
        {
            StartCoroutine(C_DetectCoroutine());
        }
    }

    // Looped detection every 'delay' amount
    private IEnumerator C_DetectCoroutine()
    {
        // Can happen if unit dies
        if (_entityLinked == null) yield break;

        ResizeCollider();
        // Remove objects that are null
        detectedEntities.RemoveAll(obj => !obj);

        Entity closest = GetClosest();
        if (closest != null)
        {
            _entityLinked.IsEnemyDetected = true;
            _entityLinked.Enemy = closest;
        }
        else
        {
            _entityLinked.IsEnemyDetected = false;
            _entityLinked.Enemy = null;
        }
        yield return new WaitForSeconds(0.05f);
        StartCoroutine(C_DetectCoroutine());
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (_entityLinked == null) return;

        // Projectiles detection + deal dmg
        Entity otherEntity = other.gameObject.GetComponent<Entity>();
        if (otherEntity == _entityLinked)
        {
            GhostOpponent isGhost = EntityLinked.gameObject.GetComponent<GhostOpponent>();
            if (isGhost != null)
            {
                if(!isGhost.dodgedProjectile()) _entityLinked.Hit(_projectileDamage);
            }
            else _entityLinked.Hit(_projectileDamage);

            // Kill if no hp
            if (otherEntity.Hp <= 0)
            {
                otherEntity.Death();
            }
            _entityLinked = null;
        }
    }

    private void OnTriggerStay2D(Collider2D other)
    {
        if (_entityLinked != null && _projectileDamage == 0)
        {
            GameObject detected = other.gameObject;
            string tagToCheck =
                (_entityLinked.gameObject.tag == "Ally") ? "Opponent" : "Ally";

            if (detected.tag == tagToCheck)
            {
                Entity entity = other.GetComponent<Entity>();
                if (!detectedEntities.Contains(entity))
                {
                    detectedEntities.Add(entity);
                }
            }
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (_entityLinked != null)
        {
            GameObject detected = other.gameObject;
            string tagToCheck =
                (_entityLinked.gameObject.tag == "Ally") ? "Opponent" : "Ally";
            if (detected.tag == tagToCheck)
            {
                Entity entity = other.GetComponent<Entity>();
                if (detectedEntities.Contains(entity))
                {
                    detectedEntities.Remove(entity);
                }
            }
        }
    }

    public Entity GetClosest()
    {
        Entity closest = null;
        float closestDistance = Mathf.Infinity;
        Vector3 currentPosition = transform.position;

        foreach (Entity entity in detectedEntities)
        {
            if (entity == null) continue;
            float distance = Vector3.Distance(currentPosition, entity.transform.position);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closest = entity;
            }
        }

        return closest;
    }

    #region Resize
    private void ResizeCollider()
    {
        if (!EntityLinked) return;
        var multiplier = EntityLinked.RangeMultiplier;
        var size = _collider.size;
        size.x = detectionRange.x * multiplier.x;
        size.y = detectionRange.y * multiplier.y;
        _collider.size = size;

        var offset = _collider.offset;
        if (offset == Vector2.zero) return;

        offset.x = Mathf.Sign(offset.x) * size.x / 2;
        _collider.offset = offset;
    }
    #endregion

    // Getter and Setter
    public Entity EntityLinked
    {
        get { return _entityLinked; }
        set { _entityLinked = value; }
    }

    public int ProjectileDamage
    {
        get { return _projectileDamage; }
        set { _projectileDamage = value; }
    }

    public Rect DetectionRectangle
    {
        get
        {
            var coll = GetComponent<BoxCollider2D>();
            var rect = new Rect(coll.offset, coll.size);
            return rect;
        }
    }
    public List<Entity> DetectedEntities { get { return detectedEntities; } }
}
