#
#
#            Nim's Runtime Library
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements a small wrapper for some needed Win API procedures,
## so that the Nim compiler does not depend on the huge Windows module.

import dynlib

when defined(nimHasStyleChecks):
  {.push styleChecks: off.}

{.passc: "-DWIN32_LEAN_AND_MEAN".}

const
  useWinUnicode* = not defined(useWinAnsi)

when useWinUnicode:
  type WinChar* = Utf16Char
else:
  type WinChar* = char

# See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
type
  Handle* = int
  LONG* = int32
  ULONG* = int32
  PULONG* = ptr int
  WINBOOL* = int32
    ## `WINBOOL` uses opposite convention as posix, !=0 meaning success.
    # xxx this should be distinct int32, distinct would make code less error prone
  PBOOL* = ptr WINBOOL
  DWORD* = int32
  PDWORD* = ptr DWORD
  LPINT* = ptr int32
  ULONG_PTR* = uint
  PULONG_PTR* = ptr uint
  HDC* = Handle
  HGLRC* = Handle
  BYTE* = uint8

  SECURITY_ATTRIBUTES* {.final, pure.} = object
    nLength*: int32
    lpSecurityDescriptor*: pointer
    bInheritHandle*: WINBOOL

  STARTUPINFO* {.final, pure.} = object
    cb*: int32
    lpReserved*: cstring
    lpDesktop*: cstring
    lpTitle*: cstring
    dwX*: int32
    dwY*: int32
    dwXSize*: int32
    dwYSize*: int32
    dwXCountChars*: int32
    dwYCountChars*: int32
    dwFillAttribute*: int32
    dwFlags*: int32
    wShowWindow*: int16
    cbReserved2*: int16
    lpReserved2*: pointer
    hStdInput*: Handle
    hStdOutput*: Handle
    hStdError*: Handle

  PROCESS_INFORMATION* {.final, pure.} = object
    hProcess*: Handle
    hThread*: Handle
    dwProcessId*: int32
    dwThreadId*: int32

  FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
    dwLowDateTime*: DWORD
    dwHighDateTime*: DWORD

  BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
    dwFileAttributes*: DWORD
    ftCreationTime*: FILETIME
    ftLastAccessTime*: FILETIME
    ftLastWriteTime*: FILETIME
    dwVolumeSerialNumber*: DWORD
    nFileSizeHigh*: DWORD
    nFileSizeLow*: DWORD
    nNumberOfLinks*: DWORD
    nFileIndexHigh*: DWORD
    nFileIndexLow*: DWORD

  OSVERSIONINFO* {.final, pure.} = object
    dwOSVersionInfoSize*: DWORD
    dwMajorVersion*: DWORD
    dwMinorVersion*: DWORD
    dwBuildNumber*: DWORD
    dwPlatformId*: DWORD
    szCSDVersion*: array[0..127, WinChar]

  Protoent* = object
    p_name*: cstring
    p_aliases*: cstringArray
    p_proto*: cshort


const
  STARTF_USESHOWWINDOW* = 1'i32
  STARTF_USESTDHANDLES* = 256'i32
  HIGH_PRIORITY_CLASS* = 128'i32
  IDLE_PRIORITY_CLASS* = 64'i32
  NORMAL_PRIORITY_CLASS* = 32'i32
  REALTIME_PRIORITY_CLASS* = 256'i32
  WAIT_OBJECT_0* = 0'i32
  WAIT_TIMEOUT* = 0x00000102'i32
  WAIT_FAILED* = 0xFFFFFFFF'i32
  INFINITE* = -1'i32
  STILL_ACTIVE* = 0x00000103'i32

  STD_INPUT_HANDLE* = -10'i32
  STD_OUTPUT_HANDLE* = -11'i32
  STD_ERROR_HANDLE* = -12'i32

  DETACHED_PROCESS* = 8'i32

  SW_SHOWNORMAL* = 1'i32
  INVALID_HANDLE_VALUE* = Handle(-1)

  CREATE_UNICODE_ENVIRONMENT* = 1024'i32

  PIPE_ACCESS_DUPLEX* = 0x00000003'i32
  PIPE_ACCESS_INBOUND* = 1'i32
  PIPE_ACCESS_OUTBOUND* = 2'i32
  PIPE_NOWAIT* = 0x00000001'i32
  SYNCHRONIZE* = 0x00100000'i32

  CREATE_NO_WINDOW* = 0x08000000'i32

  HANDLE_FLAG_INHERIT* = 0x00000001'i32

proc isSuccess*(a: WINBOOL): bool {.inline.} =
  ## Returns true if `a != 0`. Windows uses a different convention than POSIX,
  ## where `a == 0` is commonly used on success.
  a != 0
proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
    stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.}
proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
    stdcall, dynlib: "kernel32", importc: "GetVersionExA", sideEffect.}

proc getVersion*(): DWORD {.stdcall, dynlib: "kernel32", importc: "GetVersion", sideEffect.}

proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
    importc: "CloseHandle".}

proc readFile*(hFile: Handle, buffer: pointer, nNumberOfBytesToRead: int32,
               lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "ReadFile", sideEffect.}

proc writeFile*(hFile: Handle, buffer: pointer, nNumberOfBytesToWrite: int32,
                lpNumberOfBytesWritten: ptr int32,
                lpOverlapped: pointer): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "WriteFile", sideEffect.}

proc createPipe*(hReadPipe, hWritePipe: var Handle,
                 lpPipeAttributes: var SECURITY_ATTRIBUTES,
                 nSize: int32): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "CreatePipe", sideEffect.}

proc createNamedPipe*(lpName: WideCString,
                     dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize,
                     nInBufferSize, nDefaultTimeOut: int32,
                     lpSecurityAttributes: ptr SECURITY_ATTRIBUTES): Handle {.
    stdcall, dynlib: "kernel32", importc: "CreateNamedPipeW", sideEffect.}

proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil,
                    nBufferSize: int32 = 0,
                    lpBytesRead: ptr int32 = nil,
                    lpTotalBytesAvail: ptr int32 = nil,
                    lpBytesLeftThisMessage: ptr int32 = nil): bool {.
    stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".}

when useWinUnicode:
  proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
                     lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
                     lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
                     bInheritHandles: WINBOOL, dwCreationFlags: int32,
                     lpEnvironment, lpCurrentDirectory: WideCString,
                     lpStartupInfo: var STARTUPINFO,
                     lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.}

else:
  proc createProcessA*(lpApplicationName, lpCommandLine: cstring,
                       lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
                       lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
                       bInheritHandles: WINBOOL, dwCreationFlags: int32,
                       lpEnvironment: pointer, lpCurrentDirectory: cstring,
                       lpStartupInfo: var STARTUPINFO,
                       lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
      stdcall, dynlib: "kernel32", importc: "CreateProcessA", sideEffect.}


proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
    importc: "SuspendThread", sideEffect.}
proc resumeThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
    importc: "ResumeThread", sideEffect.}

proc waitForSingleObject*(hHandle: Handle, dwMilliseconds: int32): int32 {.
    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", sideEffect.}

proc terminateProcess*(hProcess: Handle, uExitCode: int): WINBOOL {.stdcall,
    dynlib: "kernel32", importc: "TerminateProcess", sideEffect.}

proc getExitCodeProcess*(hProcess: Handle, lpExitCode: var int32): WINBOOL {.
    stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}

proc getStdHandle*(nStdHandle: int32): Handle {.stdcall, dynlib: "kernel32",
    importc: "GetStdHandle".}
proc setStdHandle*(nStdHandle: int32, hHandle: Handle): WINBOOL {.stdcall,
    dynlib: "kernel32", importc: "SetStdHandle", sideEffect.}
proc flushFileBuffers*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
    importc: "FlushFileBuffers", sideEffect.}

proc getLastError*(): int32 {.importc: "GetLastError",
    stdcall, dynlib: "kernel32", sideEffect.}

proc setLastError*(error: int32) {.importc: "SetLastError",
    stdcall, dynlib: "kernel32", sideEffect.}

when useWinUnicode:
  proc formatMessageW*(dwFlags: int32, lpSource: pointer,
                      dwMessageId, dwLanguageId: int32,
                      lpBuffer: pointer, nSize: int32,
                      arguments: pointer): int32 {.
                      importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
else:
  proc formatMessageA*(dwFlags: int32, lpSource: pointer,
                    dwMessageId, dwLanguageId: int32,
                    lpBuffer: pointer, nSize: int32,
                    arguments: pointer): int32 {.
                    importc: "FormatMessageA", stdcall, dynlib: "kernel32".}

proc localFree*(p: pointer) {.
  importc: "LocalFree", stdcall, dynlib: "kernel32".}

when useWinUnicode:
  proc getCurrentDirectoryW*(nBufferLength: int32,
                             lpBuffer: WideCString): int32 {.
    importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
  proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
    importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
  proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
    importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
  proc removeDirectoryW*(lpPathName: WideCString): int32 {.
    importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
  proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.}

  proc getModuleFileNameW*(handle: Handle, buf: WideCString,
                           size: int32): int32 {.importc: "GetModuleFileNameW",
    dynlib: "kernel32", stdcall.}
else:
  proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
    importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
  proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
    importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
  proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
    importc: "CreateDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
  proc removeDirectoryA*(lpPathName: cstring): int32 {.
    importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
  proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.}

  proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {.
    importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}

when useWinUnicode:
  proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
                         flags: DWORD): int32 {.
    importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.}
  proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
                         security: pointer=nil): int32 {.
    importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.}
else:
  proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
                           flags: DWORD): int32 {.
    importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall, sideEffect.}
  proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
                           security: pointer=nil): int32 {.
    importc:"CreateHardLinkA", dynlib: "kernel32", stdcall, sideEffect.}

const
  FILE_ATTRIBUTE_READONLY* = 0x00000001'i32
  FILE_ATTRIBUTE_HIDDEN* = 0x00000002'i32
  FILE_ATTRIBUTE_SYSTEM* = 0x00000004'i32
  FILE_ATTRIBUTE_DIRECTORY* = 0x00000010'i32
  FILE_ATTRIBUTE_ARCHIVE* = 0x00000020'i32
  FILE_ATTRIBUTE_DEVICE* = 0x00000040'i32
  FILE_ATTRIBUTE_NORMAL* = 0x00000080'i32
  FILE_ATTRIBUTE_TEMPORARY* = 0x00000100'i32
  FILE_ATTRIBUTE_SPARSE_FILE* = 0x00000200'i32
  FILE_ATTRIBUTE_REPARSE_POINT* = 0x00000400'i32
  FILE_ATTRIBUTE_COMPRESSED* = 0x00000800'i32
  FILE_ATTRIBUTE_OFFLINE* = 0x00001000'i32
  FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000'i32

  FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000'i32
  FILE_FLAG_OPEN_NO_RECALL* = 0x00100000'i32
  FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
  FILE_FLAG_POSIX_SEMANTICS* = 0x01000000'i32
  FILE_FLAG_BACKUP_SEMANTICS* = 0x02000000'i32
  FILE_FLAG_DELETE_ON_CLOSE* = 0x04000000'i32
  FILE_FLAG_SEQUENTIAL_SCAN* = 0x08000000'i32
  FILE_FLAG_RANDOM_ACCESS* = 0x10000000'i32
  FILE_FLAG_NO_BUFFERING* = 0x20000000'i32
  FILE_FLAG_OVERLAPPED* = 0x40000000'i32
  FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32

  MAX_PATH* = 260

  MOVEFILE_COPY_ALLOWED* = 0x2'i32
  MOVEFILE_CREATE_HARDLINK* = 0x10'i32
  MOVEFILE_DELAY_UNTIL_REBOOT* = 0x4'i32
  MOVEFILE_FAIL_IF_NOT_TRACKABLE* = 0x20'i32
  MOVEFILE_REPLACE_EXISTING* = 0x1'i32
  MOVEFILE_WRITE_THROUGH* = 0x8'i32

