using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace MultiWheelC.TrajectoryPlanning.PathSmoothing.Comparison;

/// <summary>一个方法的固定五次计时样本和确定性结论。</summary>
public sealed class SmoothingTimingSummary
{
    /// <summary>创建一个只接受五个已测量样本的计时汇总。</summary>
    public SmoothingTimingSummary(
        IReadOnlyList<double> measuredElapsedMilliseconds,
        bool isDeterministic,
        string diagnostic)
    {
        if (measuredElapsedMilliseconds == null || measuredElapsedMilliseconds.Count != 5)
            throw new ArgumentException("计时汇总必须包含五个已测量样本。", nameof(measuredElapsedMilliseconds));

        var copy = new List<double>(measuredElapsedMilliseconds.Count);
        for (int index = 0; index < measuredElapsedMilliseconds.Count; index++)
        {
            double value = measuredElapsedMilliseconds[index];
            if (double.IsNaN(value) || double.IsInfinity(value) || value < 0d)
                throw new ArgumentOutOfRangeException(nameof(measuredElapsedMilliseconds), "计时样本必须为有限非负数。");
            copy.Add(value);
        }

        MeasuredElapsedMilliseconds = new ReadOnlyCollection<double>(copy);
        MedianElapsedMilliseconds = Median(copy);
        IsDeterministic = isDeterministic;
        Diagnostic = diagnostic ?? string.Empty;
    }

    /// <summary>不含预热执行的五个实测耗时，单位 ms。</summary>
    public IReadOnlyList<double> MeasuredElapsedMilliseconds { get; }

    /// <summary>五个实测耗时的稳定中位数，单位 ms。</summary>
    public double MedianElapsedMilliseconds { get; }

    /// <summary>五次输出是否具有相同的状态和稳定几何摘要。</summary>
    public bool IsDeterministic { get; }

    /// <summary>非确定性或测量失败的稳定诊断说明。</summary>
    public string Diagnostic { get; }

    /// <summary>从五次测量结果中生成计时汇总，并拒绝状态或几何不稳定的输出。</summary>
    public static SmoothingTimingSummary FromMeasurements(
        IReadOnlyList<double> measuredElapsedMilliseconds,
        IReadOnlyList<PathSmoothingResult> measuredResults)
    {
        if (measuredResults == null || measuredResults.Count != 5)
            throw new ArgumentException("确定性检查必须包含五个测量结果。", nameof(measuredResults));
        for (int index = 0; index < measuredResults.Count; index++)
        {
            if (measuredResults[index] == null)
                throw new ArgumentException("确定性检查不能包含空测量结果。", nameof(measuredResults));
        }

        PathSmoothingResult canonical = measuredResults[0];
        string canonicalDigest = StableGeometryDigest.Compute(canonical);
        for (int index = 1; index < measuredResults.Count; index++)
        {
            PathSmoothingResult measured = measuredResults[index];
            string digest = StableGeometryDigest.Compute(measured);
            if (measured.Status != canonical.Status ||
                measured.Path.Count != canonical.Path.Count ||
                measured.Segments.Count != canonical.Segments.Count ||
                !string.Equals(digest, canonicalDigest, StringComparison.Ordinal))
            {
                return new SmoothingTimingSummary(
                    measuredElapsedMilliseconds,
                    false,
                    "五次测量的状态、点数、分段数或稳定几何摘要不一致。");
            }
        }

        return new SmoothingTimingSummary(measuredElapsedMilliseconds, true, string.Empty);
    }

    internal static double Median(IReadOnlyList<double> values)
    {
        var sorted = new double[values.Count];
        for (int index = 0; index < values.Count; index++) sorted[index] = values[index];
        Array.Sort(sorted);
        int middle = sorted.Length / 2;
        return sorted.Length % 2 == 1
            ? sorted[middle]
            : (sorted[middle - 1] + sorted[middle]) / 2d;
    }
}
