#ifndef RENDER_SHADER_H
#define RENDER_SHADER_H

#include <d3d11.h>
#include <filesystem>
#include <wrl/client.h>
#include <unordered_map>

/**
 * 约定 CBuffer
 *   - CBScene  (b0)
 *   - CBPerMat (b1)
 *   - CBPerObj (b2)
 *   - CBShadow (b3)
 *
 * 约定 TextureBuffer
 *   -
 */

class Shader {
public:
    struct TexBufDesc {
        std::string name;
        UINT bindPoint{};
        UINT bindCount{};
        D3D_SHADER_INPUT_TYPE type;
        D3D_SRV_DIMENSION dimension;
    };
    struct PropertyDesc {
        std::string name;
        uint32_t offset = 0;
        uint32_t size = 0;

        uint32_t rows = 0; // matrix 行数，或数值类型通常为 1
        uint32_t columns = 0; // vector 分量数，或 matrix 列数

        union DefaultValue {
            int Int;
            int Int2[2];
            int Int3[3];
            int Int4[4];
            float Float;
            float Float2[2];
            float Float3[3];
            float Float4[4];
            float Float3x3[3][3];
            float Float4x4[4][4];
        } defaultValue;

        D3D_SHADER_VARIABLE_CLASS varClass{}; // scalar / vector / matrix / struct / object
        D3D_SHADER_VARIABLE_TYPE varType{}; // float / int / uint / bool / texture / sampler ...
    };
    struct CBufferDesc {
        std::string name;
        uint32_t bindSlot = 0;
        uint32_t byteSize = 0;
        std::vector<PropertyDesc> properties;
    };
    enum struct Entry : std::uint32_t {
        None = 0,
        VertexShader = 1 << 0,
        PixelShader = 1 << 1
    };

public: // static
    static Shader *S_GetDefaultShader(ID3D11Device *device);
    static Shader *S_CompileShader(const std::filesystem::path &shaderPath, Entry entry, ID3D11Device *device);
    static Shader *S_GetShader(const std::string &fileName);

private:
    static std::unordered_map<std::string, Shader *> map_Name_Shader;

public: // instance
    [[nodiscard]] bool HasEntry(Entry entry) const;
    [[nodiscard]] const std::vector<CBufferDesc> *GetCBufferDescs() const noexcept { return &m_cb_desc; }
    [[nodiscard]] const std::vector<TexBufDesc> *GetTexBufferDescs() const noexcept { return &m_texBuf_desc; }
    [[nodiscard]] const CBufferDesc *GetPerMatCBuffer() const noexcept { return m_perMatCbDesc; }

public:
    Entry m_entries{};
    const std::string m_name;
    const std::filesystem::path m_path;
    Microsoft::WRL::ComPtr<ID3DBlob> m_vertex_blob;
    Microsoft::WRL::ComPtr<ID3DBlob> m_pixel_blob;
    Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertex_shader;
    Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixel_shader;
    Microsoft::WRL::ComPtr<ID3D11InputLayout> m_input_layout;

private:
    Shader(const char *name, const std::filesystem::path &path);

private:
    std::vector<CBufferDesc> m_cb_desc;
    CBufferDesc *m_perMatCbDesc{}; // 如果该shader包含有效的CBPerMat,则是其Desc,否则为null
    std::vector<TexBufDesc> m_texBuf_desc;
};


//=
constexpr Shader::Entry operator|(Shader::Entry l, Shader::Entry r) {
    using T = std::underlying_type_t<Shader::Entry>;
    return static_cast<Shader::Entry>(static_cast<T>(l) | static_cast<T>(r));
}
constexpr Shader::Entry operator&(Shader::Entry l, Shader::Entry r) {
    using T = std::underlying_type_t<Shader::Entry>;
    return static_cast<Shader::Entry>(static_cast<T>(l) & static_cast<T>(r));
}
constexpr Shader::Entry &operator|=(Shader::Entry &l, Shader::Entry r) {
    l = l | r;
    return l;
}











#endif //RENDER_SHADER_H
