#ifndef OPENGL_SHADER_HPP
#define OPENGL_SHADER_HPP

#include "open_engine/renderer/shader.hpp"

#include <unordered_map>
#include <sys/types.h>
#include <glad/glad.h>
#include <string>

namespace OpenEngine {
    class OpenGLShader : public Shader
    {
        public:
            OpenGLShader(const std::string& shader_path);
            OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& frament_src);
            virtual ~OpenGLShader();

            virtual const std::string& GetName() const override { return name; };

            // activate the shader
            virtual void Bind() const override;
            virtual void Unbind() const override;

            // utility uniform functions
            void UploadBool(const std::string &name, bool value) const;
            void UploadInt(const std::string &name, int value) const;
            void UploadIntArray(const std::string &name, int* values, uint32_t count) const;
            void UploadFloat(const std::string &name, float value) const;
            void UploadMat4(const std::string &name, const glm::mat4& value) const;
            void UploadVec2(const std::string &name, const glm::vec2& value) const;
            void UploadVec3(const std::string &name, const glm::vec3& value) const;
            void UploadVec4(const std::string &name, const glm::vec4& value) const;

            virtual void SetBool(const std::string &name, bool value) const override;
            virtual void SetInt(const std::string &name, int value) const override;
            virtual void SetIntArray(const std::string &name, int* values, uint32_t count) const override;
            virtual void SetFloat(const std::string &name, float value) const override;
            virtual void SetMat4(const std::string &name, const glm::mat4& value) const override;
            virtual void SetVec2(const std::string &name, const glm::vec2& value) const override;
            virtual void SetVec3(const std::string &name, const glm::vec3& value) const override;
            virtual void SetVec4(const std::string &name, const glm::vec4& value) const override;

        private:
            std::string ReadFile(const std::string& shader_path);
            std::unordered_map<GLenum, std::string> PreProcess(const std::string& shader_source);
            
            void CompileOrGetVulkanBinaries(const std::unordered_map<GLenum, std::string>& shader_sources);
            void CompileOrGetOpenGLBinaries();
            void CreateProgram();
            void Reflect(GLenum stage, const std::vector<uint32_t>& shader_data);
            //void CheckCompileErrors(unsigned int shader, const std::string& type);

        private:
            std::string name;
            std::string file_path;

            std::unordered_map<GLenum, std::vector<uint32_t>> vulkan_spirv;
            std::unordered_map<GLenum, std::vector<uint32_t>> opengl_spirv;

            std::unordered_map<GLenum, std::string> m_OpenGLSourceCode;

            u_int32_t id;
    };
}

#endif // OPENGL_SHADER_HPP
