using System;
using System.Drawing;
using System.Numerics;
using ClumsyCore;
using ClumsyCore.DTools;
using ClumsyCore.Interfaces;
using ClumsyCore.Pilot;
using CommonUsage.Chassis;
using MultiWheelC.Control.Execution;
using MultiWheelC.StateEstimation;
using MultiWheelC.Trajectory;
using MyParking.Shared;

namespace MultiWheelC
{
    /// <summary>
    /// 测试平滑左转轨迹、停车原地左转90°和再次直行的组合运动执行过程。
    /// </summary>
    [MovementTest(name = "新版控制器：直线-圆弧-折线组合测试")]
    public sealed class CompositeStopTurnGoTest : MovementTest
    {
        private const float MillimetersPerMeter = 1000f;

        private readonly Painter _painter =
            UI.GetPainter("CompositeStopTurnGoTest");

        private DriveTask _task;
        private TrackingExperimentRecorder _recorder;

        public int TrialNumber = 1; // 重复实验编号。
        public double StraightLengthMeters = 2.0; // 圆弧前后直线长度，单位m。
        public double TurnRadiusMeters = 2.0; // 平滑左转名义半径，单位m。
        public double TurnAngleDegrees = 90.0; // 含过渡段在内的总左转角度。
        public double CurvatureTransitionLengthMeters = 0.80; // 单侧过渡长度，单位m。
        public double InPlaceLeftTurnDegrees = 90.0; // 停车后的原地左转角度。
        public double FinalStraightLengthMeters = 4.5; // 自转后的直线长度，单位m。
        public double StraightMaximumSpeedMetersPerSecond = 0.40; // 直线限速。
        public double CurveMaximumSpeedMetersPerSecond = 0.30; // 转弯和过渡段限速。
        public double AccelerationMetersPerSecondSquared = 0.20; // 参考加速度。
        public double DecelerationMetersPerSecondSquared = 0.08; // 参考减速度。
        public double PointSpacingMeters = 0.02; // 离散轨迹点间距。

