#pragma once

class render_target;
class texture;
class slang_handle;

class shader
{
public:
    virtual ~shader() = default;
    explicit shader(const std::shared_ptr<slang_handle>& handle) : handle_(handle) {}

    virtual bool init() { return false; }
    [[nodiscard]] virtual bool is_initialized() const = 0;
    [[nodiscard]] virtual bool is_valid() const { return handle_ != nullptr && is_initialized(); }
    
    virtual void bind() = 0;
    virtual void unbind() = 0;
    virtual void compute(int x, int y, int z) {}
    virtual void draw(int width, int height, std::shared_ptr<shader> in_vertex_shader = nullptr) {}

    // param setters
    virtual void set_constant_buffer(const char* name, void* buffer, int size) = 0;
    virtual void set_uav_buffer(const char* name, void* buffer, int size, int element_size) {}
    virtual void set_texture(const char* name, std::shared_ptr<texture> in_texture) = 0;
    virtual void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target) = 0;

    template<typename T>
    void set_constant_buffer(const char* name, const T& buffer)
    {
        set_constant_buffer(name, (void*)&buffer, sizeof(T));
    }
    template<typename T>
    void set_constant_buffer(const char* name, const std::vector<T>& buffer)
    {
        set_constant_buffer(name, (void*)buffer.data(), sizeof(T) * buffer.size());
    }
    
    template<typename T>
    void set_uav_buffer(const char* name, const T& buffer)
    {
        set_uav_buffer(name, (void*)&buffer, sizeof(T));
    }
    template<typename T>
    void set_uav_buffer(const char* name, const std::vector<T>& buffer)
    {
        set_uav_buffer(name, (void*)buffer.data(), sizeof(T) * buffer.size());
    }
protected:
    std::shared_ptr<slang_handle> handle_;
};