using UnityEngine;
using WaveHarmonic.Crest;

namespace NBF
{
    public sealed class BobberFloating : MonoBehaviour
    {
        public WaterRenderer _water;

        [SerializeField] Rigidbody _RigidBody;

        [Tooltip("要瞄准哪一层水的碰撞层。")] [SerializeField]
        CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;

        [Header("浮力")]
        [Header("力强度")]
        [Tooltip("对于探测器而言，大致为 100 比 1 的质量与力的比例，以使质心保持在表面附近。对于“对齐法线”，默认值适用于具有默认刚体的默认球体。")]
        [SerializeField]
        float _BuoyancyForceStrength = 10f;

        [Header("扭矩强度")] [Tooltip("使船体方向与水的法线方向一致时所施加扭矩的大小。")] [SerializeField]
        float _BuoyancyTorqueStrength = 8f;

        [Header("最大力矩")] [Tooltip("将浮力值固定在此数值上。\n\n适用于处理完全浸没的物体。")] [SerializeField]
        float _MaximumBuoyancyForce = 100f;

        [Header("高度偏移")] [Tooltip("从变换中心到船体底部的高度偏移（如果存在）。\n\n默认值适用于默认球体。该值无需精确测量从中心到底部的距离。")] [SerializeField]
        float _CenterToBottomOffset = -1f;

        [Tooltip("顺着波浪 “冲浪” 的近似流体动力学效果。")] [Range(0, 1)] [SerializeField]
        float _AccelerateDownhill;


        [Header("拖拽")] [Tooltip("在水中时使用拖拽功能。\n将此属性添加到刚体所声明的拖拽力上。")] [SerializeField]
        Vector3 _Drag = new(2f, 3f, 1f);

        [Tooltip("在水中会产生旋转阻力。\n\n将此阻力添加到刚体上已声明的旋转阻力值之上。")] [SerializeField]
        float _AngularDrag = 0.2f;

        [Tooltip("施加拉力的位置的垂直偏移量。")] [SerializeField]
        float _ForceHeightOffset;


        [Header("波响应")] [Tooltip("用于物理计算的物体宽度。\n\n此值越大，波响应的滤波效果和平滑程度就越高。如果无法对较大波长进行滤波，则应增加 LOD 级别。")] [SerializeField]
        float _ObjectWidth = 3f;


        // Debug
        [Space(10)] [SerializeField] DebugFields _Debug = new();

        [System.Serializable]
        sealed class DebugFields
        {
            [SerializeField] internal bool _DrawQueries = false;
        }

        internal const string k_FixedUpdateMarker = "Crest.FloatingObject.FixedUpdate";

        static Unity.Profiling.ProfilerMarker s_FixedUpdateMarker = new(k_FixedUpdateMarker);

        /// <summary>
        /// 这个物体的任何部分是否浸泡在水中？
        /// </summary>
        public bool InWater { get; private set; }

        readonly SampleFlowHelper _SampleFlowHelper = new();

        Vector3[] _QueryPoints;
        Vector3[] _QueryResultDisplacements;
        Vector3[] _QueryResultVelocities;
        Vector3[] _QueryResultNormal;

        // internal FloatingObjectProbe[] _Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };

        public FloatingObjectProbe[] Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };


        private void Start()
        {
            if (_RigidBody == null) TryGetComponent(out _RigidBody);

            var points = Probe;
            // Advanced 还需要为中心增设一个位置。
            var length = points.Length;
            _QueryPoints = new Vector3[length];
            _QueryResultDisplacements = new Vector3[length];
            _QueryResultVelocities = new Vector3[length];
            _QueryResultNormal = new Vector3[length];
        }

