#pragma once

/* Win32 Type Definitions */

using HRESULT = int;
using LPCVOID = const void*;
using SIZE_T  = size_t;
using LPCSTR  = const char*;
using UINT    = unsigned int;
using ULONG   = unsigned long;
using LPVOID  = void*;
using REFIID  = void*;

/* dxcompiler Type Definitions */
using BOOL               = int;
using REFCLSID           = void*;
using LPCWSTR            = wchar_t*;
using IDxcBlobEncoding   = void; /* hack, unused */
using IDxcBlobWide       = void; /* hack, unused */
using IDxcIncludeHandler = void; /* hack, unused */

/* Unlike vkd3d-utils, libdxcompiler.so does not use msabi */
#if !defined(_WIN32)
#define __stdcall
#endif

/* Compiler Interface, _technically_ unofficial but it's MS C++, come on */
using DXC_OUT_KIND = enum DXC_OUT_KIND {
    DXC_OUT_NONE           = 0,
    DXC_OUT_OBJECT         = 1,
    DXC_OUT_ERRORS         = 2,
    DXC_OUT_PDB            = 3,
    DXC_OUT_SHADER_HASH    = 4,
    DXC_OUT_DISASSEMBLY    = 5,
    DXC_OUT_HLSL           = 6,
    DXC_OUT_TEXT           = 7,
    DXC_OUT_REFLECTION     = 8,
    DXC_OUT_ROOT_SIGNATURE = 9,
    DXC_OUT_EXTRA_OUTPUTS  = 10,
    DXC_OUT_REMARKS        = 11,
    DXC_OUT_TIME_REPORT    = 12,
    DXC_OUT_TIME_TRACE     = 13,
    DXC_OUT_LAST           = DXC_OUT_TIME_TRACE,
    DXC_OUT_NUM_ENUMS,
    // DXC_OUT_FORCE_DWORD = 0xFFFFFFFF
};

#define DXC_CP_UTF8 65001
#define DXC_CP_UTF16 1200
#define DXC_CP_UTF32 12000
/* This is for binary, ANSI-text, or to tell the compiler to try autodetecting UTF using the BOM */
#define DXC_CP_ACP 0

using DxcBuffer = struct DxcBuffer
{
    LPCVOID Ptr;
    SIZE_T  Size;
    UINT    Encoding;
};

/* *INDENT-OFF* */ // clang-format off

static uint8 IID_IDxcBlob[] = {
    0x08, 0xFB, 0xA5, 0x8B,
    0x95, 0x51,
    0xE2, 0x40,
    0xAC,
    0x58,
    0x0D,
    0x98,
    0x9C,
    0x3A,
    0x01,
    0x02
};
using IDxcBlob =  struct IDxcBlob;
using IDxcBlobVtbl =  struct IDxcBlobVtbl
{
    HRESULT(__stdcall *QueryInterface)(IDxcBlob *This, REFIID riid, void **ppvObject);
    ULONG(__stdcall *AddRef)(IDxcBlob *This);
    ULONG(__stdcall *Release)(IDxcBlob *This);

    LPVOID(__stdcall *GetBufferPointer)(IDxcBlob *This);
    SIZE_T(__stdcall *GetBufferSize)(IDxcBlob *This);
};
struct IDxcBlob
{
    IDxcBlobVtbl *lpVtbl;
};

