#if defined(_CLANGD)
  #define DN_WITH_OS 1
  #include "../dn.h"
  #include "dn_os_w32.h"
  #include <windows.h>
#endif

// NOTE: DN_Mem
static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect)
{
  DN_Assert((protect & ~DN_MemPage_All) == 0);
  DN_Assert(protect != 0);
  DN_U32 result = 0;

  if (protect & DN_MemPage_NoAccess) {
    result = PAGE_NOACCESS;
  } else if (protect & DN_MemPage_ReadWrite) {
    result = PAGE_READWRITE;
  } else if (protect & DN_MemPage_Read) {
    result = PAGE_READONLY;
  } else if (protect & DN_MemPage_Write) {
    DN_LogWarningF("Windows does not support write-only pages, granting read+write access");
    result = PAGE_READWRITE;
  }

  if (protect & DN_MemPage_Guard)
    result |= PAGE_GUARD;

  DN_AssertF(result != PAGE_GUARD, "Page guard is a modifier, you must also specify a page permission like read or/and write");
  return result;
}

DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags)
{
  unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
  unsigned long flags         = MEM_RESERVE;
  if (commit == DN_MemCommit_Yes)
      flags |= MEM_COMMIT;

  void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
  if (flags & MEM_COMMIT) {
    DN_Core *dn = DN_Get();
    DN_AtomicAddU64(&dn->os.vmem_allocs_total, 1);
    DN_AtomicAddU64(&dn->os.vmem_allocs_frame, 1);
  }
  return result;
}

DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
{
  bool result = false;
  if (!ptr || size == 0)
    return false;
  unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
  result                      = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
  DN_Core *dn                 = DN_Get();
  DN_AtomicAddU64(&dn->os.vmem_allocs_total, 1);
  DN_AtomicAddU64(&dn->os.vmem_allocs_frame, 1);
  return result;
}

DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size)
{
  // NOTE: This is a decommit call, which is explicitly saying to free the
  // pages but not the address space, you would use OS_MemRelease to release
  // everything.
  DN_MSVC_WARNING_PUSH
  DN_MSVC_WARNING_DISABLE(6250) // Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs).  This causes address space leaks.
  VirtualFree(ptr, size, MEM_DECOMMIT);
  DN_MSVC_WARNING_POP
}

DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
{
  (void)size;
  VirtualFree(ptr, 0, MEM_RELEASE);
}

DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
{
  if (!ptr || size == 0)
    return 0;

  static DN_Str8 const ALIGNMENT_ERROR_MSG = DN_Str8Lit("Page protection requires pointers to be page aligned because we can only guard memory at a multiple of the page boundary.");
  DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, DN_Get()->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
  DN_AssertF(DN_IsPowerOfTwoAligned(size, DN_Get()->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);

  unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
  unsigned long prev_flags    = 0;
  int           result        = VirtualProtect(ptr, size, os_page_flags, &prev_flags);

  (void)prev_flags;
  if (result == 0)
    DN_AssertF(result, "VirtualProtect failed");
  return result;
}

DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem)
{
  DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0;
  DN_Assert(size <= DN_Cast(DWORD)(-1));
  void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);

  DN_Core *dn = DN_Get();
  if (dn) {
    DN_AtomicAddU64(&dn->os.mem_allocs_total, 1);
    DN_AtomicAddU64(&dn->os.mem_allocs_frame, 1);
  }
  return result;
}

DN_API void DN_OS_MemDealloc(void *ptr)
{
  HeapFree(GetProcessHeap(), 0, ptr);
}

// NOTE: Date
DN_API DN_Date DN_OS_DateLocalTimeNow()
{
  SYSTEMTIME sys_time;
  GetLocalTime(&sys_time);

  DN_Date result      = {};
  result.hour         = DN_Cast(DN_U8) sys_time.wHour;
  result.milliseconds = DN_Cast(DN_U8) sys_time.wMilliseconds;
  result.minutes      = DN_Cast(DN_U8) sys_time.wMinute;
  result.seconds      = DN_Cast(DN_U8) sys_time.wSecond;
  result.day          = DN_Cast(DN_U8) sys_time.wDay;
  result.month        = DN_Cast(DN_U8) sys_time.wMonth;
  result.year         = DN_Cast(DN_U16) sys_time.wYear;
  return result;
}

const DN_U64 DN_OS_WIN32_UNIX_TIME_START            = 0x019DB1DED53E8000; // January 1, 1970 (start of Unix epoch) in "ticks"
const DN_U64 DN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND = 10'000'000;         // Filetime returned is in intervals of 100 nanoseconds

DN_API DN_U64 DN_OS_DateUnixTimeNs()
{
  FILETIME file_time;
  GetSystemTimeAsFileTime(&file_time);

  // NOTE: Filetime returned is in intervals of 100 nanoeseconds so we
  // multiply by 100 to get nanoseconds.
  LARGE_INTEGER date_time;
  date_time.u.LowPart  = file_time.dwLowDateTime;
  date_time.u.HighPart = file_time.dwHighDateTime;
  DN_U64 result      = (date_time.QuadPart - DN_OS_WIN32_UNIX_TIME_START) * 100;
  return result;
}

static SYSTEMTIME DN_OS_DateToSystemTime_(DN_Date date)
{
  SYSTEMTIME result    = {};
  result.wYear         = date.year;
  result.wMonth        = date.month;
  result.wDay          = date.day;
  result.wHour         = date.hour;
  result.wMinute       = date.minutes;
  result.wSecond       = date.seconds;
  result.wMilliseconds = date.milliseconds;
  return result;
}

static DN_U64 DN_OS_SystemTimeToUnixTimeS_(SYSTEMTIME *sys_time)
{
  FILETIME file_time = {};
  SystemTimeToFileTime(sys_time, &file_time);

  LARGE_INTEGER date_time;
  date_time.u.LowPart  = file_time.dwLowDateTime;
  date_time.u.HighPart = file_time.dwHighDateTime;
  DN_U64 result      = (date_time.QuadPart - DN_OS_WIN32_UNIX_TIME_START) / DN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND;
  return result;
}

DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate(DN_Date date)
{
  SYSTEMTIME local_time = DN_OS_DateToSystemTime_(date);
  SYSTEMTIME sys_time   = {};
  TzSpecificLocalTimeToSystemTime(nullptr, &local_time, &sys_time);
  DN_U64 result = DN_OS_SystemTimeToUnixTimeS_(&sys_time);
  return result;
}

DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS(DN_U64 unix_ts_s)
{
  DN_U64   unix_time    = DN_Cast(DN_U64) unix_ts_s * 10000000LL; // seconds -> 100ns units
  DN_U64   filetime_utc = unix_time + 116444736000000000LL;       // Unix epoch -> Windows epoch
  FILETIME ft_utc       = {DN_Cast(DWORD) filetime_utc, DN_Cast(DWORD)(filetime_utc >> 32)};
  FILETIME ft_local;
  bool     converted = FileTimeToLocalFileTime(&ft_utc, &ft_local);
  DN_Assert(converted);

  DN_U64 filetime_local = (DN_Cast(DN_U64) ft_local.dwHighDateTime << 32) | ft_local.dwLowDateTime;
  DN_U64 result         = (filetime_local - 116444736000000000LL) / 10000000LL;
  return result;
}

DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
{
  DN_OSW32Core *w32 = DN_Cast(DN_OSW32Core *) DN_Get()->os.platform_context;
  DN_Assert(w32->bcrypt_init_success);

  long gen_status = BCryptGenRandom(w32->bcrypt_rng_handle, DN_Cast(unsigned char *) buffer, size, 0 /*flags*/);
  // NOTE: This can only fail if the handle is invalid or one or more parameters are invalid. We
  // validate our parameters so this shouldn't be the case.
  DN_Assert(gen_status == 0);
}

DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path)
{
  DN_TcScratch   scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_OSDiskSpace result  = {};
  DN_Str16       path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);

  ULARGE_INTEGER free_bytes_avail_to_caller;
  ULARGE_INTEGER total_number_of_bytes;
  ULARGE_INTEGER total_number_of_free_bytes;
  if (!GetDiskFreeSpaceExW(path16.data,
                           &free_bytes_avail_to_caller,
                           &total_number_of_bytes,
                           &total_number_of_free_bytes)) {
    DN_TcScratchEnd(&scratch);
    return result;
  }

  result.success = true;
  result.avail   = free_bytes_avail_to_caller.QuadPart;
  result.size    = total_number_of_bytes.QuadPart;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value)
{
  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     name16  = DN_OS_W32Str8ToStr16(&scratch.arena, name);
  DN_Str16     value16 = DN_OS_W32Str8ToStr16(&scratch.arena, value);
  bool         result  = SetEnvironmentVariableW(name16.data, value16.data) != 0;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_Str8 DN_OS_ExePath(DN_Arena *arena)
{
  DN_Str8 result = {};
  if (!arena)
    return result;
  DN_TcScratch scratch   = DN_TcScratchBeginArena(&arena, 1);
  DN_Str16     exe_dir16 = DN_OS_W32EXEPathW(&scratch.arena);
  result                 = DN_OS_W32Str16ToStr8(arena, exe_dir16);
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API void DN_OS_SleepMs(DN_UInt milliseconds)
{
  Sleep(milliseconds);
}

DN_API DN_U64 DN_OS_PerfCounterFrequency()
{
  DN_OSW32Core *w32 = DN_Cast(DN_OSW32Core *) DN_Get()->os.platform_context;
  DN_Assert(w32->qpc_frequency.QuadPart);
  DN_U64 result = w32->qpc_frequency.QuadPart;
  return result;
}

DN_API DN_U64 DN_OS_PerfCounterNow()
{
  LARGE_INTEGER integer = {};
  QueryPerformanceCounter(&integer);
  DN_U64 result = integer.QuadPart;
  return result;
}

static DN_U64 DN_OS_W32FileTimeToSeconds_(FILETIME const *time)
{
  ULARGE_INTEGER time_large_int = {};
  time_large_int.u.LowPart      = time->dwLowDateTime;
  time_large_int.u.HighPart     = time->dwHighDateTime;
  DN_U64 result                 = (time_large_int.QuadPart / 10000000ULL) - 11644473600ULL;
  return result;
}

DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err)
{
  bool       result = false;
  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     src16   = DN_OS_W32Str8ToStr16(&scratch.arena, src);
  DN_Str16     dest16  = DN_OS_W32Str8ToStr16(&scratch.arena, dest);

  int fail_if_exists = overwrite == false;
  result             = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;

  if (!result) {
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    DN_ErrSinkAppendF(err,
                       win_error.code,
                       "Failed to copy file '%.*s' to '%.*s': (%u) %.*s",
                       DN_Str8PrintFmt(src),
                       DN_Str8PrintFmt(dest),
                       win_error.code,
                       DN_Str8PrintFmt(win_error.msg));
  }
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err)
{
  bool         result = false;
  DN_TcScratch scratch   = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     src16  = DN_OS_W32Str8ToStr16(&scratch.arena, src);
  DN_Str16     dest16 = DN_OS_W32Str8ToStr16(&scratch.arena, dest);

  unsigned long flags = MOVEFILE_COPY_ALLOWED;
  if (overwrite)
    flags |= MOVEFILE_REPLACE_EXISTING;

  result = MoveFileExW(src16.data, dest16.data, flags) != 0;
  if (!result) {
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    DN_ErrSinkAppendF(err,
                         win_error.code,
                         "Failed to move file '%.*s' to '%.*s': (%u) %.*s",
                         DN_Str8PrintFmt(src),
                         DN_Str8PrintFmt(dest),
                         win_error.code,
                         DN_Str8PrintFmt(win_error.msg));
  }
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_ErrSink *err)
{
  DN_OSFile result = {};
  if (path.count == 0 || path.count <= 0)
    return result;

  if ((access & ~DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0)) {
    DN_AssertInvalidCodePath;
    return result;
  }

  unsigned long create_flag = 0;
  switch (open_mode) {
    case DN_OSFileOpen_CreateAlways: create_flag = CREATE_ALWAYS; break;
    case DN_OSFileOpen_OpenIfExist:  create_flag = OPEN_EXISTING; break;
    case DN_OSFileOpen_OpenAlways:   create_flag = OPEN_ALWAYS;   break;
    default:                         DN_AssertInvalidCodePath;    return result;
  }

  unsigned long access_mode = 0;
  if (access & DN_OSFileAccess_AppendOnly) {
    DN_AssertF((access & ~DN_OSFileAccess_AppendOnly) == 0,
               "Append can only be applied exclusively to the file, other access modes not permitted");
    access_mode = FILE_APPEND_DATA;
  } else {
    if (access & DN_OSFileAccess_Read)
      access_mode |= GENERIC_READ;
    if (access & DN_OSFileAccess_Write)
      access_mode |= GENERIC_WRITE;
    if (access & DN_OSFileAccess_Execute)
      access_mode |= GENERIC_EXECUTE;
  }

  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);
  void        *handle  = CreateFileW(/*LPCWSTR               lpFileName*/ path16.data,
                                     /*DWORD                 dwDesiredAccess*/ access_mode,
                                     /*DWORD                 dwShareMode*/ FILE_SHARE_READ | FILE_SHARE_WRITE,
                                     /*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ nullptr,
                                     /*DWORD                 dwCreationDisposition*/ create_flag,
                                     /*DWORD                 dwFlagsAndAttributes*/ FILE_ATTRIBUTE_NORMAL,
                                     /*HANDLE                hTemplateFile*/ nullptr);

  if (handle == INVALID_HANDLE_VALUE) {
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    result.error          = true;
    DN_ErrSinkAppendF(err, win_error.code, "Failed to open file at '%.*s': '%.*s'", DN_Str8PrintFmt(path), DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
    return result;
  }

  result.handle = handle;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size, DN_ErrSink *err)
{
  DN_OSFileRead result = {};
  if (!file || !file->handle || file->error || !buffer || size <= 0)
    return result;

  if (size > ULONG_MAX) {
    DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size);
    DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc));
    return result;
  }

  unsigned long bytes_read  = 0;
  unsigned long read_result = ReadFile(/*HANDLE       hFile*/ file->handle,
                                       /*LPVOID       lpBuffer*/ buffer,
                                       /*DWORD        nNumberOfBytesToRead*/ DN_Cast(unsigned long) size,
                                       /*LPDWORD      lpNumberOfByesRead*/ &bytes_read,
                                       /*LPOVERLAPPED lpOverlapped*/ nullptr);
  if (read_result == 0) {
    DN_TcScratch  scratch   = DN_TcScratchBeginArena(nullptr, 0);
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
    return result;
  }

  if (bytes_read != size) {
    DN_TcScratch  scratch   = DN_TcScratchBeginArena(nullptr, 0);
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    DN_ErrSinkAppendF(
        err,
        win_error.code,
        "Failed to read the desired number of bytes from file, we read %uB but we expected %uB: (%u) %.*s",
        bytes_read,
        DN_Cast(unsigned long) size,
        win_error.code,
        DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
    return result;
  }

  result.bytes_read = bytes_read;
  result.success    = true;
  return result;
}

DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *buffer, DN_USize size, DN_ErrSink *err)
{
  if (!file || !file->handle || file->error || !buffer || size <= 0)
    return false;

  bool        result = true;
  char const *end    = DN_Cast(char *) buffer + size;
  for (char const *ptr = DN_Cast(char const *) buffer; result && ptr != end;) {
    unsigned long write_size    = DN_Cast(unsigned long) DN_Min((unsigned long)-1, end - ptr);
    unsigned long bytes_written = 0;
    result                      = WriteFile(file->handle, ptr, write_size, &bytes_written, nullptr /*lpOverlapped*/) != 0;
    ptr += bytes_written;
  }

  if (!result) {
    DN_TcScratch  scratch          = DN_TcScratchBeginArena(nullptr, 0);
    DN_OSW32Error win_error        = DN_OS_W32LastError(&scratch.arena);
    DN_Str8x32    buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size);
    DN_ErrSinkAppendF(err, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DN_Str8PrintFmt(buffer_size_str8), DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
  }
  return result;
}

