import sys
from _ctypes import _CArgObject, _CField
from ctypes import (
    Array,
    Structure,
    _Pointer,
    _SimpleCData,
    c_char,
    c_char_p,
    c_double,
    c_float,
    c_int,
    c_long,
    c_longlong,
    c_short,
    c_uint,
    c_ulong,
    c_ulonglong,
    c_ushort,
    c_void_p,
    c_wchar,
    c_wchar_p,
)
from typing import Any, TypeVar
from typing_extensions import Self, TypeAlias

if sys.version_info >= (3, 12):
    from ctypes import c_ubyte

    BYTE = c_ubyte
else:
    from ctypes import c_byte

    BYTE = c_byte

WORD = c_ushort
DWORD = c_ulong
CHAR = c_char
WCHAR = c_wchar
UINT = c_uint
INT = c_int
DOUBLE = c_double
FLOAT = c_float
BOOLEAN = BYTE
BOOL = c_long

class VARIANT_BOOL(_SimpleCData[bool]): ...

ULONG = c_ulong
LONG = c_long
USHORT = c_ushort
SHORT = c_short
LARGE_INTEGER = c_longlong
_LARGE_INTEGER = c_longlong
ULARGE_INTEGER = c_ulonglong
_ULARGE_INTEGER = c_ulonglong

OLESTR = c_wchar_p
LPOLESTR = c_wchar_p
LPCOLESTR = c_wchar_p
LPWSTR = c_wchar_p
LPCWSTR = c_wchar_p
LPSTR = c_char_p
LPCSTR = c_char_p
LPVOID = c_void_p
LPCVOID = c_void_p

# These two types are pointer-sized unsigned and signed ints, respectively.
# At runtime, they are either c_[u]long or c_[u]longlong, depending on the host's pointer size
# (they are not really separate classes).
class WPARAM(_SimpleCData[int]): ...
class LPARAM(_SimpleCData[int]): ...

ATOM = WORD
LANGID = WORD
COLORREF = DWORD
LGRPID = DWORD
LCTYPE = DWORD
LCID = DWORD

HANDLE = c_void_p
HACCEL = HANDLE
HBITMAP = HANDLE
HBRUSH = HANDLE
HCOLORSPACE = HANDLE
HDC = HANDLE
HDESK = HANDLE
HDWP = HANDLE
HENHMETAFILE = HANDLE
HFONT = HANDLE
HGDIOBJ = HANDLE
HGLOBAL = HANDLE
HHOOK = HANDLE
HICON = HANDLE
HINSTANCE = HANDLE
HKEY = HANDLE
HKL = HANDLE
HLOCAL = HANDLE
HMENU = HANDLE
HMETAFILE = HANDLE
HMODULE = HANDLE
HMONITOR = HANDLE
HPALETTE = HANDLE
HPEN = HANDLE
HRGN = HANDLE
HRSRC = HANDLE
HSTR = HANDLE
HTASK = HANDLE
HWINSTA = HANDLE
HWND = HANDLE
SC_HANDLE = HANDLE
SERVICE_STATUS_HANDLE = HANDLE

_CIntLikeT = TypeVar("_CIntLikeT", bound=_SimpleCData[int])
_CIntLikeField: TypeAlias = _CField[_CIntLikeT, int, _CIntLikeT | int]

class RECT(Structure):
    left: _CIntLikeField[LONG]
    top: _CIntLikeField[LONG]
    right: _CIntLikeField[LONG]
    bottom: _CIntLikeField[LONG]

RECTL = RECT
_RECTL = RECT
tagRECT = RECT

class _SMALL_RECT(Structure):
    Left: _CIntLikeField[SHORT]
    Top: _CIntLikeField[SHORT]
    Right: _CIntLikeField[SHORT]
    Bottom: _CIntLikeField[SHORT]

SMALL_RECT = _SMALL_RECT

class _COORD(Structure):
    X: _CIntLikeField[SHORT]
    Y: _CIntLikeField[SHORT]

class POINT(Structure):
    x: _CIntLikeField[LONG]
    y: _CIntLikeField[LONG]

POINTL = POINT
_POINTL = POINT
tagPOINT = POINT

class SIZE(Structure):
    cx: _CIntLikeField[LONG]
    cy: _CIntLikeField[LONG]

SIZEL = SIZE
tagSIZE = SIZE

def RGB(red: int, green: int, blue: int) -> int: ...

class FILETIME(Structure):
    dwLowDateTime: _CIntLikeField[DWORD]
    dwHighDateTime: _CIntLikeField[DWORD]

_FILETIME = FILETIME

class MSG(Structure):
    hWnd: _CField[HWND, int | None, HWND | int | None]
    message: _CIntLikeField[UINT]
    wParam: _CIntLikeField[WPARAM]
    lParam: _CIntLikeField[LPARAM]
    time: _CIntLikeField[DWORD]
    pt: _CField[POINT, POINT, POINT]

tagMSG = MSG
MAX_PATH: int