static uint8 IID_IDxcBlobUtf8[] = {
    0xC9, 0x36, 0xA6, 0x3D,
    0x71, 0xBA,
    0x24, 0x40,
    0xA3,
    0x01,
    0x30,
    0xCB,
    0xF1,
    0x25,
    0x30,
    0x5B
};
using IDxcBlobUtf8 =  struct IDxcBlobUtf8;
using IDxcBlobUtf8Vtbl =  struct IDxcBlobUtf8Vtbl
{
    HRESULT(__stdcall *QueryInterface)(IDxcBlobUtf8 *This, REFIID riid, void **ppvObject);
    ULONG(__stdcall *AddRef)(IDxcBlobUtf8 *This);
    ULONG(__stdcall *Release)(IDxcBlobUtf8 *This);

    LPVOID(__stdcall *GetBufferPointer)(IDxcBlobUtf8 *This);
    SIZE_T(__stdcall *GetBufferSize)(IDxcBlobUtf8 *This);

    HRESULT(__stdcall *GetEncoding)(IDxcBlobUtf8 *This, BOOL *pKnown, uint32 *pCodePage);

    LPCSTR(__stdcall *GetStringPointer)(IDxcBlobUtf8 *This);
    SIZE_T(__stdcall *GetStringLength)(IDxcBlobUtf8 *This);
};
struct IDxcBlobUtf8
{
    IDxcBlobUtf8Vtbl *lpVtbl;
};

static uint8 IID_IDxcResult[] = {
    0xDA, 0x6C, 0x34, 0x58,
    0xE7, 0xDD,
    0x97, 0x44,
    0x94,
    0x61,
    0x6F,
    0x87,
    0xAF,
    0x5E,
    0x06,
    0x59
};
using IDxcResult =  struct IDxcResult;
using IDxcResultVtbl =  struct IDxcResultVtbl
{
    HRESULT(__stdcall *QueryInterface)(IDxcResult *This, REFIID riid, void **ppvObject);
    ULONG(__stdcall *AddRef)(IDxcResult *This);
    ULONG(__stdcall *Release)(IDxcResult *This);

    HRESULT(__stdcall *GetStatus)(IDxcResult *This, HRESULT *pStatus);
    HRESULT(__stdcall *GetResult)(IDxcResult *This, IDxcBlob **ppResult);
    HRESULT(__stdcall *GetErrorBuffer)(IDxcResult *This, IDxcBlobEncoding **ppErrors);

    BOOL(__stdcall *HasOutput)(IDxcResult *This, DXC_OUT_KIND dxcOutKind);
    HRESULT(__stdcall *GetOutput)(
        IDxcResult *This,
        DXC_OUT_KIND dxcOutKind,
        REFIID iid,
        void **ppvObject,
        IDxcBlobWide **ppOutputName
    );
    uint32(__stdcall *GetNumOutputs)(IDxcResult *This);
    DXC_OUT_KIND(__stdcall *GetOutputByIndex)(IDxcResult *This, uint32 Index);
    DXC_OUT_KIND(__stdcall *PrimaryOutput)(IDxcResult *This);
};
struct IDxcResult
{
    IDxcResultVtbl *lpVtbl;
};

static struct
{
    uint32 Data1;
    uint16 Data2;
    uint16 Data3;
    uint8 Data4[8];
} CLSID_DxcCompiler = {
    .Data1 = 0x73e22d93,
    .Data2 = 0xe6ce,
    .Data3 = 0x47f3,
    .Data4 = { 0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0 }
};
static uint8 IID_IDxcCompiler3[] = {
    0x87, 0x46, 0x8B, 0x22,
    0x6A, 0x5A,
    0x30, 0x47,
    0x90,
    0x0C,
    0x97,
    0x02,
    0xB2,
    0x20,
    0x3F,
    0x54
};
using IDxcCompiler3 =  struct IDxcCompiler3;
using IDxcCompiler3Vtbl =  struct IDxcCompiler3Vtbl
{
    HRESULT(__stdcall *QueryInterface)(IDxcCompiler3 *This, REFIID riid, void **ppvObject);
    ULONG(__stdcall *AddRef)(IDxcCompiler3 *This);
    ULONG(__stdcall *Release)(IDxcCompiler3 *This);

    HRESULT(__stdcall *Compile)(
        IDxcCompiler3 *This,
        const DxcBuffer *pSource,
        LPCWSTR *pArguments,
        uint32 argCount,
        IDxcIncludeHandler *pIncludeHandler,
        REFIID riid,
        LPVOID *ppResult
    );

    HRESULT(__stdcall *Disassemble)(
        IDxcCompiler3 *This,
        const DxcBuffer *pObject,
        REFIID riid,
        LPVOID *ppResult
    );
};
struct IDxcCompiler3
{
    const IDxcCompiler3Vtbl *lpVtbl;
};