DN_API bool DN_OS_FileFlush(DN_OSFile *file, DN_ErrSink *err)
{
  if (!file || !file->handle || file->error)
    return false;

  BOOL result = FlushFileBuffers(DN_Cast(HANDLE) file->handle);
  if (!result) {
    DN_TcScratch  scratch   = DN_TcScratchBeginArena(nullptr, 0);
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    DN_ErrSinkAppendF(err, win_error.code, "Failed to flush file buffer to disk: %.*s", DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
  }

  return DN_Cast(bool) result;
}

DN_API void DN_OS_FileClose(DN_OSFile *file)
{
  if (!file || !file->handle || file->error)
    return;
  CloseHandle(file->handle);
  *file = {};
}

DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
{
  DN_OSPathInfo result = {};
  if (path.count == 0)
    return result;

  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);

  WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
  if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) {
    DN_TcScratchEnd(&scratch);
    return result;
  }

  result.exists                = true;
  result.create_time_in_s      = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreationTime);
  result.last_access_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastAccessTime);
  result.last_write_time_in_s  = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastWriteTime);

  LARGE_INTEGER large_int = {};
  large_int.u.HighPart    = DN_Cast(int32_t) attrib_data.nFileSizeHigh;
  large_int.u.LowPart     = attrib_data.nFileSizeLow;
  result.size             = (DN_U64)large_int.QuadPart;

  if (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
    if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      result.type = DN_OSPathInfoType_Directory;
    else
      result.type = DN_OSPathInfoType_File;
  }

  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_PathDelete(DN_Str8 path)
{
  bool result = false;
  if (path.count == 0)
    return result;

  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);
  if (path16.count) {
    result = DeleteFileW(path16.data);
    if (!result)
      result = RemoveDirectoryW(path16.data);
  }
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_PathIsFile(DN_Str8 path)
{
  bool result = false;
  if (path.count == 0)
    return result;

  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);
  if (path16.count) {
    WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
    if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
      result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
               !(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  }
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_PathIsDir(DN_Str8 path)
{
  bool result = false;
  if (path.count == 0)
    return result;

  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);
  if (path16.count) {
    WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
    if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
      result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
               (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  }

  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
{
  bool         result  = true;
  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str16     path16  = DN_OS_W32Str8ToStr16(&scratch.arena, path);

  // NOTE: Go back from the end of the string to all the directories in the
  // string, and try to create them. Since Win32 API cannot create
  // intermediate directories that don't exist in a path we need to go back
  // and record all the directories until we encounter one that exists.
  //
  // From that point onwards go forwards and make all the directories
  // inbetween by null-terminating the string temporarily, creating the
  // directory and so forth until we reach the end.
  //
  // If we find a file at some point in the path we fail out because the
  // series of directories can not be made if a file exists with the same
  // name.
  for (DN_USize index = 0; index < path16.count; index++) {
    bool    first_char = index == (path16.count - 1);
    wchar_t ch         = path16.data[index];
    if (ch == '/' || ch == '\\' || first_char) {
      wchar_t temp = path16.data[index];
      if (!first_char)
        path16.data[index] = 0; // Temporarily null terminate it

      WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
      bool                      successful  = GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data); // Check

      if (successful) {
        if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
          // NOTE: The directory exists, continue iterating the path
        } else {
          // NOTE: There's some kind of file that exists at the path
          // but it's not a directory. This request to make a
          // directory is invalid.
          DN_TcScratchEnd(&scratch);
          return false;
        }
      } else {
        // NOTE: There's nothing that exists at this path, we can create
        // a directory here
        result |= (CreateDirectoryW(path16.data, nullptr) == 0);
      }

      if (!first_char)
        path16.data[index] = temp; // Undo null termination
    }
  }
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
{
  if (path.count == 0 || !it || path.count <= 0)
    return false;

  DN_TcScratch             scratch    = DN_TcScratchBeginArena(nullptr, 0);
  DN_OSW32FolderIteratorW wide_it = {};
  DN_Str16               path16  = {};
  if (it->handle) {
    wide_it.handle = it->handle;
  } else {
    bool needs_asterisks = DN_Str8EndsWithSensitive(path, DN_Str8Lit("\\")) ||
                           DN_Str8EndsWithSensitive(path, DN_Str8Lit("/"));
    bool has_glob = DN_Str8EndsWithSensitive(path, DN_Str8Lit("\\*")) ||
                    DN_Str8EndsWithSensitive(path, DN_Str8Lit("/*"));

    DN_Str8 adjusted_path = path;
    if (!has_glob) {
      // NOTE: We are missing the glob for enumerating the files, we will
      // add those characters in this branch, so overwrite the null
      // character, add the glob and re-null terminate the buffer.
      if (needs_asterisks)
        adjusted_path = DN_Str8FmtOsPathArena(&scratch.arena, "%.*s*", DN_Str8PrintFmt(path));
      else
        adjusted_path = DN_Str8FmtOsPathArena(&scratch.arena, "%.*s/*", DN_Str8PrintFmt(path));
    }

    path16 = DN_OS_W32Str8ToStr16(&scratch.arena, adjusted_path);
    if (path16.count <= 0) { // Conversion error
      DN_TcScratchEnd(&scratch);
      return false;
    }
  }

  bool result = DN_OS_W32DirWIterate(path16, &wide_it);
  it->handle  = wide_it.handle;
  if (result) {
    int size      = DN_OS_W32Str16ToStr8Buffer(wide_it.file_name, it->buffer, DN_ArrayCountU(it->buffer));
    it->file_name = DN_Str8FromPtr(it->buffer, size);
  }

  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API void DN_OS_Exit(int32_t exit_code)
{
  ExitProcess(DN_Cast(UINT) exit_code);
}

DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
                                      char                *stdout_buffer,
                                      DN_USize            *stdout_size,
                                      char                *stderr_buffer,
                                      DN_USize            *stderr_size,
                                      DN_U32               timeout_ms,
                                      DN_ErrSink          *err)
{
  DN_OSExecResult result             = {};
  size_t          stdout_buffer_size = 0;
  size_t          stderr_buffer_size = 0;
  if (stdout_size) {
    stdout_buffer_size = *stdout_size;
    *stdout_size       = 0;
  }

  if (stderr_size) {
    stderr_buffer_size = *stderr_size;
    *stderr_size       = 0;
  }

  if (!handle.process || handle.os_error_code || handle.exit_code) {
    if (handle.os_error_code)
      result.os_error_code = handle.os_error_code;
    else
      result.exit_code = handle.exit_code;

    DN_Assert(!handle.stdout_read);
    DN_Assert(!handle.stdout_write);
    DN_Assert(!handle.stderr_read);
    DN_Assert(!handle.stderr_write);
    DN_Assert(!handle.process);
    return result;
  }

  DN_TcScratch scratch                = DN_TcScratchBeginArena(nullptr, 0);
  DWORD        stdout_bytes_available = 0;
  DWORD        stderr_bytes_available = 0;
  PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr);
  PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr);

  DWORD exec_result = WAIT_TIMEOUT;
  if (stdout_bytes_available == 0 && stderr_bytes_available == 0)
    exec_result = WaitForSingleObject(handle.process, timeout_ms);

  if (exec_result == WAIT_FAILED) {
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    result.os_error_code  = win_error.code;
    DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
    return result;
  }

  if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) {
    // NOTE: Read stdout from process
    // If the pipes are full, the process will block. We periodically
    // flush the pipes to make sure this doesn't happen
    char sink[DN_Kilobytes(8)];
    stdout_bytes_available = 0;
    if (PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr)) {
      if (stdout_bytes_available) {
        DWORD    bytes_read  = 0;
        char    *dest_buffer = handle.stdout_write && stdout_buffer ? stdout_buffer : sink;
        DN_USize dest_size   = handle.stdout_write && stdout_buffer ? stdout_buffer_size : DN_ArrayCountU(sink);
        BOOL     success     = ReadFile(handle.stdout_read, dest_buffer, DN_Cast(DWORD) dest_size, &bytes_read, NULL);
        if (success) {
          if (stdout_size)
            *stdout_size = bytes_read;
        } else {
          DN_ErrSinkAppendF(err, 1, "Failed to read bytes from stdout");
        }
      }
    }

    // NOTE: Read stderr from process
    stderr_bytes_available = 0;
    if (PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr)) {
      if (stderr_bytes_available) {
        char  *dest_buffer = handle.stderr_write && stderr_buffer ? stderr_buffer : sink;
        size_t dest_size   = handle.stderr_write && stderr_buffer ? stderr_buffer_size : DN_ArrayCountU(sink);
        DWORD  bytes_read  = 0;
        BOOL   success     = ReadFile(handle.stderr_read, dest_buffer, DN_Cast(DWORD) dest_size, &bytes_read, NULL);
        if (success) {
          if (stderr_size)
            *stderr_size = bytes_read;
        } else {
          DN_ErrSinkAppendF(err, 1, "Failed to read bytes from stderr");
        }
      }
    }
  }

  result.finished = exec_result == WAIT_OBJECT_0 || exec_result == WAIT_FAILED;
  if (exec_result == WAIT_OBJECT_0) {
    DWORD exit_status;
    if (GetExitCodeProcess(handle.process, &exit_status)) {
      result.exit_code = exit_status;
    } else {
      DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
      result.os_error_code  = win_error.code;
      DN_ErrSinkAppendF(err,
                         result.os_error_code,
                         "Failed to retrieve command exit code: %.*s",
                         DN_Str8PrintFmt(win_error.msg));
    }

    // NOTE: Cleanup
    if (handle.stdout_write)
      CloseHandle(handle.stdout_write);
    if (handle.stderr_write)
      CloseHandle(handle.stderr_write);
    if (handle.stdout_read)
      CloseHandle(handle.stdout_read);
    if (handle.stderr_read)
      CloseHandle(handle.stderr_read);
    if (handle.process)
      CloseHandle(handle.process);
  }

  result.stdout_text = DN_Str8FromPtr(stdout_buffer, stdout_size ? *stdout_size : 0);
  result.stderr_text = DN_Str8FromPtr(stderr_buffer, stderr_size ? *stderr_size : 0);
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_ErrSink *err)
{
  DN_OSExecResult result = {};
  if (!handle.process || handle.os_error_code || handle.exit_code) {
    result.finished = true;
    if (handle.os_error_code)
      result.os_error_code = handle.os_error_code;
    else
      result.exit_code = handle.exit_code;

    DN_Assert(!handle.stdout_read);
    DN_Assert(!handle.stdout_write);
    DN_Assert(!handle.stderr_read);
    DN_Assert(!handle.stderr_write);
    DN_Assert(!handle.process);
    return result;
  }

  DN_TcScratch   scratch        = DN_TcScratchBeginArena(&arena, 1);
  DN_Str8Builder stdout_builder = {};
  DN_Str8Builder stderr_builder = {};
  if (arena) {
    stdout_builder = DN_Str8BuilderFromArena(&scratch.arena);
    stderr_builder = DN_Str8BuilderFromArena(&scratch.arena);
  }

  DN_U32 const SLOW_WAIT_TIME_MS = 100;
  DN_U32 const FAST_WAIT_TIME_MS = 20;
  DN_U32       wait_ms           = FAST_WAIT_TIME_MS;
  while (!result.finished) {
    size_t stdout_size   = DN_Kilobytes(8);
    size_t stderr_size   = DN_Kilobytes(8);
    char  *stdout_buffer = DN_ArenaNewArray(&scratch.arena, char, stdout_size, DN_ZMem_No);
    char  *stderr_buffer = DN_ArenaNewArray(&scratch.arena, char, stderr_size, DN_ZMem_No);
    result               = DN_OS_ExecPump(handle, stdout_buffer, &stdout_size, stderr_buffer, &stderr_size, wait_ms, err);
    DN_Str8BuilderAppendCopy(&stdout_builder, result.stdout_text);
    DN_Str8BuilderAppendCopy(&stderr_builder, result.stderr_text);
    wait_ms = (result.stdout_text.count || result.stderr_text.count) ? FAST_WAIT_TIME_MS : SLOW_WAIT_TIME_MS;
  }

  // NOTE: Get stdout/stderr. If no arena is passed this is a no-op
  result.stdout_text = DN_Str8FromStr8BuilderArena(&stdout_builder, arena);
  result.stderr_text = DN_Str8FromStr8BuilderArena(&stderr_builder, arena);
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs args, DN_ErrSink *err)
{
  // NOTE: Pre-amble
  DN_OSExecAsyncHandle result = {};
  if (cmd_line.count == 0)
    return result;

  // NOTE: Render the command line into single string to pass into the Win32 API. We'll quote the
  // string on behalf of the user if there's whitespace in the string
  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  DN_Str8      cmd     = {};
  {
    DN_USize count_req     = 0;
    DN_Str8  cmd_separator = DN_Str8Lit(" ");
    for (DN_ForIt(it, DN_Str8, &cmd_line)) {
      if (it.index)
        count_req += cmd_separator.count;

      if (!DN_Str8StartsWithSensitive(*it.data, DN_Str8Lit("\""))) {
        if (DN_Str8Find(*it.data, DN_Str8FindFlag_Whitespace).found)
          count_req += 2; // If there's whitespace, we'll quote the string for the user
      }
      count_req += it.data->count;
    }

    // NOTE: Build the string
    cmd = DN_Str8AllocArena(count_req, DN_ZMem_No, &scratch.arena);
    if (cmd.data) {
      DN_USize write_index = 0;
      for (DN_ForIt(it, DN_Str8, &cmd_line)) {
        if (it.index) {
          DN_Memcpy(cmd.data + write_index, cmd_separator.data, cmd_separator.count);
          write_index += cmd_separator.count;
        }

        // NOTE: Quote the string if needed
        DN_Str8 str8      = *it.data;
        bool needs_quotes = false;
        if (!DN_Str8StartsWithSensitive(*it.data, DN_Str8Lit("\"")))
          needs_quotes = DN_Str8Find(str8, DN_Str8FindFlag_Whitespace).found;

        if (needs_quotes)
          cmd.data[write_index++] = '\"';

        // NOTE: Write contents
        DN_Memcpy(cmd.data + write_index, str8.data, str8.count);
        write_index += str8.count;

        // NOTE: Close quote if needed
        if (needs_quotes)
          cmd.data[write_index++] = '\"';
      }
      DN_Assert(write_index == cmd.count);
    }
  }
  DN_AssertF(cmd.count < DN_Kilobytes(32), "Command exceeds the allowable size in Win32");

  DN_Str16       cmd16         = DN_OS_W32Str8ToStr16(&scratch.arena, cmd);
  DN_Str16       working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args.working_dir);
  DN_Str8Builder env_builder   = DN_Str8BuilderFromArena(&scratch.arena);
  DN_Str8BuilderAppendArrayRef(&env_builder, args.environment.data, args.environment.count);
  if (env_builder.string_size)
    DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0"));

  DN_Str8  env_block8  = DN_Str8FromStr8BuilderDelimitArena(&env_builder, DN_Str8Lit("\0"), &scratch.arena);
  DN_Str16 env_block16 = {};
  if (env_block8.count)
    env_block16 = DN_OS_W32Str8ToStr16(&scratch.arena, env_block8);

  // NOTE: Stdout/err security attributes
  SECURITY_ATTRIBUTES save_std_security_attribs = {};
  save_std_security_attribs.nLength             = sizeof(save_std_security_attribs);
  save_std_security_attribs.bInheritHandle      = true;

  // NOTE: Redirect stdout
  HANDLE stdout_read  = {};
  HANDLE stdout_write = {};
  DN_DEFER
  {
    if (result.os_error_code || result.exit_code) {
      CloseHandle(stdout_read);
      CloseHandle(stdout_write);
    }
  };

  if (DN_BitIsSet(args.flags, DN_OSExecFlags_SaveStdout)) {
    if (!CreatePipe(&stdout_read, &stdout_write, &save_std_security_attribs, /*nSize*/ 0)) {
      DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
      result.os_error_code    = win_error.code;
      DN_ErrSinkAppendF(
          err,
          result.os_error_code,
          "Failed to create stdout pipe to redirect the output of the command '%.*s': %.*s",
          DN_Str8PrintFmt(cmd),
          DN_Str8PrintFmt(win_error.msg));
      DN_TcScratchEnd(&scratch);
      return result;
    }

    if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
      DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
      result.os_error_code  = win_error.code;
      DN_ErrSinkAppendF(err,
                         result.os_error_code,
                         "Failed to make stdout 'read' pipe non-inheritable when trying to "
                         "execute command '%.*s': %.*s",
                         DN_Str8PrintFmt(cmd),
                         DN_Str8PrintFmt(win_error.msg));
      DN_TcScratchEnd(&scratch);
      return result;
    }
  }

  // NOTE: Redirect stderr
  HANDLE stderr_read  = {};
  HANDLE stderr_write = {};
  DN_DEFER
  {
    if (result.os_error_code || result.exit_code) {
      CloseHandle(stderr_read);
      CloseHandle(stderr_write);
    }
  };

  if (DN_BitIsSet(args.flags, DN_OSExecFlags_SaveStderr)) {
    if (DN_BitIsSet(args.flags, DN_OSExecFlags_MergeStderrToStdout)) {
      stderr_read  = stdout_read;
      stderr_write = stdout_write;
    } else {
      if (!CreatePipe(&stderr_read, &stderr_write, &save_std_security_attribs, /*nSize*/ 0)) {
        DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
        result.os_error_code  = win_error.code;
        DN_ErrSinkAppendF(
            err,
            result.os_error_code,
            "Failed to create stderr pipe to redirect the output of the command '%.*s': %.*s",
            DN_Str8PrintFmt(cmd),
            DN_Str8PrintFmt(win_error.msg));
        DN_TcScratchEnd(&scratch);
        return result;
      }

      if (!SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) {
        DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
        result.os_error_code  = win_error.code;
        DN_ErrSinkAppendF(err,
                          result.os_error_code,
                          "Failed to make stderr 'read' pipe non-inheritable when trying to "
                          "execute command '%.*s': %.*s",
                          DN_Str8PrintFmt(cmd),
                          DN_Str8PrintFmt(win_error.msg));
        DN_TcScratchEnd(&scratch);
        return result;
      }
    }
  }

  // NOTE: Create a null stdin pipe so child gets EOF on read
  // TODO: Eventually we should let the caller optionally write to stdin
  HANDLE stdin_read  = {};
  if (DN_BitIsSet(args.flags, DN_OSExecFlags_SaveStdout) || DN_BitIsSet(args.flags, DN_OSExecFlags_SaveStderr)) {
    HANDLE stdin_write = {};
    if (CreatePipe(&stdin_read, &stdin_write, &save_std_security_attribs, 0))
      CloseHandle(stdin_write);
  }

  // NOTE: Execute command
  PROCESS_INFORMATION proc_info     = {};
  STARTUPINFOW        startup_info  = {};
  startup_info.cb                   = sizeof(STARTUPINFOW);
  startup_info.hStdError            = stderr_write ? stderr_write : GetStdHandle(STD_ERROR_HANDLE);
  startup_info.hStdOutput           = stdout_write ? stdout_write : GetStdHandle(STD_OUTPUT_HANDLE);
  startup_info.hStdInput            = stdin_read ? stdin_read : GetStdHandle(STD_INPUT_HANDLE);
  startup_info.dwFlags             |= STARTF_USESTDHANDLES;
  BOOL create_result = CreateProcessW(nullptr,
                                      cmd16.data,
                                      nullptr,
                                      nullptr,
                                      true,
                                      CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
                                      env_block16.data,
                                      working_dir16.data,
                                      &startup_info,
                                      &proc_info);
  if (stdin_read)
    CloseHandle(stdin_read);

  if (!create_result) {
    DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
    result.os_error_code  = win_error.code;
    DN_ErrSinkAppendF(err, result.os_error_code, "Failed to execute command '%.*s': %.*s", DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg));
    DN_TcScratchEnd(&scratch);
    return result;
  }

  // NOTE: Post-amble
  CloseHandle(proc_info.hThread);
  result.process      = proc_info.hProcess;
  result.stdout_read  = stdout_read;
  result.stdout_write = stdout_write;
  if (DN_BitIsSet(args.flags, DN_OSExecFlags_SaveStderr) && DN_BitIsNotSet(args.flags, DN_OSExecFlags_MergeStderrToStdout)) {
    result.stderr_read  = stderr_read;
    result.stderr_write = stderr_write;
  }
  result.exec_flags = args.flags;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_OSW32Core *DN_OS_W32GetCore()
{
  DN_Core *dn = DN_Get();
  DN_Assert(dn && dn->os_init);
  DN_OSW32Core *result = DN_Cast(DN_OSW32Core *)dn->os.platform_context;
  return result;
}

