using UnityEngine;

namespace AsImpL.MathUtil
{
    /// <summary>
    /// Vertex structure used for triangulation.
    /// </summary>
    /// <seealso cref="Triangulation"/>
    public class Vertex
    {
        private Vertex prevVertex;
        private Vertex nextVertex;
        private float triangleArea;
        private bool triangleHasChanged;

        /// <summary>
        /// Coordinates in 3D space.
        /// </summary>
        public Vector3 Position { get; private set; }

        /// <summary>
        /// Saved index in the original list.
        /// </summary>
        public int OriginalIndex { get; private set; }

        /// <summary>
        /// Reference to the previous vertex this vertex is attached to.
        /// </summary>
        public Vertex PreviousVertex
        {
            get
            {
                return prevVertex;
            }

            set
            {
                triangleHasChanged = prevVertex != value;
                prevVertex = value;
            }
        }

        /// <summary>
        /// Reference to the next vertex this vertex is attached to.
        /// </summary>
        public Vertex NextVertex
        {
            get
            {
                return nextVertex;
            }

            set
            {
                triangleHasChanged = nextVertex != value;
                nextVertex = value;
            }
        }

        /// <summary>
        /// Area of the triangle this vertex belongs to,
        /// automatically computed each time the connected vertices change.
        /// </summary>
        public float TriangleArea
        {
            get
            {
                if (triangleHasChanged)
                {
                    ComputeTriangleArea();
                }
                return triangleArea;
            }
        }


        /// <summary>
        /// Construct a Vertex by defining its index in the original list and its position in 3D space.
        /// </summary>
        /// <param name="originalIndex">Index in the original list.</param>
        /// <param name="position">Position in 3D space.</param>
        public Vertex(int originalIndex, Vector3 position)
        {
            OriginalIndex = originalIndex;
            Position = position;
        }


        /// <summary>
        /// Get 2D position of this vertex on the plane defined by the given normal.
        /// </summary>
        /// <param name="planeNormal">Normal of the plane used to project 3D vertices in 2D.</param>
        /// <returns></returns>
        public Vector2 GetPosOnPlane(Vector3 planeNormal)
        {
            Quaternion planeRotation = new Quaternion();
            planeRotation.SetFromToRotation(planeNormal, Vector3.back);

            Vector3 projPos = planeRotation * Position;
            Vector2 pos_2d_xy = new Vector2(projPos.x, projPos.y);

            return pos_2d_xy;
        }


        private void ComputeTriangleArea()
        {
            Vector3 side1 = PreviousVertex.Position - Position;
            Vector3 side2 = NextVertex.Position - Position;
            Vector3 crossProd = Vector3.Cross(side1, side2);
            triangleArea = crossProd.magnitude / 2f;
        }

    }
}
