#include "graphics/shader.h"

Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
	std::string vertexSource;
	std::string fragmentSource;
	std::ifstream vertexStream;
	std::ifstream fragmentStream;

	vertexStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
	fragmentStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
	try
	{
		vertexStream.open(vertexPath);
		fragmentStream.open(fragmentPath);

		std::stringstream vStringStream, fStringStream;

		vStringStream << vertexStream.rdbuf();
		fStringStream << fragmentStream.rdbuf();

		vertexSource = vStringStream.str();
		fragmentSource = fStringStream.str();

		vertexStream.close();
		fragmentStream.close();
	}
	catch(std::exception e)
	{
		std::cout << "failed to open shader files " << e.what() << std::endl;
		return;
	}
	const char* vSource = vertexSource.c_str();
	const char* fSource = fragmentSource.c_str();

	unsigned int vertexid, fragmentid;
	char infoLog[512];
	int success;

	//compile vertex shader
	vertexid = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexid, 1, &vSource, NULL);
	glCompileShader(vertexid);

	glGetShaderiv(vertexid, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(vertexid, 512, NULL, infoLog);
		std::cout << "VERTEX SHADER COMPILE ERROR\n" << infoLog << std::endl;
	}

	//compile fragment shader
	fragmentid = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentid, 1, &fSource, NULL);
	glCompileShader(fragmentid);

	glGetShaderiv(fragmentid, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentid, 512, NULL, infoLog);
		std::cout << "FRAGMENT SHADER COMPILE ERROR\n" << infoLog << std::endl;
	}

	//create and link program with compiled shaders
	ID = glCreateProgram();
	glAttachShader(ID, vertexid);
	glAttachShader(ID, fragmentid);
	glLinkProgram(ID);

	glGetProgramiv(ID, GL_LINK_STATUS, &success);
	if (!success)
	{
		glGetProgramInfoLog(ID, 512, NULL, infoLog);
		std::cout << "PROGRAM LINKER ERROR\n" << infoLog << std::endl;
	}

	glDeleteShader(vertexid);
	glDeleteShader(fragmentid);
}

void Shader::setFloat(const std::string& name, float value)
{
	glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::setInt(const std::string& name, int value)
{
	glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::setBool(const std::string& name, bool value)
{
	glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::setMatrix4f(const std::string& name, const float* value)
{
	GLuint loc = glGetUniformLocation(ID, name.c_str());
	glUniformMatrix4fv(loc, 1, GL_FALSE, value);
}

Shader::~Shader()
{
	glDeleteProgram(ID);
}
