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

GLuint location;

/*GLuint normalPosition;*/
/*GLuint colorPosition;*/
/*GLuint vertexPosition;*/
/*GLuint fudgeLocation;*/
/*GLuint projectionMatrix;*/
/*GLuint modelViewMatrix;*/

struct shader
{
        unsigned int id;
	FILE *sourceFile;
	const GLchar* filename;
        GLchar* source;
	
}vertShader, fragShader;

unsigned int shaderProgram;

/*------------------------------------------------------------------------*/

/* Program Size */
size_t ProgramSize(const GLchar* ShaderFile)
{
        int programFile;
        size_t size = -1;
        
        /* File Open */		
	programFile = open(ShaderFile, O_RDONLY);
	if(!programFile)
	{
	      printf(" COULD NOT LOAD SHADER! \n");
	      return -1;
	}
        
        /* Data Byte */
        size = lseek(programFile, 0, SEEK_END);
        /*printf(" GLSL DATA BYTE: %ld \n", size);*/
        
        /* File Read */ 
        char ShaderBuffer[size];
        read(programFile, ShaderBuffer, size);
        if(!programFile)
        {
              printf(" ERROR BY READ! \n");
              return -1;
        }
        
        /* File Close */
        close(programFile);
        
        return size;
}

/*------------------------------------------------------------------------*/

/* Create Shader */
void createShader(const GLchar* vert, const GLchar* frag)
{	
        size_t size1;
        size_t size2;
        
        /* Create Shader */
	#if SHADER_LOG
	vertShader.id = glCreateShader(GL_VERTEX_SHADER);
	fragShader.id = glCreateShader(GL_FRAGMENT_SHADER);
	#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 */
	glShaderSource(vertShader.id, 1, (const GLchar**)&vertShader.source, NULL);
        glShaderSource(fragShader.id, 1, (const GLchar**)&fragShader.source, NULL);	

        /* Compile Shader */
	if(!compileShader(vertShader.id) || !compileShader(fragShader.id))
        {
		  printf(" COULD NOT COMPILE THE SHADERS! \n");
        }
        
        /* File Close */
	fclose(vertShader.sourceFile);
	fclose(fragShader.sourceFile);
        
        /* Free */
        free(vertShader.source);
        free(fragShader.source);
}	

/*-------------------------------------------------------------------------*/

/* Compile Shader */
int compileShader(unsigned int shaderID)
{
	/* Compile Shader */
	glCompileShader(shaderID);
        int result = 0x8200;
        
	/* Compile Status */
	glGetShaderiv(shaderID, GL_COMPILE_STATUS, &result);	
	if(!result)
	{
		printf(" COULD NOT COMPILE SHADER: %d \n", shaderID);
		outputLog(shaderID);
		glDeleteShader(shaderID);
		return -1;
	}
	
	return shaderID;
}

/*-------------------------------------------------------------------------*/

/* Shader Log */
void outputLog(unsigned int shaderID)
{
        int infoLen = 0;
        int status = 0;
        GLchar *infoLog;

	/* Shader Length */
	glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infoLen);
	infoLog = (GLchar *)malloc(infoLen*sizeof(GLchar)); 
	
	/* Shader Info Log */
	printf(" SHADER CONTAINS ERRORS! \n");	
	glGetShaderInfoLog(shaderID, infoLen, &status, infoLog);
        printf("%s \n", infoLog);
        free(infoLog);
}

/*-------------------------------------------------------------------------*/

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

	/* Create Program */
	shaderProgram = glCreateProgram();
	/* Attach the Shaders to Object */
	glAttachShader(shaderProgram, vertShader.id);
	glAttachShader(shaderProgram, fragShader.id);	
	/* Link the Program */	
	glLinkProgram(shaderProgram);
	
	/* Program Status */
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkStatus);
	if(!linkStatus)
	{	
	        /* Program Info Log */
		glGetProgramInfoLog(shaderProgram, linkStatus, &status, NULL);
		printf( " COULD NOT LINK PROGRAM: \n");
		glDeleteShader(shaderProgram);
		return -1;
	}
	
	return shaderProgram;
}

/*-------------------------------------------------------------------------*/

/* Link Program */	
void linkProgram(unsigned int shaderProgram)
{	
	/* Link the Program */	
	glLinkProgram(shaderProgram);
}

/*-------------------------------------------------------------------------*/

/* Location */
void 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 = glGetAttribLocation(shaderProgram, name);

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

/*-------------------------------------------------------------------------*/

/* Uniform */
void 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);*/

	location = glGetUniformLocation(shaderProgram, name);
	glUniform1f(location, matrix);
}

/*-------------------------------------------------------------------------*/

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

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

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

/*-------------------------------------------------------------------------*/

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

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

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

/*-------------------------------------------------------------------------*/

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

/*-------------------------------------------------------------------------*/

/* Uniform */
void sendUniform3f(unsigned int shaderProgram, const char* name, 
                    const float x, const float y, const float z)
{
        location = glGetUniformLocation(shaderProgram, name);
        glUniform3f(location, x, y, z);
}

/*-------------------------------------------------------------------------*/

/* Scalar Uniform */
void sendUniform(unsigned int shaderProgram, const char* name, const float scalar)
{
        location = glGetUniformLocation(shaderProgram, name);
        glUniform1f(location, scalar);
}

/*-------------------------------------------------------------------------*/

/* Create Program */	
void bindShader(unsigned int shaderProgram)
{	
	/* Bind Program */
	glUseProgram(shaderProgram);
}

/*-------------------------------------------------------------------------*/

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

	/* Delete the Shaders */
	glDetachShader(shaderProgram, vertShader.id);	
	glDetachShader(shaderProgram, fragShader.id);
	glDeleteShader(vertShader.id);
	glDeleteShader(fragShader.id);
	glDeleteShader(shaderProgram);
	
	/* Program Status */
	glGetProgramiv(shaderProgram, GL_DELETE_STATUS, &deletStatus);
	if(!deletStatus)
	{	
	        /* Program Info Log */
		glGetProgramInfoLog(shaderProgram, deletStatus, &status, NULL);
		printf( " COULD NOT DELETE SHADER: \n");
	}
}

/*-------------------------------------------------------------------------*/