type
  WIN32_FIND_DATA* {.pure.} = object
    dwFileAttributes*: int32
    ftCreationTime*: FILETIME
    ftLastAccessTime*: FILETIME
    ftLastWriteTime*: FILETIME
    nFileSizeHigh*: int32
    nFileSizeLow*: int32
    dwReserved0: int32
    dwReserved1: int32
    cFileName*: array[0..(MAX_PATH) - 1, WinChar]
    cAlternateFileName*: array[0..13, WinChar]

when useWinUnicode:
  proc findFirstFileW*(lpFileName: WideCString,
                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
      stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.}
  proc findNextFileW*(hFindFile: Handle,
                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
      stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.}
else:
  proc findFirstFileA*(lpFileName: cstring,
                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
      stdcall, dynlib: "kernel32", importc: "FindFirstFileA", sideEffect.}
  proc findNextFileA*(hFindFile: Handle,
                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
      stdcall, dynlib: "kernel32", importc: "FindNextFileA", sideEffect.}

proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32",
  importc: "FindClose".}

when useWinUnicode:
  proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
                        lpBuffer: WideCString,
                        lpFilePart: var WideCString): int32 {.
                        stdcall, dynlib: "kernel32",
                        importc: "GetFullPathNameW", sideEffect.}
  proc getFileAttributesW*(lpFileName: WideCString): int32 {.
                          stdcall, dynlib: "kernel32",
                          importc: "GetFileAttributesW", sideEffect.}
  proc setFileAttributesW*(lpFileName: WideCString,
                           dwFileAttributes: int32): WINBOOL {.
      stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.}

  proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
                 bFailIfExists: WINBOOL): WINBOOL {.
    importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.}

  proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
    importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.}
  proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
                    flags: DWORD): WINBOOL {.
    importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.}

  proc getEnvironmentStringsW*(): WideCString {.
    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.}
  proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.}

  proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
    stdcall, dynlib: "kernel32", sideEffect.}

else:
  proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
                        lpBuffer: cstring, lpFilePart: var cstring): int32 {.
                        stdcall, dynlib: "kernel32",
                        importc: "GetFullPathNameA", sideEffect.}
  proc getFileAttributesA*(lpFileName: cstring): int32 {.
                          stdcall, dynlib: "kernel32",
                          importc: "GetFileAttributesA", sideEffect.}
  proc setFileAttributesA*(lpFileName: cstring,
                           dwFileAttributes: int32): WINBOOL {.
      stdcall, dynlib: "kernel32", importc: "SetFileAttributesA", sideEffect.}

  proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
                 bFailIfExists: cint): cint {.
    importc: "CopyFileA", stdcall, dynlib: "kernel32", sideEffect.}

  proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
    importc: "MoveFileA", stdcall, dynlib: "kernel32", sideEffect.}
  proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring,
                    flags: DWORD): WINBOOL {.
    importc: "MoveFileExA", stdcall, dynlib: "kernel32", sideEffect.}

  proc getEnvironmentStringsA*(): cstring {.
    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA", sideEffect.}
  proc freeEnvironmentStringsA*(para1: cstring): int32 {.
    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA", sideEffect.}

  proc getCommandLineA*(): cstring {.
    importc: "GetCommandLineA", stdcall, dynlib: "kernel32", sideEffect.}

proc rdFileTime*(f: FILETIME): int64 =
  result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)

proc rdFileSize*(f: WIN32_FIND_DATA): int64 =
  result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)

proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
  importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall, sideEffect.}

proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
                                    importc: "Sleep", sideEffect.}

when useWinUnicode:
  proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile,
                     lpParameters, lpDirectory: WideCString,
                     nShowCmd: int32): Handle{.
      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.}

