#pragma once
#include <cstdint>
#include <type_traits>
#include <bit>

#ifdef _WIN32
    #include <windows.h>
#else
    #define near
    #define far

    typedef char CHAR;
    typedef wchar_t WCHAR;
    typedef unsigned long DWORD;
    typedef int BOOL;
    typedef unsigned char BYTE;
    typedef unsigned short WORD;
    typedef float FLOAT;
    typedef FLOAT* PFLOAT;
    typedef BOOL near* PBOOL;
    typedef BOOL far* LPBOOL;
    typedef BYTE near* PBYTE;
    typedef BYTE far* LPBYTE;
    typedef int near* PINT;
    typedef int far* LPINT;
    typedef WORD near* PWORD;
    typedef WORD far* LPWORD;
    typedef long far* LPLONG;
    typedef DWORD near* PDWORD;
    typedef DWORD far* LPDWORD;
    typedef void far* LPVOID;
    typedef const void far* LPCVOID;
    typedef unsigned long ULONG;
    typedef ULONG* PULONG;
    typedef signed long LONG;
    typedef LONG* PLONG;
    typedef unsigned long long ULONGLONG;
    typedef ULONGLONG* PULONGLONG;
    typedef unsigned short USHORT;
    typedef USHORT* PUSHORT;
    typedef unsigned char UCHAR;
    typedef UCHAR* PUCHAR;
    typedef char* PSZ;
    typedef int INT;
    typedef unsigned int UINT;
    typedef unsigned int* PUINT;
#endif

// real win32 handles will never use the upper bits unless something goes really wrong
#define CHECK_GUEST_HANDLE(HANDLE) (((HANDLE) & 0x80000000) == 0x80000000)
#define GUEST_HANDLE(HANDLE) ((HANDLE) | 0x80000000)
#define HOST_HANDLE(HANDLE) ((HANDLE) & ~0x80000000)

template<typename T>
struct be
{
    T value;

    be() : value(0)
    {
    }

    be(const T v)
    {
        set(v);
    }

    static T byteswap(T value)
    {
        if constexpr (std::is_same_v<T, double>)
        {
            const uint64_t swapped = std::byteswap(*reinterpret_cast<uint64_t*>(&value));
            return *reinterpret_cast<const T*>(&swapped);
        }
        else if constexpr (std::is_same_v<T, float>)
        {
            const uint32_t swapped = std::byteswap(*reinterpret_cast<uint32_t*>(&value));
            return *reinterpret_cast<const T*>(&swapped);
        }
        else
        {
            return std::byteswap(value);
        }
    }

    void set(const T v)
    {
        value = byteswap(v);
    }

    T get() const
    {
        return byteswap(value);
    }

    be& operator| (T value)
    {
        set(get() | value);
        return *this;
    }

    be& operator& (T value)
    {
        set(get() & value);
        return *this;
    }

    operator T() const
    {
        return get();
    }

    be& operator=(T v)
    {
        set(v);
        return *this;
    }
};

template<typename T>
struct xpointer
{
    be<uint32_t> ptr;

    xpointer(T* ptr) : ptr(ptr)
    {

    }

    T* get() const
    {
        if (!ptr.value)
        {
            return nullptr;
        }

        return static_cast<T*>(ptr);
    }

    operator T* () const
    {
        return get();
    }

    T* operator->() const
    {
        return get();
    }
};

typedef BYTE XBYTE;
typedef be<uint16_t> XWORD;
typedef be<uint32_t> XDWORD;
typedef be<uint64_t> XQWORD;

typedef XBYTE* XLPBYTE;
typedef XWORD* XLPWORD;
typedef XDWORD* XLPDWORD;
typedef XQWORD* XLPQWORD;

struct _XLIST_ENTRY;
typedef _XLIST_ENTRY XLIST_ENTRY;
typedef xpointer<XLIST_ENTRY> PXLIST_ENTRY;

typedef struct _XLIST_ENTRY
{
    XDWORD Flink;
    XDWORD Blink;
} XLIST_ENTRY;

typedef struct _XDISPATCHER_HEADER
{
    union
    {
        struct
        {
            UCHAR Type;
            union
            {
                UCHAR Abandoned;
                UCHAR Absolute;
                UCHAR NpxIrql;
                UCHAR Signalling;
            };
            union
            {
                UCHAR Size;
                UCHAR Hand;
            };
            union
            {
                UCHAR Inserted;
                UCHAR DebugActive;
                UCHAR DpcActive;
            };
        };
        XDWORD Lock;
    };

    XDWORD SignalState;
    XLIST_ENTRY WaitListHead;
} XDISPATCHER_HEADER, * XPDISPATCHER_HEADER;

// These variables are never accessed in guest code, we can safely use them in little endian
typedef struct _XRTL_CRITICAL_SECTION
{
    XDISPATCHER_HEADER Header;
    long LockCount;
    int32_t RecursionCount;
    uint32_t OwningThread;
} XRTL_CRITICAL_SECTION;

typedef struct _XANSI_STRING {
    XWORD Length;
    XWORD MaximumLength;
    xpointer<char> Buffer;
} XANSI_STRING;

typedef struct _XOBJECT_ATTRIBUTES
{
    XDWORD RootDirectory;
    xpointer<XANSI_STRING> Name;
    xpointer<void> Attributes;
} XOBJECT_ATTRIBUTES;

typedef XDISPATCHER_HEADER XKEVENT;

typedef struct _XIO_STATUS_BLOCK
{
    union {
        XDWORD Status;
        XDWORD Pointer;
    };
    be<uint32_t> Information;
} XIO_STATUS_BLOCK;

typedef struct _XOVERLAPPED {
    XDWORD Internal;
    XDWORD InternalHigh;
    XDWORD Offset;
    XDWORD OffsetHigh;
    XDWORD hEvent;
} XOVERLAPPED;

// this name is so dumb
typedef struct _XXOVERLAPPED {
    union
    {
        struct
        {
            XDWORD Error;
            XDWORD Length;
        };

        struct
        {
            uint32_t InternalLow;
            uint32_t InternalHigh;
        };
    };
    uint32_t InternalContext;
    XDWORD hEvent;
    XDWORD pCompletionRoutine;
    XDWORD dwCompletionContext;
    XDWORD dwExtendedError;
} XXOVERLAPPED, *PXXOVERLAPPED;

static_assert(sizeof(_XXOVERLAPPED) == 0x1C);

typedef struct _XVIDEO_MODE
{
    be<uint32_t> DisplayWidth;
    be<uint32_t> DisplayHeight;
    be<uint32_t> IsInterlaced;
    be<uint32_t> IsWidescreen;
    be<uint32_t> IsHighDefinition;
    be<uint32_t> RefreshRate;
    be<uint32_t> VideoStandard;
    be<uint32_t> Unknown4A;
    be<uint32_t> Unknown01;
    be<uint32_t> reserved[3];
} XVIDEO_MODE;

typedef struct _XKSEMAPHORE
{
    XDISPATCHER_HEADER Header;
    XDWORD Limit;
} XKSEMAPHORE;