

using System;
using System.Globalization;
using System.Numerics;
using System.Threading;
using ClumsyCore;
using ClumsyCore.Interfaces;
using ClumsyCore.Pilot;
using CommonUsage.Chassis;
using FundamentalLib;
using MDCSToolBox.Clumsy.Movements;
using MDCSToolBox.Clumsy.Pilot;
using MyParking.Shared;
using MultiWheelC.StateEstimation;

namespace MultiWheelC
{
    public abstract class InPlaceRotateTestBase : MovementTest
    {
        public float RelativeAngleDegrees; // 相对当前航向的旋转角度，逆时针为正。
        public int TrialNumber = 1; // 重复实验编号。
        public InPlaceRotationFeedbackMode FeedbackMode =
            InPlaceRotationFeedbackMode.DetourAbsoluteHeading;

        private DriveTask _task;
        private TrackingExperimentRecorder _recorder;
        private readonly string _trajectoryName;

        protected InPlaceRotateTestBase(
            float relativeAngleDegrees,
            string trajectoryName)
        {
            RelativeAngleDegrees =
                relativeAngleDegrees;
            _trajectoryName =
                trajectoryName;
        }

        // 从当前Detour航向开始，原地相对旋转指定角度并记录实验数据。
        public override void Test()
        {
            var config = PilotDefinition.Conf;

            if (float.IsNaN(RelativeAngleDegrees) ||
                float.IsInfinity(RelativeAngleDegrees) ||
                float.IsNaN(config.InPlaceRotateMaxSpeed) ||
                float.IsInfinity(config.InPlaceRotateMaxSpeed) ||
                config.InPlaceRotateMaxSpeed <= 0f ||
                float.IsNaN(config.InPlaceRotateMinimumSpeed) ||
                float.IsInfinity(config.InPlaceRotateMinimumSpeed) ||
                config.InPlaceRotateMinimumSpeed <= 0f ||
                config.InPlaceRotateMinimumSpeed >
                    config.InPlaceRotateMaxSpeed)
            {
                Console.WriteLine("原地旋转测试参数无效。");
                return;
            }

            var location = DetourInterface.getCartLocation();
            if (double.IsNaN(location.x) ||
                double.IsInfinity(location.x) ||
                double.IsNaN(location.y) ||
                double.IsInfinity(location.y) ||
                double.IsNaN(location.th) ||
                double.IsInfinity(location.th))
            {
                Console.WriteLine(
                    "Detour当前位姿无效，取消原地旋转测试。");
                return;
            }

            var chassis =
                PilotDefinition.Chassis as MultiWheelChassis;
            if (chassis == null)
            {
                Console.WriteLine(
                    "当前底盘不是MultiWheelChassis，无法执行原地旋转测试。");
                return;
            }

            var stateProvider =
                ParkingVehicleStateProviderFactory.Create(
                    chassis);
            if (!stateProvider.TryGetState(out _))
            {
                Console.WriteLine(
                    "无法读取原地旋转起点状态：" +
                    stateProvider.LastFailureReason);
                return;
            }

            var rotationCenter =
                new Vector2((float)location.x, (float)location.y);
            var targetWorldAngle =
                (float)AngleMath.NormalizeDegrees(
                    location.th + RelativeAngleDegrees);
            var movementAngleTarget =
                FeedbackMode ==
                    InPlaceRotationFeedbackMode
                        .RelativeWheelOdometry
                    ? RelativeAngleDegrees
                    : targetWorldAngle;

            Console.WriteLine(
                "原地自转实际参数：" +
                $"Kp={config.InPlaceRotateKp:F3}，" +
                $"Ki={config.InPlaceRotateKi:F3}，" +
                $"Kd={config.InPlaceRotateKd:F3}，" +
                $"到位误差={config.InPlaceRotateArriveDeg:F2}°，" +
                $"最小角速度={config.InPlaceRotateMinimumSpeed:F2}°/s，" +
                $"最大角速度={config.InPlaceRotateMaxSpeed:F2}°/s，" +
                $"角加速度={config.InPlaceRotateAcc:F2}°/s²，" +
                $"舵轮到位误差={config.InPlaceRotateWheelAlignDeg:F2}°，" +
                $"旋转超时={config.InPlaceRotateTimeoutSec:F1}s；" +
                $"起点航向={location.th:F2}°，" +
                $"目标航向={targetWorldAngle:F2}°，" +
                $"反馈模式={FeedbackMode}。");
            Console.WriteLine(
                "原地自转CSV保存目录：" +
                TrackingExperimentRecorder.DefaultOutputDirectory);

            _recorder = new TrackingExperimentRecorder(
                controllerName:
                    FeedbackMode ==
                        InPlaceRotationFeedbackMode
                            .RelativeWheelOdometry
                        ? "InPlaceRotateWheelOdometry"
                        : "InPlaceRotateFilteredPID",
                trajectoryName: _trajectoryName,
                trialNumber: TrialNumber,
                referenceStart: rotationCenter,
                referenceEnd: rotationCenter,
                referenceSpeed: 0f,
                referenceAngularSpeed:
                    (float)AngleMath.DegreesToRadians(
                        config.InPlaceRotateMaxSpeed),
                diagnosticChassis: chassis,
                diagnosticStateProvider: stateProvider);
            _recorder.Start();

            try
            {
                _task = new DriveTask(
                    new MultiWheelRotateInPlace
                    {
                        AngleTarget = movementAngleTarget,
                        FeedbackMode = FeedbackMode,
                        Chassis = chassis,
                        StateProvider = stateProvider,
                        CommandAngularSpeedObserver =
                            commandAngularSpeed =>
                                _recorder?.UpdateCommand(
                                    0f,
                                    (float)AngleMath.DegreesToRadians(
                                        commandAngularSpeed))
                    }.Get());

                _task.Wait();

                // 保留少量停止后的样本，用于观察角速度是否回到零。
                Thread.Sleep(300);
            }
            finally
            {
                _task?.Stop();
                _recorder?.UpdateCommand(0f, 0f);
                _recorder?.StopAndSave();
                _task = null;
                _recorder = null;
            }
        }

