/*********************************************************************
*                                                                    *          
*                              FLY                                   *
*                           --E#Y#E--				     *
*                         ===MUSCLE===                               *
*                                                                    *
*********************************************************************/
/* Header Files */
#include "uniform.h"

//---------------------------------------------------------------------------

/* Program Size */
size_t UNIFORM::ProgramSize(const GLchar* ShaderFile)
{
        std::ifstream programFile;
        char* ShaderBuffer; 
        size_t size = -1;
        
        /* File Open */		
	programFile.open(ShaderFile);
	if(!programFile.good()) 
	{
	      std::cerr << " COULD NOT LOAD SHADER! " << std::endl;
	      return -1;
	}
	
	programFile.seekg(0, std::ios::end);
	size = programFile.tellg();
        
        /* Data Byte */
        //std::cout << " GLSL DATA BYTE: " << size << " \n";
        programFile.seekg(0, std::ios::beg);
        
        /* File Read */
        ShaderBuffer = new char[size];
        programFile.read(ShaderBuffer, size);
        if(!programFile)
        {
              std::cerr << " ERROR BY READ! " << std::endl;
              return -1;
        }
        
        /* File Close */
        programFile.close();
        
        return size;
}

//---------------------------------------------------------------------------

/* Create Shader */
void UNIFORM::createShader(const GLchar* vert, const GLchar* frag)
{
        size_t size1;
        size_t size2;
        
        /* Create Shader */
	#if SHADER_LOG
	vertShader.id = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	fragShader.id = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	#else
	vertShader.id = glCreateShaderObjectARB(GL_ARB_vertex_shader);
	fragShader.id = glCreateShaderObjectARB(GL_ARB_fragment_shader);
	#endif
        
        /* Shader Filename */
        vertShader.filename = vert;
        fragShader.filename = frag;      
        /* File Open */
        vertShader.sourceFile = fopen(vertShader.filename, "rb");
        fragShader.sourceFile = fopen(fragShader.filename, "rb");

	/* Version 1 / STABLE */
	size1 = ProgramSize(vertShader.filename);
	size2 = ProgramSize(fragShader.filename);
	/* Malloc */
	vertShader.source = (GLchar *)malloc(size1*sizeof(GLchar)); 
	fragShader.source = (GLchar *)malloc(size2*sizeof(GLchar)); 
	/* Read File */
	vertShader.id = fread(vertShader.source, size1, 1, vertShader.sourceFile);
	fragShader.id = fread(fragShader.source, size2, 1, fragShader.sourceFile);
	
	/* Shader Empty */
	if(!vertShader.source || !fragShader.source)
        {
                exit(1);
        }
		
	/* Send to Source */
	glShaderSourceARB(vertShader.id, 1, (const GLchar**)&vertShader.source, NULL);
        glShaderSourceARB(fragShader.id, 1, (const GLchar**)&fragShader.source, NULL);

	/* Compile Shader */
	if(!compileShader(vertShader.id) || !compileShader(fragShader.id))
        {
		  std::cerr << " COULD NOT COMPILE THE SHADERS! " << std::endl;
        }
        
        /* File Close */
	fclose(vertShader.sourceFile);
	fclose(fragShader.sourceFile);
        
        /* Free */
        free(vertShader.source);
        free(fragShader.source);
}	

//---------------------------------------------------------------------------

/* Compile Shader */
int UNIFORM::compileShader(unsigned int shaderID)
{
	/* Compile Shader */
	glCompileShaderARB(shaderID);
	int result = 0x8200;
        
	/* Compile Status */
	glGetObjectParameterivARB(shaderID, GL_OBJECT_COMPILE_STATUS_ARB, &result);	
	if(!result)
	{
		std::cout << " COULD NOT COMPILE SHADER: " << shaderID << std::endl;
		outputLog(shaderID);
		glDeleteObjectARB(shaderID);
		return -1;
	}
	
	return shaderID;
}

