#pragma once
#include <fstream>
#include <vector>
#include <string>
#include <glad/glad.h>
#include <glm/gtc/type_ptr.hpp>

#include "Enums.h"


namespace Nebulix 
{
	class Shader
	{
	public:
		Shader() = delete;
		Shader(std::string& vertexShaderfile, std::string& fragmentShaderfile);
		~Shader() 
		{
			glDeleteProgram(shaderId);
		}

		/// <summary>
		/// To actually use and activate the shader, call this.
		/// </summary>
		void Use() { glUseProgram(shaderId); }

		void SetBool(const std::string& name, bool value) const 
		{
			glUniform1i(glGetUniformLocation(shaderId, name.c_str()), (int)value);
		}
		void SetInt(const std::string& name, int value) const
		{
			glUniform1i(glGetUniformLocation(shaderId, name.c_str()), value);
		}
		void SetFloat(const std::string& name, float x) const
		{
			glUniform1f(glGetUniformLocation(shaderId, name.c_str()), x);
		}
		void SetFloat(const std::string& name, float x, float y) const
		{
			glUniform2f(glGetUniformLocation(shaderId, name.c_str()), x, y);
		}
		void SetFloat(const std::string& name, float x, float y, float z) const
		{
			glUniform3f(glGetUniformLocation(shaderId, name.c_str()), x, y, z);
		}
		void SetFloat(const std::string& name, float x, float y, float z, float w) const
		{
			glUniform4f(glGetUniformLocation(shaderId, name.c_str()), x, y, z, w);
		}
		void SetFloat(const std::string& name, glm::vec3 vec) const
		{
			glUniform3f(glGetUniformLocation(shaderId, name.c_str()), vec.x, vec.y, vec.z);
		}

		void SetMatrix(const std::string& name, glm::mat4 mat) const
		{
			glUniformMatrix4fv(glGetUniformLocation(shaderId, name.c_str()), 1, GL_FALSE, glm::value_ptr(mat));
		}

	private:
		std::string &_vertexFile, &_fragmentFile;
		GLuint shaderId;

		void CompileShadercode(std::ifstream& shaderSource, GLuint& shaderId, ShaderType type);
		void CreateProgram(GLuint& vertexShader, GLuint& fragmentShader);
	};
}
