#define NO_SDL_GLEXT

#include <GL/glew.h>
#include <SDL/SDL_OpenGL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

#include "demo_shaders.h"
#include "demo_init_gl.h"

GLuint pshaders[NPROGRAMS];
GLuint vshaders[NPROGRAMS];
GLuint programs[NPROGRAMS];

GLhandleARB pshadersARB[NPROGRAMS];
GLhandleARB vshadersARB[NPROGRAMS];
GLhandleARB programsARB[NPROGRAMS];

char *ps_filenames[] = {	"./data/shaders/plasma.frag", 
							"./data/shaders/polar.frag", 
							"./data/shaders/bump.frag", 
							"./data/shaders/blur.frag", 
							"./data/shaders/fire.frag", 
							"./data/shaders/emboss.frag",
							"./data/shaders/voronoi.frag",
							"./data/shaders/water.frag", 
							"./data/shaders/water_displacement.frag", 
							"./data/shaders/implicit_plasma.frag", 
							"./data/shaders/implicit_spheres.frag", 
							"./data/shaders/landscape.frag", 
							"./data/shaders/shading.frag", 
							"./data/shaders/envmap.frag", 
							"./data/shaders/implicit_polar.frag", 
							"./data/shaders/effedares_final_zoom.frag", 
							"./data/shaders/effedares_final_rotozoom.frag"};

char *vs_filenames[] = {	"./data/shaders/plasma.vert", 
							"./data/shaders/polar.vert", 
							"./data/shaders/bump.vert", 
							"./data/shaders/blur.vert", 
							"./data/shaders/fire.vert", 
							"./data/shaders/emboss.vert", 
							"./data/shaders/voronoi.vert", 
							"./data/shaders/water.vert", 
							"./data/shaders/water_displacement.vert", 
							"./data/shaders/implicit_plasma.vert", 
							"./data/shaders/implicit_spheres.vert", 
							"./data/shaders/landscape.vert", 
							"./data/shaders/shading.vert", 
							"./data/shaders/envmap.vert", 
							"./data/shaders/implicit_polar.vert", 
							"./data/shaders/effedares_final_zoom.vert", 
							"./data/shaders/effedares_final_rotozoom.vert"};


GLint MyGetUniformLocation(int program_id, GLchar *sh_var_name)
{
	if (HasOpenGL2)
		return glGetUniformLocation(programs[program_id], sh_var_name);
	else
		return glGetUniformLocationARB(programsARB[program_id], sh_var_name);
}

void MyUniformnfv(GLint var_name, int elements_num, float *values)
{
	switch(elements_num)
	{
		case 1:
			if (HasOpenGL2)
				glUniform1fv(var_name, elements_num, values);
			else
				glUniform1fvARB(var_name, elements_num, values);
		break;

		case 2:
			if (HasOpenGL2)
				glUniform2fv(var_name, elements_num, values);
			else
				glUniform2fvARB(var_name, elements_num, values);
		break;
		
		case 3:
			if (HasOpenGL2)
				glUniform3fv(var_name, elements_num, values);
			else
				glUniform3fvARB(var_name, elements_num, values);
		break;

		case 4:
			if (HasOpenGL2)
				glUniform4fv(var_name, elements_num, values);
			else
				glUniform4fvARB(var_name, elements_num, values);
		break;
	}
}

void MyUniform1f(GLint var_name, float value)
{
	if (HasOpenGL2)
		glUniform1f(var_name, value);
	else
		glUniform1fARB(var_name, value);
}

void MyUniform1i(GLint var_name, int value)
{
	if (HasOpenGL2)
		glUniform1i(var_name, value);
	else
		glUniform1iARB(var_name, value);
}

void MyUseProgram(int program_id)
{
	if (HasOpenGL2)
		glUseProgram(programs[program_id]);
	else
		glUseProgramObjectARB(programsARB[program_id]);
}

void NotUseProgram()
{
	if (HasOpenGL2)
		glUseProgram(0);
	else
		glUseProgramObjectARB(0);
}

char *textFileRead(char *fn)
{
	FILE *fp;
	char *content = NULL;

	int count=0;

	if (fn != NULL) {
		fp = fopen(fn,"rt");

		if (fp != NULL) {

      fseek(fp, 0, SEEK_END);
      count = ftell(fp);
      rewind(fp);

			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	return content;
}

void printShaderInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n\n",infoLog);
        free(infoLog);
    }
}

void printProgramInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n\n",infoLog);
        free(infoLog);
    }
}

void printInfoLogARB(GLhandleARB obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n\n",infoLog);
        free(infoLog);
    }
}

void LoadShaders()
{
	char *ps_source[NPROGRAMS];
	char *vs_source[NPROGRAMS];

	for (int i=0; i<NPROGRAMS; i++)
		ps_source[i] = NULL;

	for (int i=0; i<NPROGRAMS; i++)
		vs_source[i] = NULL;


	for (int i=0; i<NPROGRAMS; i++)
	{
		if (HasOpenGL2)
			pshaders[i] = glCreateShader(GL_FRAGMENT_SHADER);
		else
			pshadersARB[i] = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

		ps_source[i] = textFileRead(ps_filenames[i]);

		const char *pss = ps_source[i];
		if (HasOpenGL2)
		{
			glShaderSource(pshaders[i], 1, &pss, NULL);
			glCompileShader(pshaders[i]);
			printShaderInfoLog(pshaders[i]);
		}
		else
		{
			glShaderSourceARB(pshadersARB[i], 1, &pss, NULL);
			glCompileShaderARB(pshadersARB[i]);
			printInfoLogARB(pshadersARB[i]);
		}
	}

	for (int i=0; i<NPROGRAMS; i++)
	{
		if (HasOpenGL2)
			vshaders[i] = glCreateShader(GL_VERTEX_SHADER);
		else
			vshadersARB[i] = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);

		vs_source[i] = textFileRead(vs_filenames[i]);

		const char *vss = vs_source[i];
		if (HasOpenGL2)
		{
			glShaderSource(vshaders[i], 1, &vss, NULL);
			glCompileShader(vshaders[i]);
			printShaderInfoLog(vshaders[i]);
		}
		else
		{
			glShaderSourceARB(vshadersARB[i], 1, &vss, NULL);
			glCompileShaderARB(vshadersARB[i]);
			printInfoLogARB(vshadersARB[i]);
		}
	}
}

void InitPrograms()
{
	for (int i=0; i<NPROGRAMS; i++)
	{
		if (HasOpenGL2)
		{
			programs[i] = glCreateProgram();
			glAttachShader(programs[i], vshaders[i]);
			glAttachShader(programs[i], pshaders[i]);
			glLinkProgram(programs[i]);
			printProgramInfoLog(programs[i]);
		}
		else
		{
			programsARB[i] = glCreateProgramObjectARB();
			glAttachObjectARB(programsARB[i], vshadersARB[i]);
			glAttachObjectARB(programsARB[i], pshadersARB[i]);
			glLinkProgramARB(programsARB[i]);
			printInfoLogARB(programsARB[i]);
		}
	}
}
