using System;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;

namespace Unity.Splines.Examples
{
    [Serializable]
    public struct SplineLineRendererSettings
    {
        public float width;
        public Material material;
        [Range(16, 512)]
        public int subdivisions;
        public Color startColor, endColor;
    }

    [RequireComponent(typeof(SplineContainer))]
    public class SplineRenderer : MonoBehaviour
    {
        SplineContainer m_SplineContainer;
        Spline[] m_Splines;
        bool m_Dirty;
        Vector3[] m_Points;

        [SerializeField]
        SplineLineRendererSettings m_LineRendererSettings = new SplineLineRendererSettings() {
            width = .5f,
            subdivisions = 64
        };

        LineRenderer[] m_Lines;

        void Awake()
        {
            m_SplineContainer = GetComponent<SplineContainer>();
            m_Splines = m_SplineContainer.Splines.ToArray();
        }

        void OnEnable()
        {
            Spline.Changed += OnSplineChanged;
        }

        void OnDisable()
        {
            Spline.Changed -= OnSplineChanged;
        }

        void OnSplineChanged(Spline spline, int knotIndex, SplineModification modificationType)
        {
            for (int i = 0, c = m_Splines.Length; !m_Dirty && i < c; ++i)
                if (m_Splines[i] == spline)
                    m_Dirty = true;
        }

        void Update()
        {
            if (m_Lines?.Length != m_Splines.Length)
            {
                if (m_Lines != null)
                    foreach (var line in m_Lines) DestroyImmediate(line.gameObject);

                m_Lines = new LineRenderer[m_Splines.Length];

                for (int i = 0, c = m_Splines.Length; i < c; ++i)
                {
                    m_Lines[i] = new GameObject().AddComponent<LineRenderer>();
                    m_Lines[i].gameObject.name = $"SplineRenderer {i}";
                    m_Lines[i].transform.SetParent(transform, true);
                }

                m_Dirty = true;
            }

            // It's nice to be able to see resolution changes at runtime
            if (m_Points?.Length != m_LineRendererSettings.subdivisions)
            {
                m_Dirty = true;
                m_Points = new Vector3[m_LineRendererSettings.subdivisions];
                foreach (var line in m_Lines)
                    line.positionCount = m_LineRendererSettings.subdivisions;
            }

            if (!m_Dirty)
                return;

            m_Dirty = false;
            var trs = m_SplineContainer.transform.localToWorldMatrix;

            for (int s = 0, c = m_Splines.Length; s < c; ++s)
            {
                if (m_Splines[s].Count < 1)
                    continue;

                for (int i = 0; i < m_LineRendererSettings.subdivisions; i++)
                    m_Points[i] = math.transform(trs, m_Splines[s].EvaluatePosition(i / (m_LineRendererSettings.subdivisions - 1f)));

                m_Lines[s].widthCurve = new AnimationCurve(new Keyframe(0f, m_LineRendererSettings.width));
                m_Lines[s].startColor = m_LineRendererSettings.startColor;
                m_Lines[s].endColor = m_LineRendererSettings.endColor;
                m_Lines[s].material = m_LineRendererSettings.material;
                m_Lines[s].useWorldSpace = true;
                m_Lines[s].SetPositions(m_Points);
            }
        }
    }
}