else:
  proc shellExecuteA*(hwnd: Handle, lpOperation, lpFile,
                     lpParameters, lpDirectory: cstring,
                     nShowCmd: int32): Handle{.
      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA", sideEffect.}

proc getFileInformationByHandle*(hFile: Handle,
  lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle", sideEffect.}

const
  WSADESCRIPTION_LEN* = 256
  WSASYS_STATUS_LEN* = 128
  FD_SETSIZE* = 64
  MSG_PEEK* = 2

  INADDR_ANY* = 0'u32
  INADDR_LOOPBACK* = 0x7F000001
  INADDR_BROADCAST* = -1
  INADDR_NONE* = -1

  ws2dll = "Ws2_32.dll"

proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll, sideEffect.}

type
  SocketHandle* = distinct int

type
  WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object
    wVersion, wHighVersion: int16
    szDescription: array[0..WSADESCRIPTION_LEN, char]
    szSystemStatus: array[0..WSASYS_STATUS_LEN, char]
    iMaxSockets, iMaxUdpDg: int16
    lpVendorInfo: cstring

  SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object
    sa_family*: uint16
    sa_data*: array[0..13, char]

  PSockAddr = ptr SockAddr

  InAddr* {.importc: "IN_ADDR", header: "winsock2.h", union.} = object
    s_addr*: uint32  # IP address

  Sockaddr_in* {.importc: "SOCKADDR_IN",
                  header: "winsock2.h".} = object
    sin_family*: uint16
    sin_port*: uint16
    sin_addr*: InAddr
    sin_zero*: array[0..7, char]

  In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object
    bytes* {.importc: "u.Byte".}: array[0..15, char]

  Sockaddr_in6* {.importc: "SOCKADDR_IN6",
                   header: "ws2tcpip.h".} = object
    sin6_family*: uint16
    sin6_port*: uint16
    sin6_flowinfo*: int32 # unsigned
    sin6_addr*: In6_addr
    sin6_scope_id*: int32 # unsigned

  Sockaddr_storage* {.importc: "SOCKADDR_STORAGE",
                      header: "winsock2.h".} = object
    ss_family*: uint16
    ss_pad1 {.importc: "__ss_pad1".}: array[6, byte]
    ss_align {.importc: "__ss_align".}: int64
    ss_pad2 {.importc: "__ss_pad2".}: array[112, byte]

  Servent* = object
    s_name*: cstring
    s_aliases*: cstringArray
    when defined(cpu64):
      s_proto*: cstring
      s_port*: int16
    else:
      s_port*: int16
      s_proto*: cstring

  Hostent* = object
    h_name*: cstring
    h_aliases*: cstringArray
    h_addrtype*: int16
    h_length*: int16
    h_addr_list*: cstringArray

  TFdSet* = object
    fd_count*: cint # unsigned
    fd_array*: array[0..FD_SETSIZE-1, SocketHandle]

  AddrInfo* = object
    ai_flags*: cint         ## Input flags.
    ai_family*: cint        ## Address family of socket.
    ai_socktype*: cint      ## Socket type.
    ai_protocol*: cint      ## Protocol of socket.
    ai_addrlen*: csize_t        ## Length of socket address.
    ai_canonname*: cstring  ## Canonical name of service location.
    ai_addr*: ptr SockAddr ## Socket address of socket.
    ai_next*: ptr AddrInfo ## Pointer to next in list.

  SockLen* = cuint

when defined(cpp):
  type
    Timeval* {.importc: "timeval", header: "<time.h>".} = object
      tv_sec*, tv_usec*: int32
else:
  type
    Timeval* = object
      tv_sec*, tv_usec*: int32

var
  SOMAXCONN* {.importc, header: "winsock2.h".}: cint
  INVALID_SOCKET* {.importc, header: "winsock2.h".}: SocketHandle
  SOL_SOCKET* {.importc, header: "winsock2.h".}: cint
  SO_DEBUG* {.importc, header: "winsock2.h".}: cint ## turn on debugging info recording
  SO_ACCEPTCONN* {.importc, header: "winsock2.h".}: cint # socket has had listen()
  SO_REUSEADDR* {.importc, header: "winsock2.h".}: cint # allow local address reuse
  SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "winsock2.h".}: cint # allow port reuse. Since Windows does not really support it, mapped to SO_REUSEADDR. This shouldn't cause problems.

  SO_KEEPALIVE* {.importc, header: "winsock2.h".}: cint # keep connections alive
  SO_DONTROUTE* {.importc, header: "winsock2.h".}: cint # just use interface addresses
  SO_BROADCAST* {.importc, header: "winsock2.h".}: cint # permit sending of broadcast msgs
  SO_USELOOPBACK* {.importc, header: "winsock2.h".}: cint # bypass hardware when possible
  SO_LINGER* {.importc, header: "winsock2.h".}: cint # linger on close if data present
  SO_OOBINLINE* {.importc, header: "winsock2.h".}: cint # leave received OOB data in line

  SO_DONTLINGER* {.importc, header: "winsock2.h".}: cint
  SO_EXCLUSIVEADDRUSE* {.importc, header: "winsock2.h".}: cint # disallow local address reuse
  SO_ERROR* {.importc, header: "winsock2.h".}: cint
  TCP_NODELAY* {.importc, header: "winsock2.h".}: cint

proc `==`*(x, y: SocketHandle): bool {.borrow.}

proc getservbyname*(name, proto: cstring): ptr Servent {.
  stdcall, importc: "getservbyname", dynlib: ws2dll, sideEffect.}

proc getservbyport*(port: cint, proto: cstring): ptr Servent {.
  stdcall, importc: "getservbyport", dynlib: ws2dll, sideEffect.}

proc gethostbyaddr*(ip: ptr InAddr, len: cuint, theType: cint): ptr Hostent {.
  stdcall, importc: "gethostbyaddr", dynlib: ws2dll, sideEffect.}

proc gethostbyname*(name: cstring): ptr Hostent {.
  stdcall, importc: "gethostbyname", dynlib: ws2dll, sideEffect.}

proc gethostname*(hostname: cstring, len: cint): cint {.
  stdcall, importc: "gethostname", dynlib: ws2dll, sideEffect.}

proc getprotobyname*(
  name: cstring
): ptr Protoent {.stdcall, importc: "getprotobyname", dynlib: ws2dll, sideEffect.}

proc getprotobynumber*(
  proto: cint
): ptr Protoent {.stdcall, importc: "getprotobynumber", dynlib: ws2dll, sideEffect.}

proc socket*(af, typ, protocol: cint): SocketHandle {.
  stdcall, importc: "socket", dynlib: ws2dll.}

proc closesocket*(s: SocketHandle): cint {.
  stdcall, importc: "closesocket", dynlib: ws2dll.}

proc accept*(s: SocketHandle, a: ptr SockAddr, addrlen: ptr SockLen): SocketHandle {.
  stdcall, importc: "accept", dynlib: ws2dll.}
proc bindSocket*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {.
  stdcall, importc: "bind", dynlib: ws2dll.}
proc connect*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {.
  stdcall, importc: "connect", dynlib: ws2dll.}
proc getsockname*(s: SocketHandle, name: ptr SockAddr,
                  namelen: ptr SockLen): cint {.
  stdcall, importc: "getsockname", dynlib: ws2dll.}
proc getpeername*(s: SocketHandle, name: ptr SockAddr,
                  namelen: ptr SockLen): cint {.
  stdcall, importc, dynlib: ws2dll.}
proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer,
                 optlen: ptr SockLen): cint {.
  stdcall, importc: "getsockopt", dynlib: ws2dll.}
proc setsockopt*(s: SocketHandle, level, optname: cint, optval: pointer,
                 optlen: SockLen): cint {.
  stdcall, importc: "setsockopt", dynlib: ws2dll.}