static DN_OSW32SyncPrimitive *DN_OS_U64ToW32SyncPrimitive_(DN_U64 u64)
{
  DN_OSW32SyncPrimitive *result = nullptr;
  DN_Memcpy(&result, &u64, sizeof(u64));
  return result;
}

static DN_U64 DN_OS_W32SyncPrimitiveToU64(DN_OSW32SyncPrimitive *primitive)
{
  DN_U64 result = 0;
  static_assert(sizeof(result) == sizeof(primitive), "Pointer size mis-match");
  DN_Memcpy(&result, &primitive, sizeof(result));
  return result;
}

static DN_OSW32SyncPrimitive *DN_OS_W32AllocSyncPrimitive_()
{
  DN_OSW32Core          *w32    = DN_OS_W32GetCore();
  DN_OSW32SyncPrimitive *result = nullptr;
  EnterCriticalSection(&w32->sync_primitive_free_list_mutex);
  {
    if (w32->sync_primitive_free_list) {
      result                        = w32->sync_primitive_free_list;
      w32->sync_primitive_free_list = w32->sync_primitive_free_list->next;
      result->next                  = nullptr;
    } else {
      DN_OSCore *os = &DN_Get()->os;
      result        = DN_ArenaNew(&os->arena, DN_OSW32SyncPrimitive, DN_ZMem_Yes);
    }
  }
  LeaveCriticalSection(&w32->sync_primitive_free_list_mutex);
  return result;
}

