using UnityEngine;

public class ZiplineSplineBuilder : MonoBehaviour
{
    [SerializeField]
    Transform startPoint;

    [SerializeField]
    Transform endPoint;

    [SerializeField]
    bool _autoBuildSpline = true;

    [Range(2, 10)]
    [SerializeField]
    int resolution = 5; // Number of segments/points along the catenary

    [Range(0.0f, 0.1f)]
    public float slack = 0.03f;

    SplineContainer _splineContainer;

    private void Awake()
    {
        _splineContainer = GetComponent<SplineContainer>();
        Build();
    }

    public void Build()
    {
        var spline = new Spline();
        _splineContainer.Spline = spline;
        Vector3 A = startPoint.position;
        Vector3 B = endPoint.position;
        Vector3 dir = B - A;
        float length = dir.magnitude;
        // --- Compute sag ---
        float heightDiff = B.y - A.y;
        float slope = Mathf.Clamp(heightDiff / length, -1f, 1f);
        float sag = length * slack;
        // Reduce sag for very steep lines (prevents weird shapes)
        sag *= Mathf.Clamp01(1f - Mathf.Abs(slope));
        // --- Generate multi-point catenary curve ---
        int pointCount = Mathf.Max(2, resolution);
        Vector3[] points = new Vector3[pointCount];
        for (int i = 0; i < pointCount; i++)
        {
            float t = (float)i / (pointCount - 1);
            Vector3 basePos = Vector3.Lerp(A, B, t);
            // Parabolic sag curve weighted towards the middle (sin(pi * t) peaks at 1 in the center)
            float parabolaFactor = Mathf.Sin(t * Mathf.PI);
            // Bias sag slightly toward the lower endpoint
            float bias = Mathf.Lerp(1f, (heightDiff > 0 ? 0.8f : 1.2f), Mathf.Abs(slope));
            float localSag = sag * parabolaFactor * bias;
            points[i] = basePos + Vector3.down * localSag;
        }
        // Force exact start and end positions to match transforms
        points[0] = A;
        points[pointCount - 1] = B;
        // --- Build Bezier Knots with smooth tangents ---
        for (int i = 0; i < pointCount; i++)
        {
            Vector3 currentPos = points[i];
            Vector3 tanIn = Vector3.zero;
            Vector3 tanOut = Vector3.zero;
            if (i > 0 && i < pointCount - 1)
            {
                // Smooth interior tangents pointing towards neighbors
                Vector3 prevPos = points[i - 1];
                Vector3 nextPos = points[i + 1];
                Vector3 tangentDir = (nextPos - prevPos) * 0.5f;
                tanIn = -tangentDir * 0.5f;
                tanOut = tangentDir * 0.5f;
            }
            else if (i == 0)
            {
                // Start tangent points smoothly toward the second point
                tanOut = (points[1] - A) * 0.5f;
            }
            else if (i == pointCount - 1)
            {
                // End tangent points smoothly from the second-to-last point
                tanIn = (B - points[pointCount - 2]) * -0.5f;
            }
            spline.Add(new BezierKnot(currentPos, tanIn, tanOut));
        }
        spline.Closed = false;
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Rebuild Spline"))
        {
            if (_splineContainer == null)
                _splineContainer = GetComponent<SplineContainer>();
            if (startPoint != null && endPoint != null)
            {
                Debug.Log("Rebuilding spline");
                Build();
            }
        }
    }

    #if UNITY_EDITOR
    private void OnValidate()
    {
        if (!_autoBuildSpline)
            return;
        if (_splineContainer == null)
            _splineContainer = GetComponent<SplineContainer>();
        if (startPoint != null && endPoint != null)
        {
            Debug.Log("Rebuilding spline");
            Build();
        }
    }
    #endif
}