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

namespace MainShell.Common
{
    class TimeoutRetryMechanism
    {
        public class WaitForConditionResult
        {
            public bool Succeeded { get; set; }

            public bool TimedOut { get; set; }

            public bool IsAborted { get; set; }
        }

        /// <summary>
        /// 在指定超时时间内轮询等待条件成立。
        /// 当 <paramref name="timeout"/> 为 <c>null</c> 时，表示无限等待，直到条件成立或外部取消。
        /// </summary>
        /// <param name="condition">待检查的条件，返回 <c>true</c> 表示条件成立。</param>
        /// <param name="timeout">最大等待时间。传入 <c>null</c> 表示无限等待；传入具体值时必须大于 0。</param>
        /// <param name="pollingInterval">轮询间隔，必须大于 0。</param>
        /// <param name="cancellationToken">用于中止等待流程。</param>
        public static WaitForConditionResult WaitForConditionWithTimeout(Func<bool> condition, TimeSpan? timeout, TimeSpan pollingInterval, CancellationToken cancellationToken)
        {
            if (condition == null)
            {
                throw new ArgumentNullException(nameof(condition));
            }

            if (timeout.HasValue && timeout.Value <= TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout), "timeout must be greater than zero when specified.");
            }

            if (pollingInterval <= TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(pollingInterval), "pollingInterval must be greater than zero.");
            }

            var stopwatch = Stopwatch.StartNew();

            while (true)
            {
                if (condition())
                {
                    return new WaitForConditionResult
                    {
                        Succeeded = true
                    };
                }

                if (timeout.HasValue && stopwatch.Elapsed > timeout.Value)
                {
                    return new WaitForConditionResult
                    {
                        Succeeded = false,
                        TimedOut = true
                    };
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return new WaitForConditionResult
                    {
                        Succeeded = false,
                        IsAborted = true
                    };
                }

                Thread.Sleep(pollingInterval);
            }
        }
    }
}
