// slang-glslang.h
#ifndef SLANG_GLSLANG_H_INCLUDED
#define SLANG_GLSLANG_H_INCLUDED

#include <cstdint>
#include <cstring>
#include <memory>
#include <stddef.h>
#include <type_traits>

typedef void (*glslang_OutputFunc)(void const* data, size_t size, void* userData);

enum
{
    GLSLANG_ACTION_COMPILE_GLSL_TO_SPIRV,
    GLSLANG_ACTION_DISSASSEMBLE_SPIRV,
    GLSLANG_ACTION_OPTIMIZE_SPIRV,
};

struct glsl_SPIRVVersion
{
    int major, minor, patch;
};

// clang-format off

#define SLANG_GLSLANG_COMPILE_REQUEST_1_0(x)  \
    x(sourcePath) \
    x(inputBegin) \
    x(inputEnd) \
    x(diagnosticFunc) \
    x(diagnosticUserData) \
    x(outputFunc) \
    x(outputUserData) \
    x(slangStage) \
    x(action) \
    x(optimizationLevel) \
    x(debugInfoType)

#define SLANG_GLSLANG_FIELD_COPY(name) name = in.name;

// clang-format on

// Pre-declare
struct glslang_CompileRequest_1_1;

// 1.0 version
struct glslang_CompileRequest_1_0
{
    void set(const glslang_CompileRequest_1_1& in);

    char const* sourcePath;

    void const* inputBegin;
    void const* inputEnd;

    glslang_OutputFunc diagnosticFunc;
    void* diagnosticUserData;

    glslang_OutputFunc outputFunc;
    void* outputUserData;

    int slangStage;

    unsigned action;

    unsigned optimizationLevel;
    unsigned debugInfoType;
};

// 1.1 version
struct glslang_CompileRequest_1_1
{
    /// Set from 1.0
    void set(const glslang_CompileRequest_1_0& in);

    size_t sizeInBytes; ///< Size in bytes of this structure

    // START! Embed the glslang_CompileRequest_1_0 fields
    char const* sourcePath;

    void const* inputBegin;
    void const* inputEnd;

    glslang_OutputFunc diagnosticFunc;
    void* diagnosticUserData;

    glslang_OutputFunc outputFunc;
    void* outputUserData;

    int slangStage;

    unsigned action;

    unsigned optimizationLevel;
    unsigned debugInfoType;
    // END! Embed the glslang_CompileRequest_1_0 fields

    const char* spirvTargetName;    /// A valid TargetName. If null will use universal based on the
                                    /// spirVersion.
    glsl_SPIRVVersion spirvVersion; ///< The SPIR-V version. If all are 0 will use the default which
                                    ///< is 1.2 currently
};

// 1.2 version
struct glslang_CompileRequest_1_2
{
    /// Set from 1.1
    void set(const glslang_CompileRequest_1_1& in);

    size_t sizeInBytes; ///< Size in bytes of this structure

    // START! Embed the glslang_CompileRequest_1_0 fields
    char const* sourcePath;

    void const* inputBegin;
    void const* inputEnd;

    glslang_OutputFunc diagnosticFunc;
    void* diagnosticUserData;

    glslang_OutputFunc outputFunc;
    void* outputUserData;

    int slangStage;

    unsigned action;

    unsigned optimizationLevel;
    unsigned debugInfoType;
    // END! Embed the glslang_CompileRequest_1_0 fields

    const char* spirvTargetName;    /// A valid TargetName. If null will use universal based on the
                                    /// spirVersion.
    glsl_SPIRVVersion spirvVersion; ///< The SPIR-V version. If all are 0 will use the default which
                                    ///< is 1.2 currently

    // glslang_CompileRequest_1_2 fields
    const char* entryPointName; // The name of the entrypoint that will appear in output spirv.
};

