using System.Collections.Generic;
using UnityEngine;

namespace Puzzy.MathUtils
{
    public static class VectorExtensions
    {
        public static List<int> Triangulate(this IList<Vector3> vertices)
        {
            var indices = new List<int>();

            int n = vertices.Count;
            if (n < 3)
            {
                Debug.LogError("Cannot triangulate fewer than 3 vertices");
                return indices;
            }
            
            bool isClosed = Vector3.Distance(vertices[0], vertices[n - 1]) < 0.0001f;
            if (isClosed)
            {
                n--;
            }
            
            List<int> vertexIndices = new List<int>();
            for (int i = 0; i < n; i++)
                vertexIndices.Add(i);

            float signedArea = 0;
            for (int i = 0; i < n; i++)
            {
                Vector2 current = vertices[i].ToXY();
                Vector2 next = vertices[(i + 1) % n].ToXY();
                signedArea += (current.x * next.y - next.x * current.y);
            }

            bool isCcw = signedArea > 0;
            if (!isCcw)
            {
                vertexIndices.Reverse();
            }

            int attempts = 0;
            while (vertexIndices.Count > 2 && attempts < n * n)
            {
                attempts++;
                bool foundEar = false;
                for (int i = 0; i < vertexIndices.Count; i++)
                {
                    int prev = vertexIndices[(i - 1 + vertexIndices.Count) % vertexIndices.Count];
                    int curr = vertexIndices[i];
                    int next = vertexIndices[(i + 1) % vertexIndices.Count];

                    Vector2 a = vertices[prev].ToXY();
                    Vector2 b = vertices[curr].ToXY();
                    Vector2 c = vertices[next].ToXY();

                    if (IsConvex(a, b, c))
                    {
                        bool isEar = true;
                        for (int j = 0; j < vertexIndices.Count; j++)
                        {
                            int testIndex = vertexIndices[j];
                            if (testIndex == prev || testIndex == curr || testIndex == next)
                                continue;
                            Vector2 p = vertices[testIndex].ToXY();
                            if (PointInTriangle(p, a, b, c))
                            {
                                isEar = false;
                                break;
                            }
                        }

                        if (isEar)
                        {
                            indices.Add(prev);
                            indices.Add(curr);
                            indices.Add(next);
                            vertexIndices.RemoveAt(i);
                            foundEar = true;
                            break;
                        }
                    }
                }

                if (!foundEar)
                {
                    Debug.LogWarning($"No ear found in attempt {attempts}");
                }
            }

            return indices;
        }

        // For a CCW polygon, a triangle (a,b,c) is convex if the cross product is positive.
        private static bool IsConvex(Vector2 a, Vector2 b, Vector2 c)
        {
            float cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
            return cross > 0;
        }

        private static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
        {
            float area = 0.5f * (-b.y * c.x + a.y * (-b.x + c.x) + a.x * (b.y - c.y) + b.x * c.y);
            float s = 1f / (2f * area) * (a.y * c.x - a.x * c.y + (c.y - a.y) * p.x + (a.x - c.x) * p.y);
            float t = 1f / (2f * area) * (a.x * b.y - a.y * b.x + (a.y - b.y) * p.x + (b.x - a.x) * p.y);
            return s >= 0 && t >= 0 && (s + t) <= 1;
        }

        // Convert Vector3 to Vector2 for triangulation in the XY plane.
        public static Vector2 ToXY(this Vector3 v)
        {
            return new Vector2(v.x, v.y);
        }

        public static Vector2 ToXZ(this Vector3 v)
        {
            return new Vector2(v.x, v.z);
        }

        public static Vector3 FromXY(this Vector2 v)
        {
            return new Vector3(v.x, v.y, 0f);
        }

        public static Vector3 FromXZ(this Vector2 v)
        {
            return new Vector3(v.x, 0f, v.y);
        }

        public static Vector3 FlipOnAxis(this Vector3 vector, Vector3 axis)
        {
            axis = axis.normalized;
            Vector3 projection = Vector3.Dot(vector, axis) * axis;
            return vector - 2 * projection;
        }
    }
}