proc listen*(s: SocketHandle, backlog: cint): cint {.
  stdcall, importc: "listen", dynlib: ws2dll.}
proc recv*(s: SocketHandle, buf: pointer, len, flags: cint): cint {.
  stdcall, importc: "recv", dynlib: ws2dll.}
proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint,
               fromm: ptr SockAddr, fromlen: ptr SockLen): cint {.
  stdcall, importc: "recvfrom", dynlib: ws2dll.}
proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet,
             timeout: ptr Timeval): cint {.
  stdcall, importc: "select", dynlib: ws2dll.}
proc send*(s: SocketHandle, buf: pointer, len, flags: cint): cint {.
  stdcall, importc: "send", dynlib: ws2dll.}
proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint,
             to: ptr SockAddr, tolen: SockLen): cint {.
  stdcall, importc: "sendto", dynlib: ws2dll.}

proc shutdown*(s: SocketHandle, how: cint): cint {.
  stdcall, importc: "shutdown", dynlib: ws2dll.}

proc getnameinfo*(a1: ptr SockAddr, a2: SockLen,
                  a3: cstring, a4: SockLen, a5: cstring,
                  a6: SockLen, a7: cint): cint {.
  stdcall, importc: "getnameinfo", dynlib: ws2dll.}

proc inet_addr*(cp: cstring): uint32 {.
  stdcall, importc: "inet_addr", dynlib: ws2dll.}

proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {.
  stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.}

proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint =
  result = if WSAFDIsSet(socket, set): 1'i32 else: 0'i32

proc FD_SET*(socket: SocketHandle, s: var TFdSet) =
  if s.fd_count < FD_SETSIZE:
    s.fd_array[int(s.fd_count)] = socket
    inc(s.fd_count)

proc FD_ZERO*(s: var TFdSet) =
  s.fd_count = 0

proc wsaStartup*(wVersionRequired: int16, WSData: ptr WSAData): cint {.
  stdcall, importc: "WSAStartup", dynlib: ws2dll.}

proc getaddrinfo*(nodename, servname: cstring, hints: ptr AddrInfo,
                  res: var ptr AddrInfo): cint {.
  stdcall, importc: "getaddrinfo", dynlib: ws2dll.}

proc freeAddrInfo*(ai: ptr AddrInfo) {.
  stdcall, importc: "freeaddrinfo", dynlib: ws2dll.}

proc inet_ntoa*(i: InAddr): cstring {.
  stdcall, importc, dynlib: ws2dll.}

const
  MAXIMUM_WAIT_OBJECTS* = 0x00000040

type
  WOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, Handle]
  PWOHandleArray* = ptr WOHandleArray

proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
                             bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}


# for memfiles.nim:

const
  GENERIC_READ* = 0x80000000'i32
  GENERIC_WRITE* = 0x40000000'i32
  GENERIC_ALL* = 0x10000000'i32
  FILE_SHARE_READ* = 1'i32
  FILE_SHARE_DELETE* = 4'i32
  FILE_SHARE_WRITE* = 2'i32

  CREATE_ALWAYS* = 2'i32
  CREATE_NEW* = 1'i32
  OPEN_EXISTING* = 3'i32
  OPEN_ALWAYS* = 4'i32
  FILE_BEGIN* = 0'i32
  INVALID_SET_FILE_POINTER* = -1'i32
  NO_ERROR* = 0'i32
  PAGE_NOACCESS* = 0x01'i32
  PAGE_EXECUTE* = 0x10'i32
  PAGE_EXECUTE_READ* = 0x20'i32
  PAGE_EXECUTE_READWRITE* = 0x40'i32
  PAGE_READONLY* = 2'i32
  PAGE_READWRITE* = 4'i32
  FILE_MAP_READ* = 4'i32
  FILE_MAP_WRITE* = 2'i32
  INVALID_FILE_SIZE* = -1'i32

  DUPLICATE_SAME_ACCESS* = 2
  FILE_READ_DATA* = 0x00000001 # file & pipe
  FILE_WRITE_DATA* = 0x00000002 # file & pipe

# Error Constants
const
  ERROR_FILE_NOT_FOUND* = 2 ## https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
  ERROR_PATH_NOT_FOUND* = 3
  ERROR_ACCESS_DENIED* = 5
  ERROR_NO_MORE_FILES* = 18
  ERROR_LOCK_VIOLATION* = 33
  ERROR_HANDLE_EOF* = 38
  ERROR_FILE_EXISTS* = 80
  ERROR_BAD_ARGUMENTS* = 165

proc duplicateHandle*(hSourceProcessHandle: Handle, hSourceHandle: Handle,
                      hTargetProcessHandle: Handle,
                      lpTargetHandle: ptr Handle,
                      dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                      dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32",
    importc: "DuplicateHandle".}

proc getHandleInformation*(hObject: Handle, lpdwFlags: ptr DWORD): WINBOOL {.
    stdcall, dynlib: "kernel32", importc: "GetHandleInformation".}

proc setHandleInformation*(hObject: Handle, dwMask: DWORD,
                           dwFlags: DWORD): WINBOOL {.stdcall,
    dynlib: "kernel32", importc: "SetHandleInformation".}

proc getCurrentProcess*(): Handle{.stdcall, dynlib: "kernel32",
                                   importc: "GetCurrentProcess".}

proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
                  lpSecurityAttributes: pointer,
                  dwCreationDisposition, dwFlagsAndAttributes: DWORD,
                  hTemplateFile: Handle): Handle {.
    stdcall, dynlib: "kernel32", importc: "CreateFileW".}
proc deleteFileW*(pathName: WideCString): int32 {.
  importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
proc createFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
                  lpSecurityAttributes: pointer,
                  dwCreationDisposition, dwFlagsAndAttributes: DWORD,
                  hTemplateFile: Handle): Handle {.
    stdcall, dynlib: "kernel32", importc: "CreateFileA".}
