namespace UniversalNetFrame451;

using static Kernel32;
using static Kernel32.DWORD;
using HANDLE = IntPtr;
public partial class Kernel32
{
    internal enum DWORD : uint
    {
        CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002,
        SYNCHRONIZE = 0x00100000,
        TIMER_MODIFY_STATE = 0x0002,
        INFINITE = 0xFFFFFFFF
    }

    [DllImport("kernel32.dll", EntryPoint = "GetSystemTimeAsFileTime")]
    internal static extern void GetSystemTimeAsFileTime(ref ulong lpSystemTimeAsFileTime);
    [DllImport("kernel32.dll", EntryPoint = "CreateWaitableTimerExW")]
    internal static extern HANDLE CreateWaitableTimerEx(IntPtr lpTimerAttributes, [In()][MarshalAs(UnmanagedType.LPWStr)] string lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess);
    [DllImport("kernel32.dll", EntryPoint = "SetWaitableTimer")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool SetWaitableTimer(HANDLE hTimer, ref long lpDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, [MarshalAs(UnmanagedType.Bool)] bool fResume);
    [DllImport("kernel32.dll", EntryPoint = "CancelWaitableTimer")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool CancelWaitableTimer(HANDLE hTimer);
    [DllImport("kernel32.dll", EntryPoint = "WaitForSingleObject")]
    internal static extern uint WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
    [DllImport("kernel32.dll", EntryPoint = "WaitForMultipleObjects")]
    internal static extern uint WaitForMultipleObjects(uint nCount, HANDLE[] lpHandles, int bWaitAll, DWORD dwMilliseconds);
    [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool CloseHandle(HANDLE hObject);
}

public class SleepEx : IDisposable
{
    HANDLE _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE);
    private bool disposedValue;

    public bool Sleep(uint dwMilliseconds, WaitHandle breakHandle = null)
    {
        if (_hTimer == IntPtr.Zero)
            throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later");
        long DueTime = -10 * 1000 * dwMilliseconds;
        SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
        if (breakHandle == null)
            return WaitForSingleObject(_hTimer, INFINITE) == 0;
        else
            return WaitForMultipleObjects(2, new nint[] { _hTimer, breakHandle.SafeWaitHandle.DangerousGetHandle() }, 0, INFINITE) == 0;
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            if (_hTimer != IntPtr.Zero)
            {
                CloseHandle(_hTimer);
                _hTimer = IntPtr.Zero;
            }

            disposedValue = true;
        }
    }

    // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
    ~SleepEx()
    {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: false);
    }

    void IDisposable.Dispose()
    {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}
public class TimerEx : IDisposable
{
    HANDLE _hTimer = IntPtr.Zero;
    private readonly CancellationTokenSource _cancellationTokenSource = new();
    private bool disposedValue;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _cancellationTokenSource.Cancel();
            if (_hTimer != IntPtr.Zero)
            {
                CancelWaitableTimer(_hTimer);
                CloseHandle(_hTimer);
                _hTimer = IntPtr.Zero;
            }
            disposedValue = true;
        }
    }

    // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
    ~TimerEx()
    {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: false);
    }

    public void Dispose()
    {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    public TimerEx(TimerCallback callback, object state, uint dueTime, uint period, uint times = uint.MaxValue)
    {
        ulong nowTime = 0;
        GetSystemTimeAsFileTime(ref nowTime);

        _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE);
        if (_hTimer == IntPtr.Zero)
            throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later");

        uint dueTimeMs = dueTime * 10000;
        uint periodMs = period * 10000;

        Task.Factory.StartNew(() =>
        {
            long DueTime = (long)nowTime + dueTimeMs;
            SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
            WaitForSingleObject(_hTimer, INFINITE);

            for (uint i = 1; i <= times && !_cancellationTokenSource.IsCancellationRequested; ++i)
            {
                callback?.Invoke(state);
                if (disposedValue)
                    break;
                GetSystemTimeAsFileTime(ref nowTime);

                for (; DueTime < (long)nowTime;)
                    DueTime += periodMs;

                SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
                WaitForSingleObject(_hTimer, INFINITE);
            }
        }, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    }
}
