

using System;
using System.Numerics;
using System.Threading;
using ClumsyCore;
using ClumsyCore.Interfaces;
using ClumsyCore.Pilot;
using FundamentalLib;
using MDCSToolBox.Clumsy.Movements;
using MDCSToolBox.Clumsy.Pilot;
using MDCSToolBox.Commons.Controllers;
using MyParking.Shared;

namespace MultiWheelC
{
    public abstract class InPlaceRotateTestBase : MovementTest
    {
        public float RelativeAngleDegrees; // 相对当前航向的旋转角度，逆时针为正。
        public float MaxAngularSpeedDegreesPerSecond = 20f; // PID输出的最大角速度。
        public int TrialNumber = 1; // 重复实验编号。

        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()
        {
            if (float.IsNaN(RelativeAngleDegrees) ||
                float.IsInfinity(RelativeAngleDegrees) ||
                float.IsNaN(MaxAngularSpeedDegreesPerSecond) ||
                float.IsInfinity(MaxAngularSpeedDegreesPerSecond) ||
                MaxAngularSpeedDegreesPerSecond <= 0f)
            {
                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 rotationCenter =
                new Vector2((float)location.x, (float)location.y);
            var targetWorldAngle =
                (float)AngleMath.NormalizeDegrees(
                    location.th + RelativeAngleDegrees);

            _recorder = new TrackingExperimentRecorder(
                controllerName: "InPlaceRotatePID",
                trajectoryName: _trajectoryName,
                trialNumber: TrialNumber,
                referenceStart: rotationCenter,
                referenceEnd: rotationCenter,
                referenceSpeed: 0f,
                referenceAngularSpeed:
                    (float)AngleMath.DegreesToRadians(
                        MaxAngularSpeedDegreesPerSecond));
            _recorder.Start();

            try
            {
                _task = new DriveTask(
                    new MultiWheelRotateInPlace
                    {
                        // MultiWheelRotateInPlace接收世界坐标系绝对航向。
                        AngleTarget = targetWorldAngle,
                        PidparamsRead = () => new PIDParams
                        {
                            Kp =
                                PilotDefinition.Conf.InPlaceRotateKp,
                            Ki =
                                PilotDefinition.Conf.InPlaceRotateKi,
                            Kd =
                                PilotDefinition.Conf.InPlaceRotateKd,
                            DeadZone =
                                PilotDefinition.Conf
                                    .InPlaceRotateArriveDeg,
                            SpeedAccPerSec =
                                PilotDefinition.Conf.InPlaceRotateAcc,
                            OutputUpperThreshold =
                                MaxAngularSpeedDegreesPerSecond,
                            MaxI =
                                PilotDefinition.Conf.InPlaceRotateMaxI
                        },
                        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();
        }

    }

    [MovementTest(name = "SendXYThSpeed：原地自转90°")]
    public sealed class TestRotate90 :
        InPlaceRotateTestBase
    {
        public TestRotate90()
            : base(90f, "Rotate90")
        {
        }
    }

    [MovementTest(name = "SendXYThSpeed：原地自转180°")]
    public sealed class TestRotate180 :
        InPlaceRotateTestBase
    {
        public TestRotate180()
            : base(180f, "Rotate180")
        {
        }
    }
}