        /// <summary>
        /// 从当前Detour位姿构造完整计划并依次执行连续跟踪、原地自转和最终直线。
        /// </summary>
        public override void Test()
        {
            if (_task != null)
            {
                Console.WriteLine(
                    "曲线-停车自转-直线组合测试已经在运行。");
                return;
            }

            if (!TrajectoryExperimentInput
                    .TryReadLateralOffsetMeters(
                        out var lateralOffsetMeters))
            {
                return;
            }

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

            var stateProvider =
                ParkingVehicleStateProviderFactory.Create(
                    chassis);
            if (!stateProvider.TryGetState(
                    out var initialState))
            {
                Console.WriteLine(
                    "无法读取组合运动起点状态：" +
                    stateProvider.LastFailureReason);
                return;
            }

            var planStartPose =
                TrajectoryExperimentInput.OffsetPoseLaterally(
                    initialState.PoseInWorld,
                    lateralOffsetMeters);
            var firstTrajectory =
                TestTrajectoryFactory
                    .CreateStraightSmoothLeftTurnStraight(
                        planStartPose,
                        StraightLengthMeters,
                        TurnRadiusMeters,
                        AngleMath.DegreesToRadians(
                            TurnAngleDegrees),
                        CurvatureTransitionLengthMeters,
                        StraightMaximumSpeedMetersPerSecond,
                        CurveMaximumSpeedMetersPerSecond,
                        AccelerationMetersPerSecondSquared,
                        DecelerationMetersPerSecondSquared,
                        PointSpacingMeters);

            var firstStopPose =
                firstTrajectory.EndPoint.PoseInWorld;
            var finalStraightYawRadians =
                AngleMath.NormalizeRadians(
                    firstStopPose.YawRadians +
                    AngleMath.DegreesToRadians(
                        InPlaceLeftTurnDegrees));
            var finalStraightStartPose =
                new Pose2D(
                    firstStopPose.XMeters,
                    firstStopPose.YMeters,
                    finalStraightYawRadians);
            var finalTrajectory =
                TestTrajectoryFactory.CreateStraight(
                    finalStraightStartPose,
                    FinalStraightLengthMeters,
                    StraightMaximumSpeedMetersPerSecond,
                    AccelerationMetersPerSecondSquared,
                    DecelerationMetersPerSecondSquared,
                    PointSpacingMeters);

            DrawPlan(
                firstTrajectory,
                finalTrajectory,
                firstStopPose);

            var plan = new MotionPlanSegment[]
            {
                new TrackMotionPlanSegment(firstTrajectory)
                {
                    // 中间停车点允许后续原地转向和末段跟踪继续收敛位置误差。
                    FinishDistanceMeters = 0.05,
                    FinishSpeedMetersPerSecond = 0.03,
                    FinishHeadingToleranceRadians =
                        AngleMath.DegreesToRadians(3.0)
                },
                new RotateInPlaceMotionPlanSegment(
                    finalStraightYawRadians),
                new TrackMotionPlanSegment(finalTrajectory)
            };

            _recorder = new TrackingExperimentRecorder(
                controllerName: "NewStanleyPidComposite",
                trajectoryName:
                    TrajectoryExperimentInput.BuildTrajectoryName(
                        "SmoothTurnStopRotateStraight",
                        lateralOffsetMeters),
                trialNumber: TrialNumber,
                referenceStart: ToMillimeterVector(
                    firstTrajectory.StartPoint.PoseInWorld),
                referenceEnd: ToMillimeterVector(
                    finalTrajectory.EndPoint.PoseInWorld),
                referenceSpeed:
                    (float)StraightMaximumSpeedMetersPerSecond,
                sampleIntervalMs: 50,
                referenceAccelerationMetersPerSecondSquared:
                    (float)AccelerationMetersPerSecondSquared,
                referenceDecelerationMetersPerSecondSquared:
                    (float)DecelerationMetersPerSecondSquared,
                diagnosticChassis: chassis,
                diagnosticStateProvider: stateProvider);
            _recorder.Start();

            var controlPointRadiusMeters =
                chassis.ControlPointRadius /
                MillimetersPerMeter;
            var movement = new MotionPlanExecutor
            {
                Segments = plan,
                StateProvider = stateProvider,
                SegmentStarted = (index, segment) =>
                {
                    _recorder?.ClearControlReference();
                    _recorder?.ClearGcpCommand();
                    _recorder?.UpdateCommand(0f, 0f);
                    Console.WriteLine(
                        $"组合运动开始第{index + 1}段：" +
                        segment.GetType().Name);
                },
                TrackingCycleObserver = (index, controller) =>
                    RecordTrackingCycle(
                        index,
                        controller,
                        controlPointRadiusMeters,
                        stateProvider),
                RotationCommandObserver = (index, omega) =>
                    _recorder?.UpdateCommand(
                        0f,
                        (float)omega)
            };

            try
            {
                _task = new DriveTask(movement.Get());
                _task.Wait();
            }
            finally
            {
                _task?.Stop();
                _recorder?.UpdateCommand(0f, 0f);
                _recorder?.StopAndSave();
                _task = null;
                _recorder = null;
                _painter?.Clear();
            }
        }

        /// <summary>
        /// 停止组合运动、保存已有实验数据并清除计划轨迹。
        /// </summary>
        public override void TestStop()
        {
            _task?.Stop();
            _recorder?.UpdateCommand(0f, 0f);
            _recorder?.StopAndSave();
            _task = null;
            _recorder = null;
            _painter?.Clear();
        }

        /// <summary>
        /// 将两段轨迹和中间原地自转位置绘制到Clumsy界面。
        /// </summary>
        private void DrawPlan(
            Trajectory2D firstTrajectory,
            Trajectory2D finalTrajectory,
            Pose2D rotationPoseInWorld)
        {
            _painter.Clear();
            DrawTrajectory(
                firstTrajectory,
                Color.DeepSkyBlue);
            DrawTrajectory(
                finalTrajectory,
                Color.Gold);

            var rotationPoint =
                ToMillimeterVector(rotationPoseInWorld);
            _painter.DrawDot(
                Color.Magenta,
                rotationPoint.X,
                rotationPoint.Y,
                10f);
        }