class WIN32_FIND_DATAA(Structure):
    dwFileAttributes: _CIntLikeField[DWORD]
    ftCreationTime: _CField[FILETIME, FILETIME, FILETIME]
    ftLastAccessTime: _CField[FILETIME, FILETIME, FILETIME]
    ftLastWriteTime: _CField[FILETIME, FILETIME, FILETIME]
    nFileSizeHigh: _CIntLikeField[DWORD]
    nFileSizeLow: _CIntLikeField[DWORD]
    dwReserved0: _CIntLikeField[DWORD]
    dwReserved1: _CIntLikeField[DWORD]
    cFileName: _CField[Array[CHAR], bytes, bytes]
    cAlternateFileName: _CField[Array[CHAR], bytes, bytes]

class WIN32_FIND_DATAW(Structure):
    dwFileAttributes: _CIntLikeField[DWORD]
    ftCreationTime: _CField[FILETIME, FILETIME, FILETIME]
    ftLastAccessTime: _CField[FILETIME, FILETIME, FILETIME]
    ftLastWriteTime: _CField[FILETIME, FILETIME, FILETIME]
    nFileSizeHigh: _CIntLikeField[DWORD]
    nFileSizeLow: _CIntLikeField[DWORD]
    dwReserved0: _CIntLikeField[DWORD]
    dwReserved1: _CIntLikeField[DWORD]
    cFileName: _CField[Array[WCHAR], str, str]
    cAlternateFileName: _CField[Array[WCHAR], str, str]

# These are all defined with the POINTER() function, which keeps a cache and will
# return a previously created class if it can. The self-reported __name__
# of these classes is f"LP_{typ.__name__}", where typ is the original class
# passed in to the POINTER() function.

# LP_c_short
class PSHORT(_Pointer[SHORT]): ...

# LP_c_ushort
class PUSHORT(_Pointer[USHORT]): ...

PWORD = PUSHORT
LPWORD = PUSHORT

# LP_c_long
class PLONG(_Pointer[LONG]): ...

LPLONG = PLONG
PBOOL = PLONG
LPBOOL = PLONG

# LP_c_ulong
class PULONG(_Pointer[ULONG]): ...

PDWORD = PULONG
LPDWORD = PDWORD
LPCOLORREF = PDWORD
PLCID = PDWORD

# LP_c_int (or LP_c_long if int and long have the same size)
class PINT(_Pointer[INT]): ...

LPINT = PINT

# LP_c_uint (or LP_c_ulong if int and long have the same size)
class PUINT(_Pointer[UINT]): ...

LPUINT = PUINT

# LP_c_float
class PFLOAT(_Pointer[FLOAT]): ...

# LP_c_longlong (or LP_c_long if long and long long have the same size)
class PLARGE_INTEGER(_Pointer[LARGE_INTEGER]): ...

# LP_c_ulonglong (or LP_c_ulong if long and long long have the same size)
class PULARGE_INTEGER(_Pointer[ULARGE_INTEGER]): ...

# LP_c_byte types
class PBYTE(_Pointer[BYTE]): ...

LPBYTE = PBYTE
PBOOLEAN = PBYTE

# LP_c_char
class PCHAR(_Pointer[CHAR]):
    # this is inherited from ctypes.c_char_p, kind of.
    @classmethod
    def from_param(cls, value: Any, /) -> Self | _CArgObject: ...

# LP_c_wchar
class PWCHAR(_Pointer[WCHAR]):
    # inherited from ctypes.c_wchar_p, kind of
    @classmethod
    def from_param(cls, value: Any, /) -> Self | _CArgObject: ...

# LP_c_void_p
class PHANDLE(_Pointer[HANDLE]): ...

LPHANDLE = PHANDLE
PHKEY = PHANDLE
LPHKL = PHANDLE
LPSC_HANDLE = PHANDLE

# LP_FILETIME
class PFILETIME(_Pointer[FILETIME]): ...

LPFILETIME = PFILETIME

# LP_MSG
class PMSG(_Pointer[MSG]): ...

LPMSG = PMSG

# LP_POINT
class PPOINT(_Pointer[POINT]): ...

LPPOINT = PPOINT
PPOINTL = PPOINT

# LP_RECT
class PRECT(_Pointer[RECT]): ...

LPRECT = PRECT
PRECTL = PRECT
LPRECTL = PRECT

# LP_SIZE
class PSIZE(_Pointer[SIZE]): ...

LPSIZE = PSIZE
PSIZEL = PSIZE
LPSIZEL = PSIZE

# LP__SMALL_RECT
class PSMALL_RECT(_Pointer[SMALL_RECT]): ...

# LP_WIN32_FIND_DATAA
class PWIN32_FIND_DATAA(_Pointer[WIN32_FIND_DATAA]): ...

LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA

# LP_WIN32_FIND_DATAW
class PWIN32_FIND_DATAW(_Pointer[WIN32_FIND_DATAW]): ...

LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW
