using ClumsyCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Text;
using System.Threading;
using MyParking.Shared;
using MultiWheelC.StateEstimation;

namespace MultiWheelC
{
    // C层实验数据：保存一个采样时刻的定位与控制命令。
    public sealed class TrackingSample
    {
        public double ElapsedSeconds;

        // Detour位置单位为mm，航向单位为deg。
        public double DetourX;
        public double DetourY;
        public double DetourTheta;

        // 车体速度单位为m/s，角速度统一使用rad/s。
        public float CommandSpeed;
        public float CommandVx;
        public float CommandVy;
        public float CommandAngularSpeed;

        // 新版状态估计统一使用SI单位；无有效控制器状态时HasProcessedState为false。
        public bool HasProcessedState;
        public double StateTimestampSeconds;
        public double StateXmeters;
        public double StateYMeters;
        public double StateYawRadians;
        public double StateWorldVxMetersPerSecond;
        public double StateWorldVyMetersPerSecond;
        public double StateBodyVxMetersPerSecond;
        public double StateBodyVyMetersPerSecond;
        public double StateAngularSpeedRadiansPerSecond;
        public bool StateVelocityEstimateValid;
        public bool HasControlReference;
        public double ControlReferenceArcLengthMeters;
        public double ControlReferenceSpeedMetersPerSecond;
        public double ControlLateralErrorMeters;
        public double ControlHeadingErrorRadians;
        public double ControlDistanceToTrajectoryMeters;
        public double ControlRemainingDistanceMeters;
    }

    // C层实验工具：统一采集并保存轨迹跟踪实验数据。
    public sealed class TrackingExperimentRecorder
    {
        private readonly string _controllerName;
        private readonly string _trajectoryName;
        private readonly int _trialNumber;
        private readonly Vector2 _referenceStart;
        private readonly Vector2 _referenceEnd;
        private readonly float _referenceSpeed;
        private readonly float _referenceAngularSpeed;
        private readonly float _referenceMotionFrameYawDegrees;
        private readonly float _referenceAccelerationMetersPerSecondSquared;
        private readonly float _referenceDecelerationMetersPerSecondSquared;
        private readonly int _sampleIntervalMs;

        private readonly List<TrackingSample> _samples =
            new List<TrackingSample>();

        private readonly object _sampleSyncRoot =
            new object();

        private readonly object _commandSyncRoot =
            new object();

        private readonly object _stateSyncRoot =
            new object();

        private readonly Stopwatch _stopwatch =
            new Stopwatch();

        private Thread _worker;
        private volatile bool _running;
        private int _started;
        private int _saved;

        private bool _hasExternalCommand;
        private float _externalCommandSpeed;
        private float _externalCommandVx;
        private float _externalCommandVy;
        private float _externalCommandAngularSpeed;
        private VehicleState? _latestProcessedState;
        private bool _hasControlReference;
        private double _controlReferenceArcLengthMeters;
        private double _controlReferenceSpeedMetersPerSecond;
        private double _controlLateralErrorMeters;
        private double _controlHeadingErrorRadians;
        private double _controlDistanceToTrajectoryMeters;
        private double _controlRemainingDistanceMeters;

        public TrackingExperimentRecorder(
            string controllerName,
            string trajectoryName,
            int trialNumber,
            Vector2 referenceStart,
            Vector2 referenceEnd,
            float referenceSpeed,
            float referenceAngularSpeed = 0f,
            int sampleIntervalMs = 50,
            float referenceMotionFrameYawDegrees = 0f,
            float referenceAccelerationMetersPerSecondSquared = 0f,
            float referenceDecelerationMetersPerSecondSquared = 0f)
        {
            if (string.IsNullOrWhiteSpace(controllerName))
                throw new ArgumentException(
                    "控制器名称不能为空。",
                    nameof(controllerName));

            if (string.IsNullOrWhiteSpace(trajectoryName))
                throw new ArgumentException(
                    "轨迹名称不能为空。",
                    nameof(trajectoryName));

            if (sampleIntervalMs <= 0)
                throw new ArgumentOutOfRangeException(
                    nameof(sampleIntervalMs),
                    "采样周期必须大于零。");

            _controllerName = controllerName;
            _trajectoryName = trajectoryName;
            _trialNumber = trialNumber;
            _referenceStart = referenceStart;
            _referenceEnd = referenceEnd;
            _referenceSpeed = referenceSpeed;
            _referenceAngularSpeed = referenceAngularSpeed;
            _referenceMotionFrameYawDegrees =
                referenceMotionFrameYawDegrees;
            _referenceAccelerationMetersPerSecondSquared =
                referenceAccelerationMetersPerSecondSquared;
            _referenceDecelerationMetersPerSecondSquared =
                referenceDecelerationMetersPerSecondSquared;
            _sampleIntervalMs = sampleIntervalMs;
        }

        // 保存成功后的CSV绝对路径；尚未保存时为空。
        public string SavedFilePath { get; private set; }

        // 启动后台采样线程。
        public void Start()
        {
            if (Interlocked.Exchange(ref _started, 1) != 0)
                return;

            _stopwatch.Restart();
            _running = true;

            // 立即保存起点静止状态，避免第一帧被后台线程延迟。
            CaptureSample();

            _worker = new Thread(SamplingLoop)
            {
                IsBackground = true,
                Name = "TrackingExperimentRecorder"
            };
            _worker.Start();
        }

        // 供Stanley/LQR控制器主动写入本周期最终速度命令。
        // 调用后优先记录该命令，不再使用底盘反解值。
        public void UpdateCommand(
            float commandSpeed,
            float commandAngularSpeed)
        {
            lock (_commandSyncRoot)
            {
                _externalCommandSpeed = commandSpeed;
                _externalCommandVx = commandSpeed;
                _externalCommandVy = 0f;
                _externalCommandAngularSpeed =
                    commandAngularSpeed;
                _hasExternalCommand = true;
            }
        }

        // 供全向、蟹行和曲线控制器写入完整车体速度命令。
        public void UpdateBodyCommand(
            float commandVx,
            float commandVy,
            float commandAngularSpeed)
        {
            lock (_commandSyncRoot)
            {
                _externalCommandVx = commandVx;
                _externalCommandVy = commandVy;
                _externalCommandSpeed =
                    (float)Math.Sqrt(
                        commandVx * commandVx +
                        commandVy * commandVy);
                _externalCommandAngularSpeed =
                    commandAngularSpeed;
                _hasExternalCommand = true;
            }
        }

        /// <summary>
        /// 保存新版控制器本周期实际使用的校验后车辆状态，供后台采样线程写入CSV。
        /// </summary>
        public void UpdateProcessedState(VehicleState state)
        {
            lock (_stateSyncRoot)
            {
                _latestProcessedState = state;
            }
        }

        /// <summary>
        /// 保存新版控制器本周期实际使用的轨迹投影、误差和参考速度。
        /// </summary>
        public void UpdateControlReference(
            double arcLengthMeters,
            double referenceSpeedMetersPerSecond,
            double lateralErrorMeters,
            double headingErrorRadians,
            double distanceToTrajectoryMeters,
            double remainingDistanceMeters)
        {
            lock (_stateSyncRoot)
            {
                _controlReferenceArcLengthMeters =
                    arcLengthMeters;
                _controlReferenceSpeedMetersPerSecond =
                    referenceSpeedMetersPerSecond;
                _controlLateralErrorMeters =
                    lateralErrorMeters;
                _controlHeadingErrorRadians =
                    headingErrorRadians;
                _controlDistanceToTrajectoryMeters =
                    distanceToTrajectoryMeters;
                _controlRemainingDistanceMeters =
                    remainingDistanceMeters;
                _hasControlReference = true;
            }
        }

        // 停止采样并将本次实验保存为CSV；重复调用只保存一次。
        public void StopAndSave()
        {
            if (Volatile.Read(ref _started) == 0)
                return;

            if (Interlocked.Exchange(ref _saved, 1) != 0)
                return;

            try
            {
                _running = false;

                if (_worker != null &&
                    _worker != Thread.CurrentThread)
                {
                    _worker.Join(
                        Math.Max(1000, _sampleIntervalMs * 4));
                }

                // 保存停止时刻的最后一帧。
                CaptureSample();
                _stopwatch.Stop();
                SaveCsv();

                Console.WriteLine(
                    $"轨迹实验数据已保存：{SavedFilePath}");
            }
            catch
            {
                // 保存失败后允许调用者再次尝试。
                Interlocked.Exchange(ref _saved, 0);
                throw;
            }
        }

        // 按固定周期采集Detour位姿和控制命令。
        private void SamplingLoop()
        {
            while (_running)
            {
                Thread.Sleep(_sampleIntervalMs);

                if (!_running)
                    break;

                CaptureSample();
            }
        }

        // 采集一帧Detour位姿和控制命令。
        private void CaptureSample()
        {
            try
            {
                var location =
                    DetourInterface.getCartLocation();

                float commandSpeed;
                float commandVx;
                float commandVy;
                float commandAngularSpeed;
                VehicleState? processedState;
                bool hasControlReference;
                double controlReferenceArcLengthMeters;
                double controlReferenceSpeedMetersPerSecond;
                double controlLateralErrorMeters;
                double controlHeadingErrorRadians;
                double controlDistanceToTrajectoryMeters;
                double controlRemainingDistanceMeters;

                lock (_commandSyncRoot)
                {
                    if (_hasExternalCommand)
                    {
                        commandSpeed =
                            _externalCommandSpeed;
                        commandVx =
                            _externalCommandVx;
                        commandVy =
                            _externalCommandVy;
                        commandAngularSpeed =
                            _externalCommandAngularSpeed;
                    }
                    else
                    {
                        var command =
                            PilotDefinition.Chassis
                                .GetCarSpeed(false);

                        commandVx = command.Vx;
                        commandVy = command.Vy;
                        // CommonUsage.GetCarSpeed().Vw的单位为deg/s，
                        // 记录器内部统一转换为rad/s。
                        commandAngularSpeed =
                            (float)AngleMath.DegreesToRadians(
                                command.Vw);
                        commandSpeed = (float)Math.Sqrt(
                            commandVx * commandVx +
                            commandVy * commandVy);
                    }
                }

                lock (_stateSyncRoot)
                {
                    processedState =
                        _latestProcessedState;
                    hasControlReference =
                        _hasControlReference;
                    controlReferenceArcLengthMeters =
                        _controlReferenceArcLengthMeters;
                    controlReferenceSpeedMetersPerSecond =
                        _controlReferenceSpeedMetersPerSecond;
                    controlLateralErrorMeters =
                        _controlLateralErrorMeters;
                    controlHeadingErrorRadians =
                        _controlHeadingErrorRadians;
                    controlDistanceToTrajectoryMeters =
                        _controlDistanceToTrajectoryMeters;
                    controlRemainingDistanceMeters =
                        _controlRemainingDistanceMeters;
                }

                var sample = new TrackingSample
                {
                    ElapsedSeconds =
                        _stopwatch.Elapsed.TotalSeconds,
                    DetourX = location.x,
                    DetourY = location.y,
                    DetourTheta = location.th,
                    CommandSpeed = commandSpeed,
                    CommandVx = commandVx,
                    CommandVy = commandVy,
                    CommandAngularSpeed =
                        commandAngularSpeed,
                    HasProcessedState =
                        processedState.HasValue,
                    HasControlReference =
                        hasControlReference,
                    ControlReferenceArcLengthMeters =
                        controlReferenceArcLengthMeters,
                    ControlReferenceSpeedMetersPerSecond =
                        controlReferenceSpeedMetersPerSecond,
                    ControlLateralErrorMeters =
                        controlLateralErrorMeters,
                    ControlHeadingErrorRadians =
                        controlHeadingErrorRadians,
                    ControlDistanceToTrajectoryMeters =
                        controlDistanceToTrajectoryMeters,
                    ControlRemainingDistanceMeters =
                        controlRemainingDistanceMeters
                };

                if (processedState.HasValue)
                {
                    var state = processedState.Value;
                    sample.StateTimestampSeconds =
                        state.SampleTimestampSeconds;
                    sample.StateXmeters =
                        state.PoseInWorld.XMeters;
                    sample.StateYMeters =
                        state.PoseInWorld.YMeters;
                    sample.StateYawRadians =
                        state.PoseInWorld.YawRadians;
                    sample.StateWorldVxMetersPerSecond =
                        state.TwistInWorld.VxMetersPerSecond;
                    sample.StateWorldVyMetersPerSecond =
                        state.TwistInWorld.VyMetersPerSecond;
                    sample.StateBodyVxMetersPerSecond =
                        state.TwistInBody.VxMetersPerSecond;
                    sample.StateBodyVyMetersPerSecond =
                        state.TwistInBody.VyMetersPerSecond;
                    sample.StateAngularSpeedRadiansPerSecond =
                        state.TwistInBody.OmegaRadiansPerSecond;
                    sample.StateVelocityEstimateValid =
                        state.HasValidVelocityEstimate;
                }

                lock (_sampleSyncRoot)
                {
                    _samples.Add(sample);
                }
            }
            catch (Exception ex)
            {
                // 单帧读取失败不应终止车辆控制或整个记录线程。
                Console.WriteLine(
                    $"轨迹实验采样失败：{ex.Message}");
            }
        }