// 1.3 version
struct glslang_CompileRequest_1_3 : public glslang_CompileRequest_1_2
{
    /// Set from 1.2
    void set(const glslang_CompileRequest_1_2& in);

    // spirv-opt CLI-style pass flags forwarded from `-Xspirv-opt`, registered on top of the preset
    // passes for the selected optimization level. Null / zero count means none. Borrowed for the
    // duration of the synchronous compile call: the producer keeps the storage alive across the
    // call, and the callee neither retains nor frees it.
    const char* const* spirvOptimizationFlags;
    size_t spirvOptimizationFlagCount;
};

// _1_3 is copied/zeroed bytewise (memset + same-type memcpy in the entry-point shims), so it must
// stay trivially copyable, and it must remain a _1_2 subclass so callers can slice to the base.
static_assert(std::is_trivially_copyable<glslang_CompileRequest_1_3>::value);
static_assert(std::is_base_of<glslang_CompileRequest_1_2, glslang_CompileRequest_1_3>::value);
// Total-size regression guard: the two appended fields must be exactly the growth over _1_2 (this
// catches an accidental added/removed field or padding change; it does not police field order).
static_assert(
    sizeof(glslang_CompileRequest_1_3) ==
    sizeof(glslang_CompileRequest_1_2) + sizeof(const char* const*) + sizeof(size_t));

inline void glslang_CompileRequest_1_0::set(const glslang_CompileRequest_1_1& in)
{
    SLANG_GLSLANG_COMPILE_REQUEST_1_0(SLANG_GLSLANG_FIELD_COPY)
}

inline void glslang_CompileRequest_1_1::set(const glslang_CompileRequest_1_0& in)
{
    SLANG_GLSLANG_COMPILE_REQUEST_1_0(SLANG_GLSLANG_FIELD_COPY)
}

inline void glslang_CompileRequest_1_2::set(const glslang_CompileRequest_1_1& in)
{
    memcpy(this, &in, sizeof(in));
}

inline void glslang_CompileRequest_1_3::set(const glslang_CompileRequest_1_2& in)
{
    // Copy the _1_2 base, then zero the fields _1_3 adds so an old caller that only knows about
    // _1_2 doesn't leave them with garbage. `in.sizeInBytes` is the _1_2 size, so restore this
    // struct's own size after the copy.
    static_cast<glslang_CompileRequest_1_2&>(*this) = in;
    sizeInBytes = sizeof(*this);
    spirvOptimizationFlags = nullptr;
    spirvOptimizationFlagCount = 0;
}

typedef struct glslang_LinkRequest_t
{
    const uint32_t** modules;    // Input: array of pointers to SPIR-V modules
    const uint32_t* moduleSizes; // Input: array of sizes of SPIR-V modules in 32-bit words
    int moduleCount;             // Input: number of modules in the array
    const uint32_t* linkResult;  // Output: pointer to linked SPIR-V module
    size_t linkResultSize;       // Output: size of the linked SPIR-V module in 32-bit words
} glslang_LinkRequest;

typedef int (*glslang_CompileFunc_1_0)(glslang_CompileRequest_1_0* request);
typedef int (*glslang_CompileFunc_1_1)(glslang_CompileRequest_1_1* request);
typedef int (*glslang_CompileFunc_1_2)(glslang_CompileRequest_1_2* request);
typedef int (*glslang_CompileFunc_1_3)(glslang_CompileRequest_1_3* request);
typedef bool (*glslang_ValidateSPIRVFunc)(const uint32_t* contents, int contentsSize);
typedef bool (*glslang_DisassembleSPIRVFunc)(const uint32_t* contents, int contentsSize);
typedef bool (*glslang_DisassembleSPIRVWithResultFunc)(
    const uint32_t* contents,
    int contentsSize,
    char** outString);
typedef void (*glslang_FreeDisassemblyFunc)(char* disassembly);
typedef int (*glslang_LinkSPIRVFunc)(glslang_LinkRequest* request);
#endif
