// +build windows
package sync

import win32 "core:sys/windows"
import "core:time"

current_thread_id :: proc "contextless" () -> int {
	return int(win32.GetCurrentThreadId())
}


// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
Semaphore :: struct {
	_handle: win32.HANDLE,
}

semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
	s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil)
}

semaphore_destroy :: proc(s: ^Semaphore) {
	win32.CloseHandle(s._handle)
}

semaphore_post :: proc(s: ^Semaphore, count := 1) {
	win32.ReleaseSemaphore(s._handle, i32(count), nil)
}

semaphore_wait_for :: proc(s: ^Semaphore) {
	// NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
	result := win32.WaitForSingleObject(s._handle, win32.INFINITE)
	assert(result != win32.WAIT_FAILED)
}


Mutex :: struct {
	_critical_section: win32.CRITICAL_SECTION,
}


mutex_init :: proc(m: ^Mutex, spin_count := 0) {
	win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count))
}

mutex_destroy :: proc(m: ^Mutex) {
	win32.DeleteCriticalSection(&m._critical_section)
}

mutex_lock :: proc(m: ^Mutex) {
	win32.EnterCriticalSection(&m._critical_section)
}

mutex_try_lock :: proc(m: ^Mutex) -> bool {
	return bool(win32.TryEnterCriticalSection(&m._critical_section))
}

mutex_unlock :: proc(m: ^Mutex) {
	win32.LeaveCriticalSection(&m._critical_section)
}

Blocking_Mutex :: struct {
	_handle: win32.SRWLOCK,
}


blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
	win32.InitializeSRWLock(&m._handle)
}

blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
	//
}

blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
	win32.AcquireSRWLockExclusive(&m._handle)
}

blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
	return bool(win32.TryAcquireSRWLockExclusive(&m._handle))
}

blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
	win32.ReleaseSRWLockExclusive(&m._handle)
}


// Blocks until signalled.
// When signalled, awakens exactly one waiting thread.
Condition :: struct {
	_handle: win32.CONDITION_VARIABLE,

	mutex: Condition_Mutex_Ptr,
}


condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
	assert(mutex != nil)
	win32.InitializeConditionVariable(&c._handle)
	c.mutex = mutex
	return true
}

condition_destroy :: proc(c: ^Condition) {
	//
}

condition_signal :: proc(c: ^Condition) -> bool {
	if c._handle.ptr == nil {
		return false
	}
	win32.WakeConditionVariable(&c._handle)
	return true
}

condition_broadcast :: proc(c: ^Condition) -> bool {
	if c._handle.ptr == nil {
		return false
	}
	win32.WakeAllConditionVariable(&c._handle)
	return true
}

condition_wait_for :: proc(c: ^Condition) -> bool {
	switch m in &c.mutex {
	case ^Mutex:
		return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE)
	case ^Blocking_Mutex:
		return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0)
	}
	return false
}
condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
	ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000)
	switch m in &c.mutex {
	case ^Mutex:
		return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms)
	case ^Blocking_Mutex:
		return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0)
	}
	return false
}




RW_Lock :: struct {
	_handle: win32.SRWLOCK,
}

rw_lock_init :: proc(l: ^RW_Lock) {
	l._handle = win32.SRWLOCK_INIT
}
rw_lock_destroy :: proc(l: ^RW_Lock) {
	//
}
rw_lock_read :: proc(l: ^RW_Lock) {
	win32.AcquireSRWLockShared(&l._handle)
}
rw_lock_try_read :: proc(l: ^RW_Lock) -> bool {
	return bool(win32.TryAcquireSRWLockShared(&l._handle))
}
rw_lock_write :: proc(l: ^RW_Lock) {
	win32.AcquireSRWLockExclusive(&l._handle)
}
rw_lock_try_write :: proc(l: ^RW_Lock) -> bool {
	return bool(win32.TryAcquireSRWLockExclusive(&l._handle))
}
rw_lock_read_unlock :: proc(l: ^RW_Lock) {
	win32.ReleaseSRWLockShared(&l._handle)
}
rw_lock_write_unlock :: proc(l: ^RW_Lock) {
	win32.ReleaseSRWLockExclusive(&l._handle)
}


thread_yield :: proc() {
	win32.SwitchToThread()
}

