using System;
using System.Diagnostics;
using System.Threading;

namespace MultiWheelC.TrajectoryPlanning.Utils;

/// <summary>一次规划操作的中立停止原因；Map 与 CoarsePath 分别映射为自己的结果状态。</summary>
internal enum PlanningOperationStopReason
{
    /// <summary>预算仍可继续执行。</summary>
    None,
    /// <summary>调用方取消了本次操作。</summary>
    Cancelled,
    /// <summary>从预算创建起已经达到总超时。</summary>
    TimedOut,
}

/// <summary>
/// 以单调时钟统一控制一次规划操作的取消和总超时。
/// 本类不依赖 Map 或 CoarsePath，供两层在长循环检查同一个停止条件。
/// </summary>
internal sealed class PlanningOperationBudget
{
    private const int CheckIntervalWorkItems = 256;

    private readonly CancellationToken _cancellationToken;
    private readonly Stopwatch _stopwatch;
    private readonly TimeSpan _timeout;
    private readonly bool _hasTimeout;

    /// <summary>
    /// 创建具有总超时的操作预算。
    /// 参数：cancellationToken 为调用方停止请求；timeout 必须非负，且从构造时开始计时。
    /// </summary>
    internal PlanningOperationBudget(CancellationToken cancellationToken, TimeSpan timeout)
    {
        if (timeout < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(timeout));
        _cancellationToken = cancellationToken;
        _timeout = timeout;
        _hasTimeout = true;
        _stopwatch = Stopwatch.StartNew();
    }

    private PlanningOperationBudget(CancellationToken cancellationToken)
    {
        _cancellationToken = cancellationToken;
        _timeout = TimeSpan.Zero;
        _hasTimeout = false;
        _stopwatch = Stopwatch.StartNew();
    }

    /// <summary>创建只响应取消、不限制总耗时的兼容预算。</summary>
    internal static PlanningOperationBudget Unlimited(CancellationToken cancellationToken)
    {
        return new PlanningOperationBudget(cancellationToken);
    }

    /// <summary>从预算创建到当前的单调经过时间。</summary>
    internal TimeSpan Elapsed { get { return _stopwatch.Elapsed; } }

    /// <summary>立即查询当前停止原因；取消优先于超时。</summary>
    internal PlanningOperationStopReason GetStopReason()
    {
        if (_cancellationToken.IsCancellationRequested) return PlanningOperationStopReason.Cancelled;
        return _hasTimeout && _stopwatch.Elapsed >= _timeout
            ? PlanningOperationStopReason.TimedOut
            : PlanningOperationStopReason.None;
    }

    /// <summary>
    /// 在循环工作单元中按固定上限检查预算。
    /// 参数：workItemCount 为当前循环共享的已处理单元计数；首次及每 256 个单元检查一次。
    /// </summary>
    internal PlanningOperationStopReason CheckEvery(ref int workItemCount)
    {
        workItemCount++;
        return workItemCount == 1 || workItemCount % CheckIntervalWorkItems == 0
            ? GetStopReason()
            : PlanningOperationStopReason.None;
    }
}