proc deleteFileA*(pathName: cstring): int32 {.
  importc: "DeleteFileA", dynlib: "kernel32", stdcall.}

proc setEndOfFile*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
    importc: "SetEndOfFile".}

proc setFilePointer*(hFile: Handle, lDistanceToMove: LONG,
                     lpDistanceToMoveHigh: ptr LONG,
                     dwMoveMethod: DWORD): DWORD {.
    stdcall, dynlib: "kernel32", importc: "SetFilePointer".}

proc getFileSize*(hFile: Handle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
    dynlib: "kernel32", importc: "GetFileSize".}

when defined(cpu32):
  type
    WinSizeT* = uint32
else:
  type
    WinSizeT* = uint64

proc mapViewOfFileEx*(hFileMappingObject: Handle, dwDesiredAccess: DWORD,
                      dwFileOffsetHigh, dwFileOffsetLow: DWORD,
                      dwNumberOfBytesToMap: WinSizeT,
                      lpBaseAddress: pointer): pointer{.
    stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}

proc createFileMappingW*(hFile: Handle,
                       lpFileMappingAttributes: pointer,
                       flProtect, dwMaximumSizeHigh: DWORD,
                       dwMaximumSizeLow: DWORD,
                       lpName: pointer): Handle {.
  stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}

when not useWinUnicode:
  proc createFileMappingA*(hFile: Handle,
                           lpFileMappingAttributes: pointer,
                           flProtect, dwMaximumSizeHigh: DWORD,
                           dwMaximumSizeLow: DWORD, lpName: cstring): Handle {.
      stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}

proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
    dynlib: "kernel32", importc: "UnmapViewOfFile".}

proc flushViewOfFile*(lpBaseAddress: pointer, dwNumberOfBytesToFlush: DWORD): WINBOOL {.
  stdcall, dynlib: "kernel32", importc: "FlushViewOfFile".}

type
  OVERLAPPED* {.pure, inheritable.} = object
    internal*: PULONG
    internalHigh*: PULONG
    offset*: DWORD
    offsetHigh*: DWORD
    hEvent*: Handle

  POVERLAPPED* = ptr OVERLAPPED

  POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
      para3: POVERLAPPED){.stdcall.}

  GUID* {.final, pure.} = object
    D1*: int32
    D2*: int16
    D3*: int16
    D4*: array[0..7, int8]

const
  ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING
  WSAECONNABORTED* = 10053
  WSAEADDRINUSE* = 10048
  WSAECONNRESET* = 10054
  WSAEDISCON* = 10101
  WSAENETRESET* = 10052
  WSAETIMEDOUT* = 10060
  WSANOTINITIALISED* = 10093
  WSAENOTSOCK* = 10038
  WSAEINPROGRESS* = 10036
  WSAEINTR* = 10004
  WSAEWOULDBLOCK* = 10035
  WSAESHUTDOWN* = 10058
  ERROR_NETNAME_DELETED* = 64
  STATUS_PENDING* = 0x103

proc createIoCompletionPort*(FileHandle: Handle, ExistingCompletionPort: Handle,
                             CompletionKey: ULONG_PTR,
                             NumberOfConcurrentThreads: DWORD): Handle{.stdcall,
    dynlib: "kernel32", importc: "CreateIoCompletionPort".}

proc getQueuedCompletionStatus*(CompletionPort: Handle,
    lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG_PTR,
                                lpOverlapped: ptr POVERLAPPED,
                                dwMilliseconds: DWORD): WINBOOL{.stdcall,
    dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}

proc getOverlappedResult*(hFile: Handle, lpOverlapped: POVERLAPPED,
              lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{.
    stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".}

# this is copy of HasOverlappedIoCompleted() macro from <winbase.h>
# because we have declared own OVERLAPPED structure with member names not
# compatible with original names.
template hasOverlappedIoCompleted*(lpOverlapped): bool =
  (cast[uint](lpOverlapped.internal) != STATUS_PENDING)

const
 IOC_OUT* = 0x40000000'i32
 IOC_IN*  = 0x80000000'i32
 IOC_WS2* = 0x08000000'i32
 IOC_INOUT* = IOC_IN or IOC_OUT

template WSAIORW*(x,y): untyped = (IOC_INOUT or x or y)

const
  SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
  SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
  AI_V4MAPPED* = 0x0008
  AF_UNSPEC* = 0
  AF_INET* = 2
  AF_INET6* = 23

var
  WSAID_CONNECTEX*: GUID = GUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
    0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
  WSAID_ACCEPTEX*: GUID = GUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
  WSAID_GETACCEPTEXSOCKADDRS*: GUID = GUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])

proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
  cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
  lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED,
  lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
  {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}

type
  TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
    len*: ULONG
    buf*: cstring

proc WSARecv*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
  bytesReceived, flags: PDWORD, lpOverlapped: POVERLAPPED,
  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
  stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}

proc WSARecvFrom*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
                  bytesReceived: PDWORD, flags: PDWORD, name: ptr SockAddr,
                  namelen: ptr cint, lpOverlapped: POVERLAPPED,
                  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
     stdcall, importc: "WSARecvFrom", dynlib: "Ws2_32.dll".}

proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
  bytesSent: PDWORD, flags: DWORD, lpOverlapped: POVERLAPPED,
  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
  stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}

proc WSASendTo*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
                bytesSent: PDWORD, flags: DWORD, name: ptr SockAddr,
                namelen: cint, lpOverlapped: POVERLAPPED,
                completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
     stdcall, importc: "WSASendTo", dynlib: "Ws2_32.dll".}

proc get_osfhandle*(fd:FileHandle): Handle {.
  importc: "_get_osfhandle", header:"<io.h>".}

proc getSystemTimes*(lpIdleTime, lpKernelTime,
                     lpUserTime: var FILETIME): WINBOOL {.stdcall,
  dynlib: "kernel32", importc: "GetSystemTimes".}

proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime,
  lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall,
  dynlib: "kernel32", importc: "GetProcessTimes".}

proc getSystemTimePreciseAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
  importc: "GetSystemTimePreciseAsFileTime", dynlib: "kernel32", stdcall, sideEffect.}

type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring,
                      stringBufSize: int32): cstring {.gcsafe, stdcall, tags: [].}

var inet_ntop_real: inet_ntop_proc = nil

let ws2 = loadLib(ws2dll)
if ws2 != nil:
  inet_ntop_real = cast[inet_ntop_proc](symAddr(ws2, "inet_ntop"))

proc WSAAddressToStringA(pAddr: ptr SockAddr, addrSize: DWORD, unused: pointer, pBuff: cstring, pBuffSize: ptr DWORD): cint {.stdcall, importc, dynlib: ws2dll.}
proc inet_ntop_emulated(family: cint, paddr: pointer, pStringBuffer: cstring,
                  stringBufSize: int32): cstring {.stdcall.} =
  case family
  of AF_INET:
    var sa: Sockaddr_in
    sa.sin_family = AF_INET
    sa.sin_addr = cast[ptr InAddr](paddr)[]
    var bs = stringBufSize.DWORD
    let r = WSAAddressToStringA(cast[ptr SockAddr](sa.addr), sa.sizeof.DWORD, nil, pStringBuffer, bs.addr)
    if r != 0:
      result = nil
    else:
      result = pStringBuffer
  of AF_INET6:
    var sa: Sockaddr_in6
    sa.sin6_family = AF_INET6
    sa.sin6_addr = cast[ptr In6_addr](paddr)[]
    var bs = stringBufSize.DWORD
    let r = WSAAddressToStringA(cast[ptr SockAddr](sa.addr), sa.sizeof.DWORD, nil, pStringBuffer, bs.addr)
    if r != 0:
      result = nil
    else:
      result = pStringBuffer
  else:
    setLastError(ERROR_BAD_ARGUMENTS)
    result = nil

proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
                  stringBufSize: int32): cstring {.stdcall.} =
  var ver: OSVERSIONINFO
  ver.dwOSVersionInfoSize = sizeof(ver).DWORD
  let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr)
  if res == 0:
    result = nil
  elif ver.dwMajorVersion >= 6:
    if inet_ntop_real == nil:
      quit("Can't load inet_ntop proc from " & ws2dll)
    result = inet_ntop_real(family, paddr, pStringBuffer, stringBufSize)
  else:
    result = inet_ntop_emulated(family, paddr, pStringBuffer, stringBufSize)

type
  WSAPROC_ACCEPTEX* = proc (sListenSocket: SocketHandle,
                            sAcceptSocket: SocketHandle,
                            lpOutputBuffer: pointer, dwReceiveDataLength: DWORD,
                            dwLocalAddressLength: DWORD,
                            dwRemoteAddressLength: DWORD,
                            lpdwBytesReceived: ptr DWORD,
                            lpOverlapped: POVERLAPPED): bool {.
                            stdcall, gcsafe, raises: [].}

  WSAPROC_CONNECTEX* = proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
                             lpSendBuffer: pointer, dwSendDataLength: DWORD,
                             lpdwBytesSent: ptr DWORD,
                             lpOverlapped: POVERLAPPED): bool {.
                             stdcall, gcsafe, raises: [].}

  WSAPROC_GETACCEPTEXSOCKADDRS* = proc(lpOutputBuffer: pointer,
                                       dwReceiveDataLength: DWORD,
                                       dwLocalAddressLength: DWORD,
                                       dwRemoteAddressLength: DWORD,
                                       LocalSockaddr: ptr PSockAddr,
                                       LocalSockaddrLength: ptr cint,
                                       RemoteSockaddr: ptr PSockAddr,
                                       RemoteSockaddrLength: ptr cint) {.
                                       stdcall, gcsafe, raises: [].}

const
  WT_EXECUTEDEFAULT* = 0x00000000'i32
  WT_EXECUTEINIOTHREAD* = 0x00000001'i32
  WT_EXECUTEINUITHREAD* = 0x00000002'i32
  WT_EXECUTEINWAITTHREAD* = 0x00000004'i32
  WT_EXECUTEONLYONCE* = 0x00000008'i32
  WT_EXECUTELONGFUNCTION* = 0x00000010'i32
  WT_EXECUTEINTIMERTHREAD* = 0x00000020'i32
  WT_EXECUTEINPERSISTENTIOTHREAD* = 0x00000040'i32
  WT_EXECUTEINPERSISTENTTHREAD* = 0x00000080'i32
  WT_TRANSFER_IMPERSONATION* = 0x00000100'i32
  PROCESS_TERMINATE* = 0x00000001'i32
  PROCESS_CREATE_THREAD* = 0x00000002'i32
  PROCESS_SET_SESSIONID* = 0x00000004'i32
  PROCESS_VM_OPERATION* = 0x00000008'i32
  PROCESS_VM_READ* = 0x00000010'i32
  PROCESS_VM_WRITE* = 0x00000020'i32
  PROCESS_DUP_HANDLE* = 0x00000040'i32
  PROCESS_CREATE_PROCESS* = 0x00000080'i32
  PROCESS_SET_QUOTA* = 0x00000100'i32
  PROCESS_SET_INFORMATION* = 0x00000200'i32
  PROCESS_QUERY_INFORMATION* = 0x00000400'i32
  PROCESS_SUSPEND_RESUME* = 0x00000800'i32
  PROCESS_QUERY_LIMITED_INFORMATION* = 0x00001000'i32
  PROCESS_SET_LIMITED_INFORMATION* = 0x00002000'i32
type
  WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32) {.stdcall.}

proc postQueuedCompletionStatus*(CompletionPort: Handle,
                                dwNumberOfBytesTransferred: DWORD,
                                dwCompletionKey: ULONG_PTR,
                                lpOverlapped: pointer): bool
     {.stdcall, dynlib: "kernel32", importc: "PostQueuedCompletionStatus".}

