using System;
using MultiWheelC.TrajectoryPlanning.Utils;

namespace MultiWheelC.TrajectoryPlanning.PathSmoothing.LocalG2;

/// <summary>局部 G2 预平滑一次运行使用的已校验不可变选项。</summary>
internal sealed class LocalG2OptionsSnapshot
{
    internal LocalG2OptionsSnapshot(PathSmoothingConfiguration configuration)
    {
        if (configuration == null) throw new ArgumentNullException(nameof(configuration));
        LocalG2QuinticOptions source = configuration.LocalG2Quintic;
        if (source == null) throw new ArgumentOutOfRangeException(nameof(configuration));

        MinimumWindowLengthMeters = source.MinimumWindowLengthMeters;
        PreferredWindowLengthMeters = source.PreferredWindowLengthMeters;
        MaximumWindowLengthMeters = source.MaximumWindowLengthMeters;
        MaximumDeviationMeters = source.MaximumDeviationMeters;
        AbsoluteCurvatureJumpFloorPerMeter = source.AbsoluteCurvatureJumpFloorPerMeter;
        CurvatureJumpRatioOfMaximum = source.CurvatureJumpRatioOfMaximum;
        MinimumPeakGradientImprovementRatio = source.MinimumPeakGradientImprovementRatio;
        MaximumVariationCostRegressionRatio = source.MaximumVariationCostRegressionRatio;
        MaximumCandidatesPerRegion = source.MaximumCandidatesPerRegion;

        if (!NumericGuard.IsPositiveFinite(MinimumWindowLengthMeters))
            throw new ArgumentOutOfRangeException(nameof(MinimumWindowLengthMeters));
        if (!NumericGuard.IsFinite(PreferredWindowLengthMeters) || PreferredWindowLengthMeters < MinimumWindowLengthMeters)
            throw new ArgumentOutOfRangeException(nameof(PreferredWindowLengthMeters));
        if (!NumericGuard.IsFinite(MaximumWindowLengthMeters) || MaximumWindowLengthMeters < PreferredWindowLengthMeters)
            throw new ArgumentOutOfRangeException(nameof(MaximumWindowLengthMeters));
        if (!NumericGuard.IsPositiveFinite(MaximumDeviationMeters))
            throw new ArgumentOutOfRangeException(nameof(MaximumDeviationMeters));
        if (!NumericGuard.IsPositiveFinite(AbsoluteCurvatureJumpFloorPerMeter))
            throw new ArgumentOutOfRangeException(nameof(AbsoluteCurvatureJumpFloorPerMeter));
        if (!NumericGuard.IsFinite(CurvatureJumpRatioOfMaximum) || CurvatureJumpRatioOfMaximum <= 0d || CurvatureJumpRatioOfMaximum > 1d)
            throw new ArgumentOutOfRangeException(nameof(CurvatureJumpRatioOfMaximum));
        if (!NumericGuard.IsFinite(MinimumPeakGradientImprovementRatio) ||
            MinimumPeakGradientImprovementRatio <= 0d || MinimumPeakGradientImprovementRatio >= 1d)
        {
            throw new ArgumentOutOfRangeException(nameof(MinimumPeakGradientImprovementRatio));
        }
        if (!NumericGuard.IsFinite(MaximumVariationCostRegressionRatio) || MaximumVariationCostRegressionRatio < 0d)
            throw new ArgumentOutOfRangeException(nameof(MaximumVariationCostRegressionRatio));
        if (MaximumCandidatesPerRegion < 1)
            throw new ArgumentOutOfRangeException(nameof(MaximumCandidatesPerRegion));
    }

    internal double MinimumWindowLengthMeters { get; }
    internal double PreferredWindowLengthMeters { get; }
    internal double MaximumWindowLengthMeters { get; }
    internal double MaximumDeviationMeters { get; }
    internal double AbsoluteCurvatureJumpFloorPerMeter { get; }
    internal double CurvatureJumpRatioOfMaximum { get; }
    internal double MinimumPeakGradientImprovementRatio { get; }
    internal double MaximumVariationCostRegressionRatio { get; }
    internal int MaximumCandidatesPerRegion { get; }
}