//---------------------------------------------------------------------------

/* Shader Log */
void UNIFORM::outputLog(unsigned int shaderID)
{	
        int infoLen = 0;
        int status = 0;
        
        vector<char> infoLog;		// VECTOR VERSION 
	//GLcharARB *infoLog;		// GLCHAR VERSION 

	/* Shader Length */
	glGetObjectParameterivARB(shaderID, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infoLen);
        infoLog.resize(infoLen);                                              // Version 1 
        //infoLog = (GLcharARB *)malloc(infoLen*sizeof(GLcharARB));           // Version 2 
	
	/* Shader Info Log */
	std::cerr << " SHADER CONTAINS ERRORS! " << std::endl;
	glGetInfoLogARB(shaderID, infoLen, &status, &infoLog[0]);
	std::cerr << string(infoLog.begin(), infoLog.end()) << std::endl;     // Version 1 
	//std::cerr << infoLog << std::endl;                                  // Version 2
	//free(infoLog);
}

//---------------------------------------------------------------------------

/* Create Program */
int UNIFORM::createProgram(unsigned int shaderProgram) 	
{
        int linkStatus = 0;
        int status = 0;

	/* Create Program */
	shaderProgram = glCreateProgramObjectARB();
	/* Attach the Shaders to Object */
	glAttachObjectARB(shaderProgram, vertShader.id);
	glAttachObjectARB(shaderProgram, fragShader.id);	
	/* Link the Program */	
	glLinkProgramARB(shaderProgram);
	
	/* Program Status */
	glGetObjectParameterivARB(shaderProgram, GL_OBJECT_LINK_STATUS_ARB, &linkStatus);	
	if(!linkStatus)
	{	
	        /* Program Info Log */
		glGetInfoLogARB(shaderProgram, linkStatus, &status, NULL);
		std::cout << " COULD NOT LINK PROGRAM: " << std::endl;
		glDeleteObjectARB(shaderProgram);
		return -1;
	}
	
	return shaderProgram;
}

//---------------------------------------------------------------------------

/* Link Program */	
void UNIFORM::linkProgram(unsigned int shaderProgram)
{	
	/* Link the Program */	
	glLinkProgramARB(shaderProgram);
}

//---------------------------------------------------------------------------

/* Location */
void UNIFORM::Location(unsigned int shaderProgram, unsigned int location, const char* name)
{
        /* Get Location */
        //vertexPosition = glGetAttribLocation(shaderProgram, "aVertex");
        //colorPosition = glGetAttribLocation(shaderProgram, "aColor");
        //normalPosition = glGetAttribLocation(shaderProgram, "aTexCoord");
        location = glGetAttribLocationARB(shaderProgram, name);

        /* Bind Location */
        //glBindAttribLocation(shaderProgram, vertexPosition, name);
        //glBindAttribLocation(shaderProgram, colorPosition, name);
        //glBindAttribLocation(shaderProgram, normalPosition, name);
        glBindAttribLocationARB(shaderProgram, location, name);
}	

//---------------------------------------------------------------------------

/* Uniform */
void UNIFORM::Uniform(unsigned int shaderProgram, const char* name, const float matrix)
{
        /* Get Uniform */
        //glUniform1f(glGetUniformLocation(shaderProgram, "aVertex"), 100.0);
        //glUniform4f(glGetUniformLocation(shaderProgram, "outColor"), 0, 0, 0, 1);
        //glUniform1f(glGetUniformLocation(shaderProgram, "texture0"), 0);

	/* Get Uniform */
	location = glGetUniformLocationARB(shaderProgram, name);
	glUniform1fARB(location, matrix);
}

//---------------------------------------------------------------------------

