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

public class FieldOfView : MonoBehaviour
{
    [SerializeField] private LayerMask layerMask;
    [SerializeField] private int rayCount = 50;
    [SerializeField] private float viewDistSmall = 3f;
    [SerializeField] private float fovSmall = 90f;
    [SerializeField] private float viewDistBig = 5f;
    [SerializeField] private float fovBig = 50f;
    [SerializeField] private float fadeSpeed = 5f;
    [SerializeField] private Material fovMaterial;
    [SerializeField] private Transform player;
    [SerializeField] private const float interactableSeenDelay = 3f; // time in seconds that the interactable has to exist before grandma can see it

    private float viewDist;
    private float fov;

    private Mesh mesh;
    private Vector3 origin;
    private float startingAngle;
    private float initialMaterialAlpha;

    private float currentAlpha = 0f;
    private float targetAlpha = 0f;
    private Material matInstance;

    private float sanityCooldown = 0f;
    private float sanityCooldownTime = 2f;
    private float sanityExtinguisherCooldown = 0f;
    private float sanityExtinguisherCooldownTime = 4f;

    
    // TODO: using this for noticing when timer is over, however we could decouple this from GrandmaAI logic
    [SerializeField] private GrandmaAI grandma;
    
    private static Vector3 GetVectorFromAngle(float angle)
    {
        float angleRad = angle * (Mathf.PI / 180f);
        return new Vector3(Mathf.Cos(angleRad), Mathf.Sin(angleRad));
    }

    public static float GetAngleFromVectorFloat(Vector3 dir)
    {
        dir = dir.normalized;
        float n = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        if (n < 0) n += 360;
        return n;
    }

    private void Start()
    {
        mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;
        origin = Vector3.zero;
        SetBigVision();

        if (grandma == null)
        {
            grandma = FindAnyObjectByType<GrandmaAI>(); // there should be only one in the scene
            Debug.LogWarning("Grandma was not assigned to the FOV script. Assigning it via script.");
        }

        if (fovMaterial != null)
        {
            matInstance = Instantiate(fovMaterial);
            GetComponent<MeshRenderer>().material = matInstance;

            initialMaterialAlpha = matInstance.color.a;
            Debug.Log("Init alpha: " + initialMaterialAlpha);

            Color c = matInstance.color;
            matInstance.color = new Color(c.r, c.g, c.b, 0f);
        }

    }

    private void LateUpdate()
    {
        sanityCooldown -= Time.deltaTime;
        sanityExtinguisherCooldown -= Time.deltaTime;
        if (matInstance == null) return;

        bool suppressLogic = grandma.finalActionTriggered;
        // when final action is triggered, FOV should fade out and stop affecting sanity
        if (!suppressLogic)
        {
            targetAlpha = PlayerInteraction.Instance != null &&
                        PlayerInteraction.Instance.IsInteracting
                ? initialMaterialAlpha
                : 0f;
        }
        else
        {
            targetAlpha = 0f;
        }

        currentAlpha = Mathf.MoveTowards(currentAlpha, targetAlpha, fadeSpeed * Time.deltaTime);

        Color c = matInstance.color;
        matInstance.color = new Color(c.r, c.g, c.b, currentAlpha);

        GetComponent<MeshRenderer>().enabled = currentAlpha > 0f;

        if (player != null)
        {
            SetOrigin(player.position + new Vector3(-0.25f, 0.2f, 0f));
        }


        UpdateMesh();
    }
    private void UpdateMesh()
    {
        float angle = startingAngle;
        float angleIncrease = fov / rayCount;

        Vector3[] vertices = new Vector3[rayCount + 2];
        Vector2[] uv = new Vector2[vertices.Length];
        int[] triangles = new int[rayCount * 3];

        vertices[0] = origin;

        int vertexIndex = 1;
        int triangleIndex = 0;

        bool sanityAppliedThisFrame = false;

        for (int i = 0; i <= rayCount; i++)
        {
            //default vertex
            Vector3 vertex = origin + GetVectorFromAngle(angle) * viewDist;

            RaycastHit2D raycastHit2D =
                Physics2D.Raycast(origin, GetVectorFromAngle(angle), viewDist, layerMask);

            if (raycastHit2D.collider != null)
            {
                if (raycastHit2D.collider.CompareTag("Interactable"))
                {
                    if (!sanityAppliedThisFrame && sanityCooldown <= 0f)
                    {
                        Hazard hazard = raycastHit2D.collider.GetComponent<Hazard>();
                        if (hazard != null && hazard.isDragging)
                        {
                            HUDManager.Instance.AddSanity(-1);
                            sanityCooldown = sanityCooldownTime;
                            sanityAppliedThisFrame = true;

                            hazard.OnDragEnd();

                            ActionAdvertiser advertiser = raycastHit2D.collider.GetComponent<ActionAdvertiser>();
                            if (advertiser != null)
                                grandma.NotifyInteractableSeen(raycastHit2D.collider.transform.position, advertiser);
                        }
                        else
                        {
                            ActionAdvertiser advertiser = raycastHit2D.collider.GetComponent<ActionAdvertiser>();
                            if (advertiser != null && advertiser.CanBeSeenByGrandma(interactableSeenDelay))
                            {
                                grandma.NotifyInteractableSeen(raycastHit2D.collider.transform.position, advertiser);
                            }
                        }
                    }
                }
                else if (raycastHit2D.collider.CompareTag("Fire"))
                {
                    ActionAdvertiser advertiser = raycastHit2D.collider.GetComponent<ActionAdvertiser>();
                    if (advertiser != null &&
                        advertiser.CanBeSeenByGrandma(interactableSeenDelay))
                    {
                        grandma.NotifyInteractableSeen(raycastHit2D.collider.transform.position, advertiser);
                    }
                }
                else if (raycastHit2D.collider.CompareTag("Extinguisher"))
                {
                    if (!sanityAppliedThisFrame && sanityExtinguisherCooldown <= 0f)
                    {
                        Hazard hazard = raycastHit2D.collider.GetComponent<Hazard>();
                        if (hazard != null && hazard.onMouseDown)
                        {
                            HUDManager.Instance.AddSanity(-1);
                            sanityExtinguisherCooldown = sanityExtinguisherCooldownTime;
                            sanityAppliedThisFrame = true;
                            hazard.OnDragEnd();
                        }
                        else if(hazard != null && !ExtinguisherHouse.Instance.IsTargetInside2D())
                        {
                            HUDManager.Instance.AddSanity(-1);
                            sanityExtinguisherCooldown = sanityExtinguisherCooldownTime;
                            sanityAppliedThisFrame = true;
                        }
                    }
                }
                else
                {
                    // solid geometry
                    vertex = raycastHit2D.point;
                }
            }

            vertices[vertexIndex] = vertex;

            if (i > 0)
            {
                triangles[triangleIndex] = 0;
                triangles[triangleIndex + 1] = vertexIndex - 1;
                triangles[triangleIndex + 2] = vertexIndex;
                triangleIndex += 3;
            }

            vertexIndex++;
            angle -= angleIncrease;
        }

        mesh.vertices = vertices;
        mesh.uv = uv;
        mesh.triangles = triangles;
    }

    public void SetOrigin(Vector3 origin)
    {
        this.origin = origin;
    }

    public void SetAimDirection(Vector3 aimDirection)
    {
        startingAngle = GetAngleFromVectorFloat(aimDirection) + fov / 2f;
    }

    public void SetBigVision()
    {
        viewDist = viewDistBig;
        fov = fovBig;
    }

    public void SetSmallVision()
    {
        viewDist = viewDistSmall;
        fov = fovSmall;
    }
}