        // 停止原地旋转并保存当前已经采集的实验数据。
        public override void TestStop()
        {
            _task?.Stop();
            _recorder?.UpdateCommand(0f, 0f);
            _recorder?.StopAndSave();
        }

        // 读取并校验测试使用的有符号相对旋转角度。
        protected static bool TryReadRelativeAngleDegrees(
            out float relativeAngleDegrees)
        {
            var input = UI.GetInput(
                "输入相对旋转角度（deg，正数逆时针，负数顺时针，范围-180到180之间）：");

            if ((!float.TryParse(
                     input,
                     NumberStyles.Float,
                     CultureInfo.CurrentCulture,
                     out relativeAngleDegrees) &&
                 !float.TryParse(
                     input,
                     NumberStyles.Float,
                     CultureInfo.InvariantCulture,
                     out relativeAngleDegrees)) ||
                float.IsNaN(relativeAngleDegrees) ||
                float.IsInfinity(relativeAngleDegrees))
            {
                Console.WriteLine("旋转角度输入无效，测试已经取消。");
                return false;
            }

            if (Math.Abs(relativeAngleDegrees) < 1e-3f)
            {
                Console.WriteLine("旋转角度不能为0，测试已经取消。");
                return false;
            }

            if (Math.Abs(relativeAngleDegrees) >= 180f)
            {
                Console.WriteLine(
                    "输入角度必须满足-180° < angle < 180°。");
                return false;
            }

            return true;
        }

    }

    [MovementTest(name = "SendXYThSpeed：输入角度原地自转")]
    public sealed class TestRotateAngle :
        InPlaceRotateTestBase
    {
        public TestRotateAngle()
            : base(0f, "RotateCustomAngle")
        {
        }

        /// <summary>
        /// 读取相对旋转角度并按正值逆时针、负值顺时针执行原地自转。
        /// </summary>
        public override void Test()
        {
            if (!TryReadRelativeAngleDegrees(
                    out var relativeAngleDegrees))
            {
                return;
            }

            RelativeAngleDegrees = relativeAngleDegrees;
            base.Test();
        }
    }

    [MovementTest(name = "轮组里程计：输入角度原地相对自转")]
    public sealed class TestWheelOdometryRotateAngle :
        InPlaceRotateTestBase
    {
        public TestWheelOdometryRotateAngle()
            : base(0f, "RotateWheelOdometryCustomAngle")
        {
            FeedbackMode =
                InPlaceRotationFeedbackMode
                    .RelativeWheelOdometry;
        }

        /// <summary>
        /// 读取相对角度并仅用滤波后的轮组角速度积分完成自转。
        /// </summary>
        public override void Test()
        {
            if (!TryReadRelativeAngleDegrees(
                    out var relativeAngleDegrees))
            {
                return;
            }

            RelativeAngleDegrees = relativeAngleDegrees;
            base.Test();
        }
    }

}