/* Uniform 4x4 */
void UNIFORM::Uniform4x4(unsigned int shaderProgram, const char* name, bool transpose, const float* matrix)
{
        /* Get Location */
        transpose = 0;

        /* Get Uniform 4x4 */
        //fudgeLocation = glGetUniformLocation(shaderProgram, "uFudgeFactor");
        //projectionMatrix = glGetUniformLocation(shaderProgram, "uProjectionMatrix");
        //modelViewMatrix = glGetUniformLocation(shaderProgram, "uModelViewMatrix");
        location = glGetUniformLocationARB(shaderProgram, name);

        /* Uniform 4x4 */
        //glUniformMatrix4fv(fudgeLocation, 1, transpose, matrix);
        //glUniformMatrix4fv(projectionMatrix, 1, transpose, matrix);
        //glUniformMatrix4fv(modelViewMatrix, 1, transpose, matrix);
        glUniformMatrix4fvARB(location, 1, transpose, matrix);
}

//---------------------------------------------------------------------------

/* Uniform 3x3 */
void UNIFORM::Uniform3x3(unsigned int shaderProgram, const char* name, bool transpose, const float* matrix)
{
        /* Get Location */
        transpose = 0;

        /* Get Uniform 3x3 */
        //fudgeLocation = glGetUniformLocation(shaderProgram, "uFudgeFactor");
        //projectionMatrix = glGetUniformLocation(shaderProgram, "uProjectionMatrix");
        //modelViewMatrix = glGetUniformLocation(shaderProgram, "uModelViewMatrix");
        location = glGetUniformLocationARB(shaderProgram, name);

        /* Uniform 3x3 */
        //glUniformMatrix3fv(fudgeLocation, 1, transpose, matrix);
        //glUniformMatrix3fv(projectionMatrix, 1, transpose, matrix);
        //glUniformMatrix3fv(modelViewMatrix, 1, transpose, matrix);
        glUniformMatrix3fvARB(location, 1, transpose, matrix);
}

//---------------------------------------------------------------------------

/* Color Uniform */
void UNIFORM::sendUniform4f(unsigned int shaderProgram, const char* name, 
          const float red, const float green, const float blue, const float alpha)
{
        location = glGetUniformLocationARB(shaderProgram, name);
        glUniform4fARB(location, red, green, blue, alpha);
}

//---------------------------------------------------------------------------

/* Uniform */
void UNIFORM::sendUniform3f(unsigned int shaderProgram, const char* name, 
                            const float x, const float y, const float z)
{
        location = glGetUniformLocationARB(shaderProgram, name);
        glUniform3fARB(location, x, y, z);
}

//---------------------------------------------------------------------------

/* Scalar Uniform */
void UNIFORM::sendUniform(unsigned int shaderProgram, const char* name, const float scalar)
{
        location = glGetUniformLocationARB(shaderProgram, name);
        glUniform1fARB(location, scalar);
}

//---------------------------------------------------------------------------

/* Create Program */	
void UNIFORM::bindShader(unsigned int shaderProgram)
{	
	/* Bind Program */
	glUseProgramObjectARB(shaderProgram);
}

//---------------------------------------------------------------------------

/* Delete Shader */
void UNIFORM::DeleteShader(unsigned int shaderProgram)
{
        int deletStatus = 0x8200;
        int status = 0;
        
        /* Fix Pipeline */
        glUseProgramObjectARB(0);

	/* Delete the Shaders */
	glDetachObjectARB(shaderProgram, vertShader.id);	
	glDetachObjectARB(shaderProgram, fragShader.id);
	glDeleteObjectARB(vertShader.id);
	glDeleteObjectARB(fragShader.id);
	glDeleteObjectARB(shaderProgram);
	
	/* Program Status */
	glGetObjectParameterivARB(shaderProgram, GL_OBJECT_DELETE_STATUS_ARB, &deletStatus);
	if(!deletStatus)
	{	
	        /* Program Info Log */
		glGetInfoLogARB(shaderProgram, deletStatus, &status, NULL);		
		std::cout << " COULD NOT DELETE SHADER: " << std::endl;
	}
}

//---------------------------------------------------------------------------