        /// <summary>
        /// 绘制一段离散世界坐标系轨迹。
        /// </summary>
        private void DrawTrajectory(
            Trajectory2D trajectory,
            Color color)
        {
            for (var index = 0;
                 index < trajectory.Count;
                 index++)
            {
                var point = ToMillimeterVector(
                    trajectory[index].PoseInWorld);
                _painter.DrawDot(
                    color,
                    point.X,
                    point.Y,
                    3f);

                if (index == 0)
                {
                    continue;
                }

                var previousPoint = ToMillimeterVector(
                    trajectory[index - 1].PoseInWorld);
                _painter.DrawLine(
                    color,
                    previousPoint.X,
                    previousPoint.Y,
                    point.X,
                    point.Y,
                    width: 2);
            }
        }

        /// <summary>
        /// 将轨迹控制周期使用的状态、参考误差和最终GCP命令写入记录器。
        /// </summary>
        private void RecordTrackingCycle(
            int motionSegmentIndex,
            ParkingGeometricController controller,
            double controlPointRadiusMeters,
            WheelFeedbackVehicleStateProvider stateProvider)
        {
            if (controller.LastCycleTiming.HasValue)
            {
                _recorder?.RecordControlCycleTiming(
                    controller.LastCycleTiming.Value,
                    motionSegmentIndex,
                    controller.LastRequestedCommand,
                    controller.LastCommand);
            }

            if (controller.LastVehicleState.HasValue)
            {
                _recorder?.UpdateProcessedState(
                    controller.LastVehicleState.Value);
            }

            if (stateProvider.TryGetLatestVelocityDiagnostics(
                    out var detourBodyVx,
                    out var detourVelocityValid,
                    out var rawWheelBodyVx,
                    out var filteredWheelBodyVx,
                    out var rawWheelBodyVy,
                    out var filteredWheelBodyVy,
                    out var wheelVelocityValid))
            {
                _recorder?.UpdateVelocityDiagnostics(
                    detourBodyVx,
                    detourVelocityValid,
                    rawWheelBodyVx,
                    filteredWheelBodyVx,
                    rawWheelBodyVy,
                    filteredWheelBodyVy,
                    wheelVelocityValid);
            }

            if (!controller.LastCommand.HasValue)
            {
                return;
            }

            if (controller.LastProjection.HasValue &&
                controller.LastControlReferenceSpeedMetersPerSecond.HasValue)
            {
                var projection =
                    controller.LastProjection.Value;
                _recorder?.UpdateControlReference(
                    projection.ArcLengthMeters,
                    controller
                        .LastControlReferenceSpeedMetersPerSecond.Value,
                    projection.LateralErrorMeters,
                    projection.HeadingErrorRadians,
                    projection.DistanceToTrajectoryMeters,
                    projection.RemainingDistanceMeters,
                    controller.LastCurvaturePreviewDistanceMeters ?? 0.0,
                    controller.LastFeedforwardCurvaturePerMeter ??
                    projection.ReferencePoint.CurvaturePerMeter);
            }

            var requestedCommand =
                controller.LastRequestedCommand ??
                controller.LastCommand.Value;
            var command = controller.LastCommand.Value;
            _recorder?.UpdateGcpCommand(
                requestedCommand.FrontAngleRadians,
                requestedCommand.RearAngleRadians,
                command.FrontAngleRadians,
                command.RearAngleRadians);
            var curvaturePerMeter = Math.Tan(
                command.FrontAngleRadians) /
                controlPointRadiusMeters;
            var angularSpeedRadiansPerSecond =
                command.SpeedMetersPerSecond *
                curvaturePerMeter;
            _recorder?.UpdateCommand(
                (float)command.SpeedMetersPerSecond,
                (float)angularSpeedRadiansPerSecond);
        }

        /// <summary>
        /// 将米制世界位姿转换为Clumsy绘图和记录使用的毫米坐标。
        /// </summary>
        private static Vector2 ToMillimeterVector(
            Pose2D poseInWorld)
        {
            return new Vector2(
                (float)(poseInWorld.XMeters *
                    MillimetersPerMeter),
                (float)(poseInWorld.YMeters *
                    MillimetersPerMeter));
        }
    }
}
