#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include <sys/times.h>

// Arrayen data är här given som en hårdkodad array.
// Du gör inte nödvändigtvis sortering men min/max-extraktion,
// första och sista elementet skall vara max och min

void CHECK_FRAMEBUFFER_STATUS()
{
	GLenum status;
	status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	printf("Framebuffer status %d\n", status);
	switch(status)
	{
		case GL_FRAMEBUFFER_COMPLETE_EXT:
			printf("Framebuffer complete\n");
			break;
		case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
			printf("Framebuffer unsuported\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
			printf("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
			printf("GL_FRAMEBUFFER_MISSING_ATTACHMENT_EXT\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
			printf("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
			printf("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
			printf("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n");
			break;
		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
			printf("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n");
			break;
		default:
			printf("Framebuffer error\n");
	}
}

// Givet pekare till källkoden (array av strängar), returnerar shaderprogramobjektet
// Hanterar inte argument till shaders, måste göras separat
GLhandleARB setupShader(const GLcharARB **vertSrc, const GLcharARB **fragSrc)
{
	GLhandleARB	programObject;	// the program used to update
	GLhandleARB	fragmentShader, vertexShader;
	
	programObject = glCreateProgramObjectARB();

	if (fragSrc != NULL)
	{	
		fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
		glShaderSourceARB(fragmentShader, 1, fragSrc, NULL);
		glCompileShaderARB(fragmentShader);
		glAttachObjectARB(programObject, fragmentShader);
	}

	if (vertSrc != NULL)
	{	
		vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
		glShaderSourceARB(vertexShader, 1, vertSrc, NULL);
		glCompileShaderARB(vertexShader);
		glAttachObjectARB(programObject, vertexShader);
	}

// Link the shader into a complete GLSL program.

	glLinkProgramARB(programObject);
	GLint progLinkSuccess;
	glGetObjectParameterivARB(programObject, GL_OBJECT_LINK_STATUS_ARB,
		&progLinkSuccess);
	if (!progLinkSuccess)
	{
		fprintf(stderr, "Shader could not be linked\n");
		exit(1);
		// Borde skicka ut hela infolog-felmeddelandet.
	}
	return programObject;
}


long GetTickCount()
{
	struct tms tm;
	return times(&tm); // 100 tick per sekund
//	return times(&tms);
}


// En enkel shader
// Ersätt med en eller flera egna
static const char *fragSource =
{
"uniform sampler2D texUnit;"
"void main(void)"
"{"
"   vec4 texVal  = texture2D(texUnit, gl_TexCoord[0].xy);"
"   gl_FragColor = sqrt(texVal);"
"}"
};

float data[64] =
{
	24,2,3,4,5,64,7,8,9,10,11,12,13,14,15,16,
	17,18,19,20,21,22,23,1,25,53,27,28,29,30,31,32,
	33,34,35,36,37,38,39,40,41,42,46,44,45,43,47,48,
	49,50,51,52,26,54,55,56,57,58,59,60,61,62,63,6
};
// => texSize måste vara 4!


int main(int argc, char **argv)
{
    // declare texture size, the actual data will be a vector 
    // of size texSize*texSize*4
    int texSize = 4;
	int i;
    // create test data
    float* result = (float*)malloc(4*texSize*texSize*sizeof(float));
    // set up glut to get valid GL context and 
    // get extension entry points
    glutInit (&argc, argv);
    glutCreateWindow("TEST1");
    glewInit();
    // viewport transform for 1:1 pixel=texel=data mapping
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0,texSize,0.0,texSize);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0,0,texSize,texSize);
    // create FBO and bind it (that is, use offscreen render target)
    GLuint fb;
    glGenFramebuffersEXT(1,&fb); 
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);
    // create texture
    GLuint tex;
    glGenTextures (1, &tex);
    glBindTexture(GL_TEXTURE_2D,tex);
    // set texture parameters
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    // define texture with floating point format
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA32F_ARB,
                 texSize,texSize,0,GL_RGBA,GL_FLOAT, data);

    // create destination texture
    GLuint desttex;
    glGenTextures (1, &desttex);
    glBindTexture(GL_TEXTURE_2D,desttex);
    // set texture parameters
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    // define texture with floating point format
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA32F_ARB,
                 texSize,texSize,0,GL_RGBA,GL_FLOAT, NULL);

CHECK_FRAMEBUFFER_STATUS();

    // attach texture
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 
                              GL_COLOR_ATTACHMENT0_EXT, 
                              GL_TEXTURE_2D,desttex,0);

CHECK_FRAMEBUFFER_STATUS();

	GLhandleARB shader;
// Skapa shader
	shader = setupShader(NULL, &fragSource);
// Hantera shadervariabler
	glUseProgramObjectARB(shader);
	GLint loc = glGetUniformLocation(shader, "texUnit");
	glUniform1f(loc, 0); // texture unit 0
	glUseProgramObjectARB(0);

// draw

		glUseProgramObjectARB(shader);
		glEnable(GL_TEXTURE_2D);
	    glBindTexture(GL_TEXTURE_2D,tex);
		glBegin(GL_QUADS);
		glTexCoord2f(0, 0);
		glVertex2f(0, 0);
		glTexCoord2f(1, 0);
		glVertex2f(texSize, 0);
		glTexCoord2f(1, 1);
		glVertex2f(texSize, texSize);
		glTexCoord2f(0, 1);
		glVertex2f(0, texSize);
		glEnd();
		glFlush();
	

    // and read back
    glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
    glReadPixels(0, 0, texSize, texSize,GL_RGBA,GL_FLOAT,result);
    // print out results
    printf("Data före processning:\n");
    for (i=0; i<texSize*texSize*4; i++)
        printf("%f\n",data[i]);
    printf("Data efter processning:\n");
    for (i=0; i<texSize*texSize*4; i++)
        printf("%f\n",result[i]);

    // clean up
    free(result);

    glDeleteFramebuffersEXT (1,&fb);
    glDeleteTextures (1,&tex);
    return 0;
}

// gcc fix-array.c -o fix-array -lGLEW -lglut -lGL