        // 将内存中的采样数据写入CSV。
        private void SaveCsv()
        {
            List<TrackingSample> snapshot;

            lock (_sampleSyncRoot)
            {
                snapshot =
                    new List<TrackingSample>(_samples);
            }

            var outputDirectory = Path.Combine(
                AppContext.BaseDirectory,
                "TrackingExperiments");

            Directory.CreateDirectory(outputDirectory);

            var fileName =
                $"{DateTime.Now:yyyyMMdd_HHmmss_fff}_" +
                $"{SanitizeFileName(_controllerName)}_" +
                $"{SanitizeFileName(_trajectoryName)}_" +
                $"Trial{_trialNumber}.csv";

            SavedFilePath = Path.Combine(
                outputDirectory,
                fileName);

            using (var writer = new StreamWriter(
                SavedFilePath,
                false,
                new UTF8Encoding(true)))
            {
                writer.WriteLine(
                    "ElapsedSeconds," +
                    "ControllerName," +
                    "TrajectoryName," +
                    "TrialNumber," +
                    "DetourX," +
                    "DetourY," +
                    "DetourTheta," +
                    "CommandSpeed," +
                    // 保留旧列（deg/s）供历史Python脚本兼容。
                    "CommandAngularSpeed," +
                    "CommandAngularSpeedRadPerSecond," +
                    "CommandVx," +
                    "CommandVy," +
                    "ReferenceStartX," +
                    "ReferenceStartY," +
                    "ReferenceEndX," +
                    "ReferenceEndY," +
                    "ReferenceSpeed," +
                    "ReferenceAngularSpeedRadPerSecond," +
                    "ReferenceMotionFrameYawDegrees," +
                    "ReferenceAccelerationMetersPerSecondSquared," +
                    "ReferenceDecelerationMetersPerSecondSquared," +
                    "HasProcessedState," +
                    "StateTimestampSeconds," +
                    "StateXMeters," +
                    "StateYMeters," +
                    "StateYawRadians," +
                    "StateWorldVxMetersPerSecond," +
                    "StateWorldVyMetersPerSecond," +
                    "StateBodyVxMetersPerSecond," +
                    "StateBodyVyMetersPerSecond," +
                    "StateAngularSpeedRadiansPerSecond," +
                    "StateVelocityEstimateValid," +
                    "HasControlReference," +
                    "ControlReferenceArcLengthMeters," +
                    "ControlReferenceSpeedMetersPerSecond," +
                    "ControlLateralErrorMeters," +
                    "ControlHeadingErrorRadians," +
                    "ControlDistanceToTrajectoryMeters," +
                    "ControlRemainingDistanceMeters");

                foreach (var sample in snapshot)
                {
                    writer.WriteLine(string.Join(
                        ",",
                        Format(sample.ElapsedSeconds),
                        EscapeCsv(_controllerName),
                        EscapeCsv(_trajectoryName),
                        _trialNumber.ToString(
                            CultureInfo.InvariantCulture),
                        Format(sample.DetourX),
                        Format(sample.DetourY),
                        Format(sample.DetourTheta),
                        Format(sample.CommandSpeed),
                        Format(
                            AngleMath.RadiansToDegrees(
                                sample.CommandAngularSpeed)),
                        Format(sample.CommandAngularSpeed),
                        Format(sample.CommandVx),
                        Format(sample.CommandVy),
                        Format(_referenceStart.X),
                        Format(_referenceStart.Y),
                        Format(_referenceEnd.X),
                        Format(_referenceEnd.Y),
                        Format(_referenceSpeed),
                        Format(_referenceAngularSpeed),
                        Format(_referenceMotionFrameYawDegrees),
                        Format(
                            _referenceAccelerationMetersPerSecondSquared),
                        Format(
                            _referenceDecelerationMetersPerSecondSquared),
                        sample.HasProcessedState
                            ? "1"
                            : "0",
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateTimestampSeconds),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateXmeters),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateYMeters),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateYawRadians),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateWorldVxMetersPerSecond),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateWorldVyMetersPerSecond),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateBodyVxMetersPerSecond),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateBodyVyMetersPerSecond),
                        FormatOptional(
                            sample.HasProcessedState,
                            sample.StateAngularSpeedRadiansPerSecond),
                        sample.HasProcessedState
                            ? sample.StateVelocityEstimateValid
                                ? "1"
                                : "0"
                            : string.Empty,
                        sample.HasControlReference
                            ? "1"
                            : "0",
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlReferenceArcLengthMeters),
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlReferenceSpeedMetersPerSecond),
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlLateralErrorMeters),
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlHeadingErrorRadians),
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlDistanceToTrajectoryMeters),
                        FormatOptional(
                            sample.HasControlReference,
                            sample.ControlRemainingDistanceMeters)));
                }
            }
        }

        // 将文件名中的非法字符替换为下划线。
        private static string SanitizeFileName(string value)
        {
            var result = value;

            foreach (var invalidCharacter in
                     Path.GetInvalidFileNameChars())
            {
                result = result.Replace(
                    invalidCharacter,
                    '_');
            }

            return result;
        }

        // 按固定小数格式输出数值，避免系统区域设置改变CSV格式。
        private static string Format(double value)
        {
            return value.ToString(
                "0.######",
                CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// 在新版状态尚未产生时为空，否则按统一小数格式输出状态数值。
        /// </summary>
        private static string FormatOptional(
            bool hasValue,
            double value)
        {
            return hasValue
                ? Format(value)
                : string.Empty;
        }

        // 对CSV文本字段进行引号和逗号转义。
        private static string EscapeCsv(string value)
        {
            if (value == null)
                return string.Empty;

            if (!value.Contains(",") &&
                !value.Contains("\"") &&
                !value.Contains("\r") &&
                !value.Contains("\n"))
            {
                return value;
            }

            return
                "\"" +
                value.Replace("\"", "\"\"") +
                "\"";
        }
    }
}
