; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Asynchronous timer library.
;
;  Target OS: Win32
;
;  Dependencies:
;
;  Notes:
;_________________________________________________________________________________________

uses user32

__sys_time_slice = 10 ;[ms]


uglobal
  if used TimerCreate
    __InternalTimerID     dd ?
    __InternalTimerThread dd ?
  end if
endg



if used TimerCreate & ~ ( defined options.DebugMode.NoTimers & options.DebugMode.NoTimers )

initialize InitWin32Timers
begin
        invoke  SetTimer, 0, 0, __sys_time_slice, __TimerProc
        mov     [__InternalTimerID], eax

        return
endp



finalize FreeWin32Timers
begin
; stop the timer
        invoke  KillTimer, 0, [__InternalTimerID]
        return
endp

end if


; This procedure is called by the system on every time quantum.
; this procedure is Win32 TimerProc

proc __TimerProc, .hwnd, .msg, .timerID, .time
begin
        lea     eax, [__ptrFirstTimer]

.loop:
        mov     eax, [eax+TTimer.next]
.loop2:
        test    eax, eax
        jz      .end_timers

        test    [eax+TTimer.flags], tmfRunning
        jz      .loop

        mov     ecx, [eax+TTimer.value]
        add     ecx, __sys_time_slice
        mov     [eax+TTimer.value], ecx

        cmp     ecx, [eax+TTimer.interval]
        jl      .loop

.exploop:
        inc     [eax+TTimer.Expired]

        mov     ecx, [eax+TTimer.interval]
        sub     [eax+TTimer.value], ecx
        cmp     [eax+TTimer.value], ecx
        jge     .exploop

        cmp     [eax+TTimer.Callback], 0
        je      .end_event

        mov     ecx, [eax+TTimer.flags]
        and     ecx, $0f
        cmp     ecx, tmfDoNothing
        je      .end_event

; call the callback procedure.
.execloop:
        pushad
        stdcall [eax+TTimer.Callback], eax
        popad

        sub     [eax+TTimer.Expired], 1
        jg      .execloop

.end_event:
        test    [eax+TTimer.flags], tmfSyncDestroy
        jz      .loop

        push    eax
        mov     eax, [eax+TTimer.next]  ; after the destruction, this pointer will be lost.
        stdcall TimerDestroy ; pointer from the stack.
        jmp     .loop2

.end_timers:
        return
endp




proc GetSysTime
.time dq ?
begin
        pushad

        lea     eax, [.time]
        invoke  GetSystemTimeAsFileTime, eax

        popad
        return
endp



__filetimeOffset = $19DB1DED53E8000


proc FileTimeToUnixTime
begin
        push    ebx ecx

        sub     eax, __filetimeOffset and $ffffffff
        sbb     edx, __filetimeOffset shr 32

        mov     ebx, eax
        mov     ecx, 10000000   ; 100 ns -> s

        mov     eax, edx
        cdq
        idiv    ecx

        xchg    eax, ebx

        idiv    ecx
        mov     edx, ebx

        pop     ecx ebx
        return
endp



proc GetTime
.time dq ?
begin
        push    ecx

        lea     eax, [.time]
        invoke  GetSystemTimeAsFileTime, eax

        mov     eax, dword [.time]
        mov     edx, dword [.time+4]

        call    FileTimeToUnixTime

        pop     ecx
        return
endp



proc GetLocalTimeOffset
.info TIME_ZONE_INFORMATION
begin
        push    ecx edx

        lea     eax, [.info]
        invoke  GetTimeZoneInformation, eax
        mov     ecx, [.info.bias]

        cmp     eax, TIME_ZONE_ID_DAYLIGHT
        jne     @f
        add     ecx, [.info.DaylightBias]
@@:

        cmp     eax, TIME_ZONE_ID_STANDARD
        jne     @f
        add     ecx, [.info.StandardBias]
@@:

        imul    ecx, 60
        neg     ecx

        mov     eax, ecx
        pop     edx ecx
        return
endp


proc Sleep, .ms
begin
        push    eax ecx edx
        invoke  SleepWin, [.ms]
        pop     edx ecx eax
        return
endp