using UnityEngine;

namespace NBF
{
    [RequireComponent(typeof(LineRenderer))]
    public class FishingRope : MonoBehaviour
    {
        [Header("Demo Parameters")] [SerializeField, Min(0)]
        float mouseOffset = 10f;

        [Header("Verlet Parameters")] [SerializeField]
        float nodeDistance = 0.35f;

        [SerializeField] float nodeColliderRadius = 0.2f;
        [SerializeField] float gravityStrength = 2;
        [SerializeField, Range(1, 500)] int totalNodes = 100;
        [SerializeField, Range(0, 1)] float velocityDampen = 0.95f;
        [SerializeField, Range(0, 0.99f)] float stiffness = 0.8f;
        [SerializeField, Range(1, 10)] int iterateCollisionsEvery = 1;
        [SerializeField, Range(1, 200)] int iterations = 10;
        [SerializeField] int colliderBufferSize = 1;

        [Header("Line Renderer")] [SerializeField]
        float ropeWidth = 0.02f;

        // 私有变量
        Camera cam;
        Vector3 gravity;
        Vector3 startLock;
        Vector3 endLock;
        bool isStartLocked = false;
        bool isEndLocked = false;

        // 数组和缓存
        Vector3[] currentNodePositions;
        Vector3[] previousNodePositions;
        Collider[] colliderHitBuffer;
        LineRenderer lineRenderer;
        GameObject nodeTester;
        SphereCollider nodeCollider;

        void Awake()
        {
            // 初始化数组
            currentNodePositions = new Vector3[totalNodes];
            previousNodePositions = new Vector3[totalNodes];
            colliderHitBuffer = new Collider[colliderBufferSize];

            // 获取组件引用
            lineRenderer = GetComponent<LineRenderer>();
            cam = Camera.main;
            gravity = new Vector3(0, -gravityStrength, 0);

            // 初始化节点测试器
            nodeTester = new GameObject("Node Tester");
            nodeTester.layer = 8;
            nodeCollider = nodeTester.AddComponent<SphereCollider>();
            nodeCollider.radius = nodeColliderRadius;

            // 初始化节点位置
            Vector3 startPos = transform.position;
            for (int i = 0; i < totalNodes; i++)
            {
                currentNodePositions[i] = startPos;
                previousNodePositions[i] = startPos;
                startPos.y -= nodeDistance;
            }

            // 设置线渲染器
            lineRenderer.startWidth = ropeWidth;
            lineRenderer.endWidth = ropeWidth;
        }

        void Update()
        {
            // 处理鼠标输入
            if (Input.GetMouseButtonDown(0))
            {
                if (!isStartLocked)
                {
                    isStartLocked = true;
                    startLock = GetMouseWorldPosition();
                }
                else if (!isEndLocked)
                {
                    isEndLocked = true;
                    endLock = GetMouseWorldPosition();
                }
            }
            else if (!isStartLocked)
            {
                startLock = GetMouseWorldPosition();
            }
            else if (isStartLocked && !isEndLocked)
            {
                endLock = GetMouseWorldPosition();
            }

            DrawRope();
        }

        void FixedUpdate()
        {
            Simulate();

            for (int i = 0; i < iterations; i++)
            {
                ApplyConstraint();

                // 减少碰撞检测频率
                if (i % (iterateCollisionsEvery + 1) == 0)
                {
                    AdjustCollisions();
                }
            }
        }

        Vector3 GetMouseWorldPosition()
        {
            return cam.ScreenToWorldPoint(Input.mousePosition + new Vector3(0, 0, mouseOffset));
        }

        void Simulate()
        {
            float fixedDt = Time.fixedDeltaTime;
            for (int i = 0; i < totalNodes; i++)
            {
                // 计算并应用速度
                Vector3 velocity = (currentNodePositions[i] - previousNodePositions[i]) * velocityDampen;
                previousNodePositions[i] = currentNodePositions[i];
                currentNodePositions[i] += velocity + gravity * fixedDt;
            }
        }

        void ApplyConstraint()
        {
            // 锁定端点
            currentNodePositions[0] = startLock;
            if (isStartLocked && isEndLocked)
            {
                currentNodePositions[totalNodes - 1] = endLock;
            }

            // 预计算所有常用值
            float halfStiffness = 0.5f * stiffness;
            float sqrNodeDistance = nodeDistance * nodeDistance;
            int nodeCountMinusOne = totalNodes - 1;

            for (int i = 0; i < nodeCountMinusOne; i++)
            {
                Vector3 node1 = currentNodePositions[i];
                Vector3 node2 = currentNodePositions[i + 1];
                Vector3 diff = node1 - node2;

                float sqrDistance = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;

                // 只有当距离差异超过一定阈值时才调整
                if (Mathf.Abs(sqrDistance - sqrNodeDistance) > 0.001f)
                {
                    float distance = Mathf.Sqrt(sqrDistance);
                    float difference = nodeDistance - distance;
                    Vector3 direction = diff / distance; // 比 normalized 更快

                    Vector3 adjustment = direction * (difference * halfStiffness);

                    currentNodePositions[i] += adjustment;
                    currentNodePositions[i + 1] -= adjustment;
                }
            }
        }

        void AdjustCollisions()
        {
            for (int i = 1; i < totalNodes; i += 2) // 跳过更多节点减少计算
            {
                int hits = Physics.OverlapSphereNonAlloc(
                    currentNodePositions[i],
                    nodeColliderRadius,
                    colliderHitBuffer,
                    ~(1 << 8)); // 忽略特定层

                for (int n = 0; n < hits; n++)
                {
                    if (Physics.ComputePenetration(
                            nodeCollider,
                            currentNodePositions[i],
                            Quaternion.identity,
                            colliderHitBuffer[n],
                            colliderHitBuffer[n].transform.position,
                            colliderHitBuffer[n].transform.rotation,
                            out Vector3 direction,
                            out float distance))
                    {
                        currentNodePositions[i] += direction * distance;
                    }
                }
            }
        }

        void DrawRope()
        {
            // 直接使用currentNodePositions数组避免额外拷贝
            lineRenderer.positionCount = totalNodes;
            lineRenderer.SetPositions(currentNodePositions);
        }

        void OnDestroy()
        {
            if (nodeTester != null)
            {
                Destroy(nodeTester);
            }
        }
    }
}