static void DN_OS_W32DeallocSyncPrimitive_(DN_OSW32SyncPrimitive *primitive)
{
  if (primitive) {
    DN_OSW32Core *w32               = DN_OS_W32GetCore();
    EnterCriticalSection(&w32->sync_primitive_free_list_mutex);
    primitive->next               = w32->sync_primitive_free_list;
    w32->sync_primitive_free_list = primitive;
    LeaveCriticalSection(&w32->sync_primitive_free_list_mutex);
  }
}

// NOTE: DN_OSSemaphore
DN_API DN_OSSemaphore DN_OS_SemaphoreInit(DN_U32 initial_count)
{
  DN_OSSemaphore         result    = {};
  DN_OSW32SyncPrimitive *primitive = DN_OS_W32AllocSyncPrimitive_();
  if (primitive) {
    SECURITY_ATTRIBUTES security_attribs = {};
    primitive->sem                       = CreateSemaphoreA(&security_attribs, initial_count, INT32_MAX, nullptr /*name*/);
    if (primitive->sem)
      result.handle = DN_OS_W32SyncPrimitiveToU64(primitive);
    if (!primitive->sem)
      DN_OS_W32DeallocSyncPrimitive_(primitive);
  }
  return result;
}

DN_API void DN_OS_SemaphoreDeinit(DN_OSSemaphore *semaphore)
{
  if (semaphore && semaphore->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
    CloseHandle(primitive->sem);
    DN_OS_W32DeallocSyncPrimitive_(primitive);
    *semaphore = {};
  }
}

DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount)
{
  if (semaphore && semaphore->handle != 0) {
    DN_OSW32SyncPrimitive *primitive  = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
    LONG                 prev_count = 0;
    ReleaseSemaphore(primitive->sem, amount, &prev_count);
  }
}

DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait(DN_OSSemaphore *semaphore, DN_U32 timeout_ms)
{
  DN_OSSemaphoreWaitResult result = {};
  if (semaphore && semaphore->handle != 0) {
    DN_OSW32SyncPrimitive *primitive   = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
    DWORD                wait_result = WaitForSingleObject(primitive->sem, timeout_ms == DN_OS_SEMAPHORE_INFINITE_TIMEOUT ? INFINITE : timeout_ms);
    if (wait_result == WAIT_TIMEOUT)
      result = DN_OSSemaphoreWaitResult_Timeout;
    else if (wait_result == WAIT_OBJECT_0)
      result = DN_OSSemaphoreWaitResult_Success;
  }
  return result;
}

// NOTE: DN_OSBarrier
DN_API DN_OSBarrier DN_OS_BarrierInit(DN_U32 thread_count)
{
  DN_OSBarrier           result    = {};
  DN_OSW32SyncPrimitive *primitive = DN_OS_W32AllocSyncPrimitive_();
  if (primitive) {
    BOOL init_result = InitializeSynchronizationBarrier(&primitive->barrier, thread_count, /*lSpinCount=*/-1);
    if (init_result) {
      result.handle = DN_OS_W32SyncPrimitiveToU64(primitive);
    } else {
      DN_OS_W32DeallocSyncPrimitive_(primitive);
    }
  }
  return result;
}

DN_API void DN_OS_BarrierDeinit(DN_OSBarrier *barrier)
{
  if (barrier && barrier->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(barrier->handle);
    bool                   result    = DeleteSynchronizationBarrier(&primitive->barrier);
    DN_Assert(result);
    DN_OS_W32DeallocSyncPrimitive_(primitive);
  }
}

DN_API void DN_OS_BarrierWait(DN_OSBarrier *barrier)
{
  if (barrier && barrier->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(barrier->handle);
    EnterSynchronizationBarrier(&primitive->barrier, /*dwFlags=*/ 0);
  }
}

// NOTE: DN_OSMutex
DN_API DN_OSMutex DN_OS_MutexInit()
{
  DN_OSW32SyncPrimitive *primitive = DN_OS_W32AllocSyncPrimitive_();
  if (primitive)
    InitializeCriticalSection(&primitive->mutex);
  DN_OSMutex result = {};
  result.handle     = DN_OS_W32SyncPrimitiveToU64(primitive);
  return result;
}

DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex)
{
  if (mutex && mutex->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
    DeleteCriticalSection(&primitive->mutex);
    DN_OS_W32DeallocSyncPrimitive_(primitive);
    *mutex = {};
  }
}

DN_API void DN_OS_MutexLock(DN_OSMutex *mutex)
{
  if (mutex && mutex->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
    EnterCriticalSection(&primitive->mutex);
  }
}

DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex)
{
  if (mutex && mutex->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
    LeaveCriticalSection(&primitive->mutex);
  }
}

// NOTE: DN_OSConditionVariable ////////////////////////////////////////////////////////////////////
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit()
{
  DN_OSW32SyncPrimitive *primitive = DN_OS_W32AllocSyncPrimitive_();
  if (primitive)
    InitializeConditionVariable(&primitive->cv);
  DN_OSConditionVariable result = {};
  result.handle                 = DN_OS_W32SyncPrimitiveToU64(primitive);
  return result;
}

DN_API void DN_OS_ConditionVariableDeinit(DN_OSConditionVariable *cv)
{
  if (cv && cv->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
    DN_OS_W32DeallocSyncPrimitive_(primitive);
    *cv = {};
  }
}

DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms)
{
  bool   result = false;
  DN_U64 now_ms = DN_OS_DateUnixTimeNs() / (1000 * 1000);
  if (now_ms < end_ts_ms) {
    DN_U64 sleep_ms = end_ts_ms - now_ms;
    result          = DN_OS_ConditionVariableWait(cv, mutex, sleep_ms);
  }
  return result;
}

DN_API bool DN_OS_ConditionVariableWait(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms)
{
  bool result = false;
  if (mutex && cv && mutex->handle != 0 && cv->handle != 0 && sleep_ms > 0) {
    DN_OSW32SyncPrimitive *mutex_primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
    DN_OSW32SyncPrimitive *cv_primitive    = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
    result                               = SleepConditionVariableCS(&cv_primitive->cv, &mutex_primitive->mutex, DN_Cast(DWORD) sleep_ms);
  }
  return result;
}

DN_API void DN_OS_ConditionVariableSignal(DN_OSConditionVariable *cv)
{
  if (cv && cv->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
    WakeConditionVariable(&primitive->cv);
  }
}

DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv)
{
  if (cv && cv->handle != 0) {
    DN_OSW32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
    WakeAllConditionVariable(&primitive->cv);
  }
}

DN_API DN_OSWindowMinimise DN_OS_WindowIsMinimised(DN_OSWindow *window)
{
  HWND                hwnd   = window ? DN_Cast(HWND) window->handle : nullptr;
  DN_OSWindowMinimise result = DN_OSWindowMinimise_No;
  if (hwnd && IsIconic(hwnd) != 0)
    result = DN_OSWindowMinimise_Yes;
  return result;
}

DN_API DN_OSWindowMaximise DN_OS_WindowIsMaximised(DN_OSWindow *window)
{
  HWND                hwnd   = window ? DN_Cast(HWND) window->handle : nullptr;
  DN_OSWindowMaximise result = DN_OSWindowMaximise_No;
  if (hwnd && IsZoomed(hwnd) != 0)
    result = DN_OSWindowMaximise_Yes;
  return result;
}

DN_API void DN_OS_WindowSetTitle(DN_OSWindow *window, DN_Str8 title)
{
  HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr;
  if (hwnd) {
    DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
    DN_Str16     title16 = DN_OS_W32Str8ToStr16(&scratch.arena, title);
    SetWindowTextW(hwnd, (WCHAR *)title16.data);
    DN_TcScratchEnd(&scratch);
  }
}

DN_API void DN_OS_WindowShow(DN_OSWindow *window, DN_OSWindowShow show)
{
  switch (show) {
    case DN_OSWindowShow_Nil:      break;
    case DN_OSWindowShow_Restore:  ShowWindow(DN_Cast(HWND)window->handle, SW_RESTORE); break;
    case DN_OSWindowShow_Minimise: ShowWindow(DN_Cast(HWND)window->handle, SW_MINIMIZE); break;
    case DN_OSWindowShow_Maximise: ShowWindow(DN_Cast(HWND)window->handle, SW_MAXIMIZE); break;
  }
}

DN_API void DN_OS_WindowBringToFront(DN_OSWindow *window)
{
  HWND hwnd = window ? DN_Cast(HWND)window->handle : nullptr;
  if (hwnd)
    BringWindowToTop(hwnd);
}

DN_API void DN_OS_WindowFocus(DN_OSWindow *window)
{
  HWND hwnd = window ? DN_Cast(HWND)window->handle : nullptr;
  if (hwnd) {
    SetForegroundWindow(hwnd);
    SetFocus(hwnd);
  }
}

DN_API bool DN_OS_WindowIsFocused(DN_OSWindow *window)
{
  bool result = false;
  HWND hwnd   = window ? DN_Cast(HWND) window->handle : nullptr;
  if (hwnd)
    result = hwnd == GetForegroundWindow();
  return result;
}

static DWORD __stdcall DN_OS_ThreadFunc_(void *user_context)
{
  DN_OS_ThreadExecute_(user_context);
  return 0;
}

DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context)
{
  bool result = false;
  if (!thread)
    return result;

  DN_OS_ThreadPreInit_(thread, func, lane, init_args, user_context);

  // TODO(doyle): Check if semaphore is valid
  DWORD               thread_id        = 0;
  SECURITY_ATTRIBUTES security_attribs = {};
  thread->handle                       = CreateThread(&security_attribs,
                                                      init_args.stack_size,
                                                      DN_OS_ThreadFunc_,
                                                      thread,
                                                      0 /*creation_flags*/,
                                                      &thread_id);

  result = thread->handle != NULL;
  if (result) {
    thread->thread_id = thread_id;
    if (thread->flags & DN_OSThreadFlags_Detached) {
      CloseHandle(thread->handle);
      thread->handle = NULL;
    }
  }

  DN_OS_ThreadPostInit_(thread, result);
  return result;
}

DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_U32 timeout_ms, DN_TcDeinitArenas deinit_arenas)
{
  bool result = true;
  if (thread && thread->handle && thread->handle != INVALID_HANDLE_VALUE) {
    DN_AssertF(DN_BitIsNotSet(thread->flags, DN_OSThreadFlags_Detached),
               "Detached threads should have their handle immediately closed and invalidated so this branch should never hit.");
    DWORD wait_result = WaitForSingleObject(thread->handle, timeout_ms);
    if (wait_result == WAIT_OBJECT_0) {
      CloseHandle(thread->handle);
      thread->handle    = INVALID_HANDLE_VALUE;
      thread->thread_id = {};
      DN_TcDeinit(&thread->context, deinit_arenas);
    } else {
      result = false;
    }
  }
  return result;
}

DN_API DN_U32 DN_OS_ThreadID()
{
  unsigned long result = GetCurrentThreadId();
  return result;
}

DN_API void DN_OS_W32ThreadSetName(DN_Str8 name)
{
  // NOTE: SetThreadDescription is only available in
  // Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607
  //
  // See: https://learn.microsoft.com/en-us/windows/w32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription
  DN_OSW32Core  *w32     = DN_OS_W32GetCore();
  DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0);
  if (w32->set_thread_description) {
    DN_Str16 name16 = DN_OS_W32Str8ToStr16(&scratch.arena, name);
    w32->set_thread_description(GetCurrentThread(), (WCHAR *)name16.data);
  } else {
    // NOTE: Fallback to throw-exception method to set thread name
    #pragma pack(push, 8)
    struct DN_OSW32ThreadNameInfo
    {
      DN_U32 dwType;
      char  *szName;
      DN_U32 dwThreadID;
      DN_U32 dwFlags;
    };
    #pragma pack(pop)

    DN_Str8                copy = DN_Str8FromStr8Arena(name, &scratch.arena);
    DN_OSW32ThreadNameInfo info = {};
    info.dwType                 = 0x1000;
    info.szName                 = (char *)copy.data;
    info.dwThreadID             = DN_OS_ThreadID();

    // TODO: Review warning 6320
    DN_MSVC_WARNING_PUSH
    DN_MSVC_WARNING_DISABLE(6320) // Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled
    DN_MSVC_WARNING_DISABLE(6322) // Empty _except block
    __try {
      RaiseException(0x406D1388, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
    }
    DN_MSVC_WARNING_POP
  }
  DN_TcScratchEnd(&scratch);
}

DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
{
  DWORD flags                     = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
  void *module_to_get_errors_from = nullptr;
  if (error_code >= 12000 && error_code <= 12175) {
    flags |= FORMAT_MESSAGE_FROM_HMODULE;
    module_to_get_errors_from = GetModuleHandleA("winhttp.dll");
  }

  wchar_t *result16 = nullptr;
  DWORD    count    = FormatMessageW(/*DWORD    dwFlags     */ flags | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                     /*LPCVOID  lpSource    */ module_to_get_errors_from,
                                     /*DWORD    dwMessageId */ error_code,
                                     /*DWORD    dwLanguageId*/ 0,
                                     /*LPWSTR   lpBuffer    */ (LPWSTR)&result16,
                                     /*DWORD    nSize       */ 0,
                                     /*va_list *Arguments   */ nullptr);

  DN_Str16 result = {};
  result.data     = result16;
  result.count    = count;
  return result;
}

DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc(DN_U32 error_code)
{
  DN_OSW32Error result = {};
  result.code        = error_code;
  DN_Str16 error16   = DN_OS_W32ErrorCodeToMsg16Alloc(error_code);
  if (error16.count)
    result.msg = DN_OS_W32Str16ToStr8FromHeap(error16);
  if (error16.data)
    LocalFree(error16.data);
  return result;
}

DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg(DN_Arena *arena, DN_U32 error_code)
{
  DN_OSW32Error result = {};
  result.code        = error_code;
  if (arena) {
    DN_Str16 error16 = DN_OS_W32ErrorCodeToMsg16Alloc(error_code);
    if (error16.count)
      result.msg = DN_OS_W32Str16ToStr8(arena, error16);
    if (error16.data)
      LocalFree(error16.data);
  }
  return result;
}

DN_API DN_OSW32Error DN_OS_W32LastError(DN_Arena *arena)
{
  DN_OSW32Error result = DN_OS_W32ErrorCodeToMsg(arena, GetLastError());
  return result;
}

DN_API DN_OSW32Error DN_OS_W32LastErrorAlloc()
{
  DN_OSW32Error result = DN_OS_W32ErrorCodeToMsgAlloc(GetLastError());
  return result;
}

DN_API void DN_OS_W32MakeProcessDPIAware()
{
  typedef bool SetProcessDpiAwareProc(void);
  typedef bool SetProcessDpiAwarenessProc(DPI_AWARENESS);
  typedef bool SetProcessDpiAwarenessContextProc(void * /*DPI_AWARENESS_CONTEXT*/);

  // NOTE(doyle): Taken from cmuratori/refterm snippet on DPI awareness. It
  // appears we can make this robust by just loading user32.dll and using
  // GetProcAddress on the DPI function. If it's not there, we're on an old
  // version of windows, so we can call an older version of the API.
  void *lib_handle = LoadLibraryA("user32.dll");
  if (!lib_handle)
    return;

  if (auto *set_process_dpi_awareness_context = DN_Cast(SetProcessDpiAwarenessContextProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAwarenessContext"))
    set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
  else if (auto *set_process_dpi_awareness = DN_Cast(SetProcessDpiAwarenessProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAwareness"))
    set_process_dpi_awareness(DPI_AWARENESS_PER_MONITOR_AWARE);
  else if (auto *set_process_dpi_aware = DN_Cast(SetProcessDpiAwareProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAware"))
    set_process_dpi_aware();
}

DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
{
  DN_Str16 result = {};
  if (!arena || src.count == 0)
    return result;

  int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, nullptr /*dest*/, 0 /*dest size*/);
  if (required_size <= 0)
    return result;

  wchar_t *buffer = DN_ArenaNewArray(arena, wchar_t, required_size + 1, DN_ZMem_No);
  if (!buffer)
    return result;

  int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, buffer, required_size);
  DN_Assert(chars_written == required_size);
  if (chars_written == required_size) {
    result.data               = buffer;
    result.count              = chars_written;
    result.data[result.count] = 0;
  }
  return result;
}

DN_API int DN_OS_W32Str8ToStr16Buffer(DN_Str8 src, wchar_t *dest, int dest_size)
{
  int result = 0;
  if (src.count == 0)
    return result;

  result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, nullptr /*dest*/, 0 /*dest size*/);
  if (result <= 0 || result > dest_size || !dest)
    return result;

  result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.count, dest, DN_Cast(int) dest_size);
  dest[DN_Min(result, dest_size - 1)] = 0;
  return result;
}

DN_API int DN_OS_W32Str16ToStr8Buffer(DN_Str16 src, char *dest, int dest_size)
{
  int result = 0;
  if (src.count == 0)
    return result;

  int src_size = DN_SaturateCastISizeToInt(src.count);
  if (src_size <= 0)
    return result;

  result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
  if (result <= 0 || result > dest_size || !dest)
    return result;

  result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, dest, DN_Cast(int) dest_size, nullptr, nullptr);
  dest[DN_Min(result, dest_size - 1)] = 0;
  return result;
}

DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src)
{
  DN_Str8 result = {};
  if (!arena || src.count == 0)
    return result;

  int src_size = DN_SaturateCastISizeToInt(src.count);
  if (src_size <= 0)
    return result;

  int required_size = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
  if (required_size <= 0)
    return result;

  // NOTE: Str8 allocate ensures there's one extra byte for
  // null-termination already so no-need to +1 the required size
  DN_Arena temp   = DN_ArenaTempBeginFromArena(arena);
  DN_Str8  buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp);
  if (buffer.count) {
    int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.count, nullptr, nullptr);
    DN_Assert(chars_written == required_size);
    if (chars_written == required_size) {
      result                   = buffer;
      result.data[result.count] = 0;
    }
  }
  DN_ArenaTempEnd(&temp, result.count == DN_Cast(DN_USize)required_size ? DN_ArenaReset_No : DN_ArenaReset_Yes);
  return result;
}

DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
{
  DN_Str8 result = {};
  if (src.count == 0)
    return result;

  int src_size = DN_SaturateCastISizeToInt(src.count);
  if (src_size <= 0)
    return result;

  int required_size = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
  if (required_size <= 0)
    return result;

  DN_Str8 buffer = {};
  buffer.data    = DN_Cast(char *) DN_OS_MemAlloc(required_size + 1, DN_ZMem_No);
  buffer.count   = required_size;
  if (!buffer.data)
    return result;

  int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.count, nullptr, nullptr);
  DN_Assert(chars_written == required_size);
  if (chars_written == required_size) {
    result                   = buffer;
    result.data[result.count] = 0;
  } else {
    DN_OS_MemDealloc(buffer.data);
    buffer = {};
  }

  return result;
}

// NOTE: Windows Executable Directory //////////////////////////////////////////
DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena)
{
  DN_TcScratch scratch        = DN_TcScratchBeginArena(&arena, 1);
  DN_Str16   result      = {};
  DN_USize   module_size = 0;
  wchar_t   *module_path = nullptr;
  do {
    module_size += 256;
    module_path = DN_ArenaNewArray(&scratch.arena, wchar_t, module_size, DN_ZMem_No);
    if (!module_path) {
      DN_TcScratchEnd(&scratch);
      return result;
    }
    module_size = DN_Cast(DN_USize) GetModuleFileNameW(nullptr /*module*/, module_path, DN_Cast(int) module_size);
  } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

  DN_USize index_of_last_slash = 0;
  for (DN_USize index = module_size - 1; !index_of_last_slash && index < module_size; index--)
    index_of_last_slash = module_path[index] == '\\' ? index : 0;

  result.data = DN_ArenaNewArray(arena, wchar_t, module_size + 1, DN_ZMem_No);
  result.count = module_size;
  DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.count);
  result.data[result.count] = 0;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_Str16 DN_OS_W32ExeDirW(DN_Arena *arena)
{
  // TODO(doyle): Implement a DN_Str16_BinarySearchReverse
  DN_TcScratch scratch        = DN_TcScratchBeginArena(&arena, 1);
  DN_Str16   result      = {};
  DN_USize   module_size = 0;
  wchar_t   *module_path = nullptr;
  do {
    module_size += 256;
    module_path = DN_ArenaNewArray(&scratch.arena, wchar_t, module_size, DN_ZMem_No);
    if (!module_path) {
      DN_TcScratchEnd(&scratch);
      return result;
    }
    module_size = DN_Cast(DN_USize) GetModuleFileNameW(nullptr /*module*/, module_path, DN_Cast(int) module_size);
  } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

  DN_USize index_of_last_slash = 0;
  for (DN_USize index = module_size - 1; !index_of_last_slash && index < module_size; index--)
    index_of_last_slash = module_path[index] == '\\' ? index : 0;

  result.data = DN_ArenaNewArray(arena, wchar_t, index_of_last_slash + 1, DN_ZMem_No);
  result.count = index_of_last_slash;
  DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.count);
  result.data[result.count] = 0;
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_Str8 DN_OS_W32WorkingDir(DN_Arena *arena, DN_Str8 suffix)
{
  DN_TcScratch scratch  = DN_TcScratchBeginArena(&arena, 1);
  DN_Str16     suffix16 = DN_OS_W32Str8ToStr16(&scratch.arena, suffix);
  DN_Str16     dir16    = DN_OS_W32WorkingDirW(&scratch.arena, suffix16);
  DN_Str8      result   = DN_OS_W32Str16ToStr8(arena, dir16);
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API DN_Str16 DN_OS_W32WorkingDirW(DN_Arena *arena, DN_Str16 suffix)
{
  DN_Assert(suffix.count >= 0);
  DN_Str16 result = {};

  // NOTE: required_size is the size required *including* the null-terminator
  DN_TcScratch  scratch       = DN_TcScratchBeginArena(&arena, 1);
  unsigned long required_size = GetCurrentDirectoryW(0, nullptr);
  unsigned long desired_size  = required_size + DN_Cast(unsigned long) suffix.count;

  wchar_t *scratch_w_path = DN_ArenaNewArray(&scratch.arena, wchar_t, desired_size, DN_ZMem_No);
  if (!scratch_w_path) {
    DN_TcScratchEnd(&scratch);
    return result;
  }

  unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, scratch_w_path);
  if ((bytes_written_wo_null_terminator + 1) != required_size) {
    // TODO(dn): Error
    DN_TcScratchEnd(&scratch);
    return result;
  }

  wchar_t *w_path = DN_ArenaNewArray(arena, wchar_t, desired_size, DN_ZMem_No);
  if (!w_path) {
    DN_TcScratchEnd(&scratch);
    return result;
  }

  if (suffix.count) {
    DN_Memcpy(w_path, scratch_w_path, sizeof(*scratch_w_path) * bytes_written_wo_null_terminator);
    DN_Memcpy(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.count);
    w_path[desired_size] = 0;
  }

  result = DN_Str16{w_path, DN_Cast(DN_USize)(desired_size - 1)};
  DN_TcScratchEnd(&scratch);
  return result;
}

DN_API bool DN_OS_W32DirWIterate(DN_Str16 path, DN_OSW32FolderIteratorW *it)
{
  WIN32_FIND_DATAW find_data = {};
  if (it->handle) {
    if (FindNextFileW(it->handle, &find_data) == 0) {
      FindClose(it->handle);
      return false;
    }
  } else {
    it->handle = FindFirstFileExW(path.data,             /*LPCWSTR lpFileName,*/
                                  FindExInfoStandard,    /*FINDEX_INFO_LEVELS fInfoLevelId,*/
                                  &find_data,            /*LPVOID lpFindFileData,*/
                                  FindExSearchNameMatch, /*FINDEX_SEARCH_OPS fSearchOp,*/
                                  nullptr,               /*LPVOID lpSearchFilter,*/
                                  FIND_FIRST_EX_LARGE_FETCH /*unsigned long dwAdditionalFlags)*/);

    if (it->handle == INVALID_HANDLE_VALUE)
      return false;
  }

  it->file_name_buf[0] = 0;
  it->file_name        = DN_Str16{it->file_name_buf, 0};

  do {
    if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.'))
      continue;

    it->file_name.count = DN_CStr16Count(find_data.cFileName);
    DN_Assert(it->file_name.count < (DN_ArrayCountU(it->file_name_buf) - 1));
    DN_Memcpy(it->file_name.data, find_data.cFileName, it->file_name.count * sizeof(wchar_t));
    it->file_name_buf[it->file_name.count] = 0;
    break;
  } while (FindNextFileW(it->handle, &find_data) != 0);

  bool result = it->file_name.count > 0;
  if (!result)
    FindClose(it->handle);
  return result;
}
