/** \file Shader.cpp
 * \brief The implementation of the 'Shader' class.
 */

#include "Shader.h"

#include "DefaultShaderSource.h"

#include <glew.h>
#include <GL/glut.h>

#include <io.h>
#include <iostream>
#include <stdio.h>
#include <fstream>

namespace OSkA
{
	Shader::Shader()
	{
	}

	Shader::~Shader()
	{
		glDetachShader(shaderProgram, vertexShaderObject);
		glDetachShader(shaderProgram, fragmentShaderObject);

		glDeleteShader(vertexShaderObject);
		glDeleteShader(fragmentShaderObject);

		glDeleteProgram(shaderProgram);
	}

	std::string Shader::initDefaultShaderTexture()
	{
		return initNewShader(DEFAULT_VERT_SOURCE_TEXTURE, DEFAULT_FRAG_SOURCE_TEXTURE);
	}

	std::string Shader::initDefaultShaderUniform()
	{
		return initNewShader(DEFAULT_VERT_SOURCE_UNIFORM, DEFAULT_FRAG_SOURCE_UNIFORM);
	}

	std::string Shader::initNewShader(std::string vertexSource, std::string fragmentSource)
	{
		std::string returnText="";

		std::string shaderVert=vertexSource;
		const char* source=shaderVert.c_str();
		int len=shaderVert.length();
		vertexShaderObject=glCreateShader(GL_VERTEX_SHADER);
		glShaderSourceARB(vertexShaderObject, 1, &source, &len);
		glCompileShaderARB(vertexShaderObject);

		std::string shaderFrag=fragmentSource;
		const char* source2=shaderFrag.c_str();
		int len2=shaderFrag.length();
		fragmentShaderObject=glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSourceARB(fragmentShaderObject, 1, &source2, &len2);
		glCompileShaderARB(fragmentShaderObject);

		shaderProgram=glCreateProgram();
		glAttachShader(shaderProgram, vertexShaderObject);
		glAttachShader(shaderProgram, fragmentShaderObject);
		glLinkProgram(shaderProgram);

		int infologLength=0;
		 int charsWritten=0;
		 char *infoLog;
		 glGetObjectParameterivARB(shaderProgram, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
		 if(infologLength > 0)
		 {
			infoLog=(char *)malloc(infologLength);
			glGetInfoLogARB(shaderProgram, infologLength, &charsWritten, infoLog);
			returnText=infoLog;
			free(infoLog);
		 }

		return returnText;
	}

	std::string Shader::initNewShaderFromFile(char* vertexSourceFile, char* fragmentSourceFile)
	{
		std::string vert, frag;
		if(!loadTextFile(vertexSourceFile, &vert))
			return "Error loading vertex source file";
		if(!loadTextFile(fragmentSourceFile, &frag))
			return "Error loading fragment source file";
		return initNewShader(vert, frag);
	}

	void Shader::enable()
	{
		glUseProgram(shaderProgram);
	}

	void Shader::disable()
	{
		glUseProgram(0);
	}

	unsigned int Shader::getAttributeLocation(char* name)
	{
		return glGetAttribLocation(shaderProgram, name);
	}

	unsigned int Shader::getUniformLocation(char* name)
	{
		return glGetUniformLocation(shaderProgram, name);
	}

	void Shader::setVertexAttribPointer(unsigned int location, int size, int type, bool normalized, int stride, void* data)
	{
		glVertexAttribPointer(location, size, type, normalized, stride, data);
	}

	void Shader::setUniformMatrix4fv(unsigned int location, int count, bool transpose, float* data)
	{
		glUniformMatrix4fv(location, count, transpose, data);
	}

	void Shader::setUniform1f(unsigned int location, float value)
	{
		glUniform1f(location, value);
	}

	void Shader::setUniform1i(unsigned int location, int value)
	{
		glUniform1i(location, value);
	}

	bool Shader::loadTextFile(char* filename, std::string* storage)
	{
		std::ifstream file;
		file.open(filename, std::ios::in);
		if(!file)return false;

		while(file.good())
		{
			char c=file.get();
			if((unsigned int)c < 255)
				*storage +=c;
		}
		return true;
	}
}
