/*
  Simple Demo for GLSL
  www.lighthouse3d.com
  
  tweaked a bit by andy
*/

#include "GL/glew.h"

#ifdef __linux__
#include <GL/glut.h>
#endif

#ifdef __APPLE__
#include <glut.h>
#endif

#ifdef _WIN32
#include "glut.h"
#endif

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


char tex0Data[800000];
char tex1Data[800000];
char tex2Data[800000];

GLuint texNumOn, texNumOff, texNumBack;

////////////////////////////////////////////////////////////////////////////////

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);
		}
	}
	
	if (content == NULL)
	   {
	   fprintf(stderr, "ERROR: could not load in file %s\n", fn);
	   exit(1);
	   }
	return content;
}           

////////////////////////////////////////////////////////////////////////////////

GLuint p;

float lpos[4] = {1.0, 0.5, 1.0, 0.0};

void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if(h == 0)
		h = 1;

	float ratio = 1.0* w / h;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	// Set the viewport to be the entire window
    glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45, ratio, 1, 1000);
	glMatrixMode(GL_MODELVIEW);
}

////////////////////////////////////////////////////////////////////////////////

void printShaderLog(GLuint prog)
{
    GLint infoLogLength = 0;
    GLsizei charsWritten  = 0;
    GLchar *infoLog;

    glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &infoLogLength);

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

void printProgramLog(GLuint shad)
{
    GLint infoLogLength = 0;
    GLsizei charsWritten  = 0;
    GLchar *infoLog;

    glGetProgramiv(shad, GL_INFO_LOG_LENGTH, &infoLogLength);

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

////////////////////////////////////////////////////////////////////////////////

float rotAmount = 0;

void renderScene(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	gluLookAt(0.0,0.0,6.0, 
		      0.0,0.0,-1.0,
			  0.0f,1.0f,0.0f);

	glLightfv(GL_LIGHT0, GL_POSITION, lpos);
    glRotatef(rotAmount,0,1,0);

    glUseProgram(p);
   // printProgramLog(p); 
    	
    GLint tex0Loc, tex1Loc, tex2Loc, alphaLoc;
    static float blender = 0.0;

   // note
    // You can't send a GL texture ID as your sampler.
    // A sampler should be from 0 to the max number of texture image units.
    // GL texture IDs are not necessarily 0 to n

    tex0Loc = glGetUniformLocation(p, "tex0");
    glUniform1i(tex0Loc, 0); // should match GL_TEXTURE0 above
    
    tex1Loc = glGetUniformLocation(p, "tex1");
    glUniform1i(tex1Loc, 1); // should match GL_TEXTURE1 above

    tex2Loc = glGetUniformLocation(p, "tex2");
    glUniform1i(tex2Loc, 2); // should match GL_TEXTURE2 above
    
    alphaLoc = glGetUniformLocation(p, "Alpha");
    glUniform1f(alphaLoc, blender);
    
    blender += 0.001;
    
    glColor3f(1.0, 1.0, 1.0);
    glBegin( GL_POLYGON );
        glTexCoord2f( 0.0f, 1.0f );
        glVertex3f( -2.0f, -2.0f, 0.0f );
        
        glTexCoord2f( 1.0f, 1.0f ); 

        glVertex3f(  2.0f, -2.0f, 0.0f );
        
        glTexCoord2f( 1.0f, 0.0f );

        glVertex3f(  2.0f,  2.0f, 0.0f );
        
        glTexCoord2f( 0.0f, 0.0f ); 
        glVertex3f( -2.0f,  2.0f, 0.0f );
   glEnd();

	glutSwapBuffers();
}

////////////////////////////////////////////////////////////////////////////////

void processNormalKeys(unsigned char key, int x, int y) {

	if (key == 27) 
		exit(0);
}

////////////////////////////////////////////////////////////////////////////////

GLuint setShaders(char * vert, char * frag) {
    GLuint v,f, pro;
	char *vs,*fs;

	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead(vert);
	fs = textFileRead(frag);

	const char * vv = vs;
	const char * ff = fs;

	glShaderSource(v, 1, &vv,NULL);
	glShaderSource(f, 1, &ff,NULL);

	free(vs);free(fs);

	printf("compiling vertex\n");
	glCompileShader(v);
	printShaderLog(v);


	printf("compiling fragment\n");
	glCompileShader(f);
	printShaderLog(f);

	pro = glCreateProgram();
	glAttachShader(pro,v);
	glAttachShader(pro,f);

	glLinkProgram(pro);
	printf("linking program\n");

	printProgramLog(pro);
	
	return(pro);
}

////////////////////////////////////////////////////////////////////////////////

GLuint setupTexture(char * texData)
{
	GLuint textureNum[1];

	glGenTextures(1, textureNum);

    glBindTexture(GL_TEXTURE_2D, textureNum[0]);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB,
        GL_UNSIGNED_BYTE, (const GLvoid *) texData);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    return(textureNum[0]);
}

////////////////////////////////////////////////////////////////////////////////

void doTexStuff(char * dataPath, char * fileName, int width, int height,
		char * TheArray)
    {
    char fullPath[256];
    FILE * imageFile;

    strcpy(fullPath, dataPath);
    strcat(fullPath, fileName);
    imageFile = fopen(fullPath, "r");
	
    if (imageFile == NULL) 
	   fprintf(stderr,"Cannot find texture file in data directory");
    else
        {
        fread(TheArray, sizeof(char), height*width*3, imageFile);
        fclose(imageFile);
        }
    }

////////////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(320,320);
	glutCreateWindow("GLSL demo");

	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutKeyboardFunc(processNormalKeys);

	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0, 0.0, 0.0, 1.0);

    doTexStuff((char *) "./", (char *) "joes_red.raw", 512, 512, tex0Data);
    texNumOn = setupTexture(tex0Data);
    
    doTexStuff((char *)  "./", (char *)  "joes_grey.raw", 512, 512, tex1Data);
    texNumOff = setupTexture(tex1Data);

    doTexStuff((char *)  "./", (char *)  "environmentmap.raw", 512, 512, tex2Data);
    texNumBack = setupTexture(tex2Data);



	glewInit();
	if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
		printf("Ready for GLSL\n");
	else {
		printf("No GLSL support\n");
		exit(1);
	}

	p = setShaders((char *)  "./joe.vert", (char *)  "./joe.frag");

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texNumOn);
    
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texNumOff);
    
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, texNumBack);

	
	glutMainLoop();
	return 0;
}