proc registerWaitForSingleObject*(phNewWaitObject: ptr Handle, hObject: Handle,
                                 Callback: WAITORTIMERCALLBACK,
                                 Context: pointer,
                                 dwMilliseconds: ULONG,
                                 dwFlags: ULONG): bool
     {.stdcall, dynlib: "kernel32", importc: "RegisterWaitForSingleObject".}

proc unregisterWait*(WaitHandle: Handle): DWORD
     {.stdcall, dynlib: "kernel32", importc: "UnregisterWait".}

proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                    dwProcessId: DWORD): Handle
     {.stdcall, dynlib: "kernel32", importc: "OpenProcess".}

when defined(useWinAnsi):
  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
                    bManualReset: DWORD, bInitialState: DWORD,
                    lpName: cstring): Handle
       {.stdcall, dynlib: "kernel32", importc: "CreateEventA".}
else:
  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
                    bManualReset: DWORD, bInitialState: DWORD,
                    lpName: ptr Utf16Char): Handle
       {.stdcall, dynlib: "kernel32", importc: "CreateEventW".}

proc setEvent*(hEvent: Handle): cint
     {.stdcall, dynlib: "kernel32", importc: "SetEvent".}

const
  FD_READ* = 0x00000001'i32
  FD_WRITE* = 0x00000002'i32
  FD_OOB* = 0x00000004'i32
  FD_ACCEPT* = 0x00000008'i32
  FD_CONNECT* = 0x00000010'i32
  FD_CLOSE* = 0x00000020'i32
  FD_QQS* = 0x00000040'i32
  FD_GROUP_QQS* = 0x00000080'i32
  FD_ROUTING_INTERFACE_CHANGE* = 0x00000100'i32
  FD_ADDRESS_LIST_CHANGE* = 0x00000200'i32
  FD_ALL_EVENTS* = 0x000003FF'i32

proc wsaEventSelect*(s: SocketHandle, hEventObject: Handle,
                     lNetworkEvents: clong): cint
    {.stdcall, importc: "WSAEventSelect", dynlib: "ws2_32.dll".}

proc wsaCreateEvent*(): Handle
    {.stdcall, importc: "WSACreateEvent", dynlib: "ws2_32.dll".}

proc wsaCloseEvent*(hEvent: Handle): bool
     {.stdcall, importc: "WSACloseEvent", dynlib: "ws2_32.dll".}

proc wsaResetEvent*(hEvent: Handle): bool
     {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".}

type
  KEY_EVENT_RECORD* {.final, pure.} = object
    eventType*: int16
    bKeyDown*: WINBOOL
    wRepeatCount*: int16
    wVirtualKeyCode*: int16
    wVirtualScanCode*: int16
    uChar*: int16
    dwControlKeyState*: DWORD

when defined(useWinAnsi):
  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
                        lpNumberOfEventsRead: ptr cint): cint
       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".}
else:
  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
                        lpNumberOfEventsRead: ptr cint): cint
       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}

type
  LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.}

const
  FIBER_FLAG_FLOAT_SWITCH* = 0x01

proc CreateFiber*(stackSize: int, fn: LPFIBER_START_ROUTINE, param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
proc CreateFiberEx*(stkCommit: int, stkReserve: int, flags: int32, fn: LPFIBER_START_ROUTINE, param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
proc ConvertThreadToFiber*(param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
proc DeleteFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.}
proc SwitchToFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.}
proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "windows.h".}

proc toFILETIME*(t: int64): FILETIME =
  ## Convert the Windows file time timestamp `t` to `FILETIME`.
  result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32))

type
  LPFILETIME* = ptr FILETIME

proc setFileTime*(hFile: Handle, lpCreationTime: LPFILETIME,
                 lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
     {.stdcall, dynlib: "kernel32", importc: "SetFileTime".}

type
  # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority
  SID_IDENTIFIER_AUTHORITY* {.importc, header: "<windows.h>".} = object
    value* {.importc: "Value".}: array[6, BYTE]
  # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
  SID* {.importc, header: "<windows.h>".} = object
    Revision: BYTE
    SubAuthorityCount: BYTE
    IdentifierAuthority: SID_IDENTIFIER_AUTHORITY
    SubAuthority: ptr ptr DWORD
  PSID* = ptr SID

const
  # https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components
  # https://github.com/mirror/mingw-w64/blob/84c950bdab7c999ace49fe8383856be77f88c4a8/mingw-w64-headers/include/winnt.h#L2994
  SECURITY_NT_AUTHORITY* = [BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(5)]
  SECURITY_BUILTIN_DOMAIN_RID* = 32
  DOMAIN_ALIAS_RID_ADMINS* = 544

proc allocateAndInitializeSid*(pIdentifierAuthority: ptr SID_IDENTIFIER_AUTHORITY,
                               nSubAuthorityCount: BYTE,
                               nSubAuthority0: DWORD,
                               nSubAuthority1: DWORD,
                               nSubAuthority2: DWORD,
                               nSubAuthority3: DWORD,
                               nSubAuthority4: DWORD,
                               nSubAuthority5: DWORD,
                               nSubAuthority6: DWORD,
                               nSubAuthority7: DWORD,
                               pSid: ptr PSID): WINBOOL
     {.stdcall, dynlib: "Advapi32", importc: "AllocateAndInitializeSid".}
proc checkTokenMembership*(tokenHandle: Handle, sidToCheck: PSID,
                           isMember: PBOOL): WINBOOL
     {.stdcall, dynlib: "Advapi32", importc: "CheckTokenMembership".}
proc freeSid*(pSid: PSID): PSID
     {.stdcall, dynlib: "Advapi32", importc: "FreeSid".}

when defined(nimHasStyleChecks):
  {.pop.} # {.push styleChecks: off.}