        private void FixedUpdate()
        {
            s_FixedUpdateMarker.Begin(this);

            var points = Probe;
            
            // 查询
            var collisions = _water.AnimatedWavesLod.Provider;
            
            // 更新查询点。
            for (var i = 0; i < points.Length; i++)
            {
                var point = points[i];
                _QueryPoints[i] =
                    transform.TransformPoint(point._Position + new Vector3(0, _RigidBody.centerOfMass.y, 0));
            }
            
            _QueryPoints[^1] = transform.position + new Vector3(0, _RigidBody.centerOfMass.y, 0);
            
            collisions.Query(GetHashCode(), _ObjectWidth, _QueryPoints, _QueryResultDisplacements,
                _QueryResultNormal, _QueryResultVelocities, _Layer);
            
            
            //我们可以将表面速度过滤为最近两帧中的较小值。
            //存在一种极端情况：当波长被开启 / 关闭时，会产生单帧速度尖峰——
            //因为此时水面确实会发生极快的运动。
            var surfaceVelocity = _QueryResultVelocities[^1];
            _SampleFlowHelper.Sample(transform.position, out var surfaceFlow, minimumLength: _ObjectWidth);
            surfaceVelocity += new Vector3(surfaceFlow.x, 0, surfaceFlow.y);
            
            if (_Debug._DrawQueries)
            {
                Debug.DrawLine(transform.position + 5f * Vector3.up,
                    transform.position + 5f * Vector3.up + surfaceVelocity, new(1, 1, 1, 0.6f));
            }


            var height = _QueryResultDisplacements[0].y + _water.SeaLevel;
            var bottomDepth = height - transform.position.y - _CenterToBottomOffset;
            var normal = _QueryResultNormal[0];

            if (_Debug._DrawQueries)
            {
                var surfPos = transform.position;
                surfPos.y = height;
                DebugUtility.DrawCross(Debug.DrawLine, surfPos, normal, 1f, Color.red);
            }

            InWater = bottomDepth > 0f;
            if (!InWater)
            {
                s_FixedUpdateMarker.End();
                return;
            }

            var buoyancy = _BuoyancyForceStrength * bottomDepth * bottomDepth * bottomDepth *
                           -Physics.gravity.normalized;
            if (_MaximumBuoyancyForce < Mathf.Infinity)
            {
                buoyancy = Vector3.ClampMagnitude(buoyancy, _MaximumBuoyancyForce);
            }

            _RigidBody.AddForce(buoyancy, ForceMode.Acceleration);

            // 在水面上滑行的近似流体动力学
            if (_AccelerateDownhill > 0f)
            {
                _RigidBody.AddForce(_AccelerateDownhill * -Physics.gravity.y * new Vector3(normal.x, 0f, normal.z),
                    ForceMode.Acceleration);
            }


            // // 朝向
            // // 与水面垂直。默认使用一个垂直方向，但也可以使用单独的垂直方向。
            // // 根据船的长度与宽度的比例。这会根据船只的不同而产生不同的旋转效果。
            // // dimensions.
            // {
            //     var normalLatitudinal = normal;
            //
            //     if (_Debug._DrawQueries)
            //         Debug.DrawLine(transform.position, transform.position + 5f * normalLatitudinal, Color.green);
            //
            //     var torqueWidth = Vector3.Cross(transform.up, normalLatitudinal);
            //     _RigidBody.AddTorque(torqueWidth * _BuoyancyTorqueStrength, ForceMode.Acceleration);
            //     _RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity);
            // }
            
            // 朝向（改进的扭矩版本）
            {
                var normalLatitudinal = normal;
            
                if (_Debug._DrawQueries)
                    Debug.DrawLine(transform.position, transform.position + 5f * normalLatitudinal, Color.green);
            
                // 限制扭矩强度基于物体质量
                var massFactor = Mathf.Clamp(_RigidBody.mass / 0.1f, 0.1f, 1f); // 质量越小，扭矩越弱
                var adjustedTorqueStrength = _BuoyancyTorqueStrength * massFactor;
                
                var torqueWidth = Vector3.Cross(transform.up, normalLatitudinal);
                
                // 添加最大扭矩限制
                var maxTorque = 5f; // 根据需要调整
                var finalTorque = Vector3.ClampMagnitude(torqueWidth * adjustedTorqueStrength, maxTorque);
                
                _RigidBody.AddTorque(finalTorque, ForceMode.Acceleration);
                _RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity, ForceMode.Acceleration);
            }


            
            // 相对于水进行拖拽操作
            if (_Drag != Vector3.zero)
            {
                var velocityRelativeToWater = _RigidBody.linearVelocity - surfaceVelocity;
            
                var forcePosition = _RigidBody.worldCenterOfMass + _ForceHeightOffset * Vector3.up;
                _RigidBody.AddForceAtPosition(
                    _Drag.x * Vector3.Dot(transform.right, -velocityRelativeToWater) * transform.right, forcePosition,
                    ForceMode.Acceleration);
                _RigidBody.AddForceAtPosition(_Drag.y * Vector3.Dot(Vector3.up, -velocityRelativeToWater) * Vector3.up,
                    forcePosition, ForceMode.Acceleration);
                _RigidBody.AddForceAtPosition(
                    _Drag.z * Vector3.Dot(transform.forward, -velocityRelativeToWater) * transform.forward,
                    forcePosition, ForceMode.Acceleration);
            }

            s_FixedUpdateMarker.End();
        }
    }

    static class DebugUtility
    {
        public delegate void DrawLine(Vector3 position, Vector3 up, Color color, float duration);

        public static void DrawCross(DrawLine draw, Vector3 position, float r, Color color, float duration = 0f)
        {
            draw(position - Vector3.up * r, position + Vector3.up * r, color, duration);
            draw(position - Vector3.right * r, position + Vector3.right * r, color, duration);
            draw(position - Vector3.forward * r, position + Vector3.forward * r, color, duration);
        }

        public static void DrawCross(DrawLine draw, Vector3 position, Vector3 up, float r, Color color,
            float duration = 0f)
        {
            up.Normalize();
            var right = Vector3.Normalize(Vector3.Cross(up, Vector3.forward));
            var forward = Vector3.Cross(up, right);
            draw(position - up * r, position + up * r, color, duration);
            draw(position - right * r, position + right * r, color, duration);
            draw(position - forward * r, position + forward * r, color, duration);
        }
    }
}