// We need all this DxcUtils garbage for DXC include dir support. Thanks Microsoft!
using IMalloc =  struct IMalloc;
using IStream =  struct IStream;
using DxcDefine =  struct DxcDefine;
using IDxcCompilerArgs =  struct IDxcCompilerArgs;

static struct
{
    uint32 Data1;
    uint16 Data2;
    uint16 Data3;
    uint8 Data4[8];
} CLSID_DxcUtils = {
    .Data1 = 0x6245d6af,
    .Data2 = 0x66e0,
    .Data3 = 0x48fd,
    .Data4 = {0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c}};
static uint8 IID_IDxcUtils[] = {
    0xcb, 0xc4, 0x05, 0x46,
    0x19, 0x20,
    0x2a, 0x49,
    0xad,
    0xa4,
    0x65,
    0xf2,
    0x0b,
    0xb7,
    0xd6,
    0x7f
};
using IDxcUtilsVtbl =  struct IDxcUtilsVtbl
{
    HRESULT (__stdcall *QueryInterface)(void *pSelf, REFIID riid, void **ppvObject);
    ULONG (__stdcall *AddRef)(void *pSelf);
    ULONG (__stdcall *Release)(void *pSelf);

    HRESULT (__stdcall *CreateBlobFromBlob)(void *pSelf, IDxcBlob *pBlob, UINT offset, UINT length, IDxcBlob **ppResult);
    HRESULT (__stdcall *CreateBlobFromPinned)(void *pSelf, LPCVOID pData, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding);
    HRESULT (__stdcall *MoveToBlob)(void *pSelf, LPCVOID pData, IMalloc *pIMalloc, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding);
    HRESULT (__stdcall *CreateBlob)(void *pSelf, LPCVOID pData, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding);
    HRESULT (__stdcall *LoadFile)(void *pSelf, LPCWSTR pFileName, UINT *pCodePage, IDxcBlobEncoding **pBlobEncoding);
    HRESULT (__stdcall *CreateReadOnlyStreamFromBlob)(void *pSelf, IDxcBlob *pBlob, IStream **ppStream);
    HRESULT (__stdcall *CreateDefaultIncludeHandler)(void *pSelf, IDxcIncludeHandler **ppResult);
    HRESULT (__stdcall *GetBlobAsUtf8)(void *pSelf, IDxcBlob *pBlob, IDxcBlobUtf8 **pBlobEncoding);
    HRESULT (__stdcall *GetBlobAsWide)(void *pSelf, IDxcBlob *pBlob, IDxcBlobWide **pBlobEncoding);
    HRESULT (__stdcall *GetDxilContainerPart)(void *pSelf, const DxcBuffer *pShader, UINT DxcPart, void **ppPartData, UINT *pPartSizeInBytes);
    HRESULT (__stdcall *CreateReflection)(void *pSelf, const DxcBuffer *pData, REFIID iid, void **ppvReflection);
    HRESULT (__stdcall *BuildArguments)(void *pSelf, LPCWSTR pSourceName, LPCWSTR pEntryPoint, LPCWSTR pTargetProfile, LPCWSTR *pArguments, UINT argCount, const DxcDefine *pDefines, UINT defineCount, IDxcCompilerArgs **ppArgs);
    HRESULT (__stdcall *GetPDBContents)(void *pSelf, IDxcBlob *pPDBBlob, IDxcBlob **ppHash, IDxcBlob **ppContainer);
};

using IDxcUtils =  struct IDxcUtils;
struct IDxcUtils
{
    const IDxcUtilsVtbl *lpVtbl;
};

/* *INDENT-ON* */ // clang-format on

/* DXCompiler */
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) || defined(SDL_PLATFORM_WINDOWS)
extern "C" HRESULT __stdcall DxcCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
#else
extern "C" HRESULT DxcCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
#endif
