
#include <GL/glew.h>
#include <stdio.h>

// All headers are automatically included from "iglu.h"
#include "iglu.h"

// IGLU classes and constants are all inside the iglu namespace.
using namespace iglu;

// This is a pointer to a IGLUTexture!  Declare pointers with the syntax
//    for "IGLUClass::Ptr myPtr;" rather than "IGLUClass *myPtr;" for ALL
//    IGLU classes!
IGLUTexture::Ptr       tex2D = 0;
IGLUShaderProgram::Ptr shader = 0;
IGLUBuffer::Ptr        uniformBuf = 0;

IGLUVertexArray::Ptr   vao = 0;

// The location & tex coords for out screen quad
static float fullScreenQuadData[] = {
	0, 0, -1, -1, 0,   // Lower-left  vertex, (u,v) = (0,0)
	1, 0, 1, -1, 0,    // Lower-right vertex, (u,v) = (1,0)
	0, 1, -1, 1, 0,    // Upper-left  vertex, (u,v) = (0,1)
	1, 1, 1, 1, 0,     // Upper-right vertex, (u,v) = (1,1)
};

// The locations to bind vertex & tex coords to in our shader
int vertexIdx, texCoordIdx, uniformBlkIdx; 

void display ( void )
{
	shader->Enable();
	    glBindBufferBase( GL_UNIFORM_BUFFER, uniformBlkIdx, uniformBuf->GetBufferID() );
		vao->DrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
	shader->Disable();

	// Swap OpenGL front/back buffers to display what we just drew.
	glutSwapBuffers();
}


void keys(unsigned char key, int x, int y)
{
	// Exit whenever any key is pressed
	exit(-1);
}

void idle ( void )
{
	tex2D->Update();
	glutPostRedisplay();
}

void InitializeUniformBuffer( void )
{
	uniformBuf = new IGLUBuffer( IGLU_UNIFORM );

	// Find the bind point for this uniform
	uniformBlkIdx = glGetUniformBlockIndex( shader->GetProgramID(), "ColorDisk" );

	// Find the size of the uniform buffer, and allocate space.
	GLint blockSize;
	glGetActiveUniformBlockiv(shader->GetProgramID(), uniformBlkIdx,
                      GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
	GLubyte *blockData = (GLubyte *)malloc(blockSize);

	// Query for the locations of the variables in the buffer
	const GLchar *varNames[] = { "diskColor", 
							     "innerRadius", "outerRadius" };
	GLuint varIndices[3];
	glGetUniformIndices(shader->GetProgramID(), 3, varNames, varIndices);
	GLint varOffset[3];
	glGetActiveUniformsiv(shader->GetProgramID(), 3, varIndices,
						  GL_UNIFORM_OFFSET, varOffset);

    // Plop data into the buffer
	GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f };
	GLfloat innerRadius = 0.0f, outerRadius = 2.5f;
	memcpy( blockData + varOffset[0], colors, 8 * sizeof( float ) );
	memcpy( blockData + varOffset[1], &innerRadius, 1 * sizeof( float ) );
	memcpy( blockData + varOffset[2], &outerRadius, 1 * sizeof( float ) );

	// Copy the data into the buffer
	uniformBuf->SetBufferData( blockSize, blockData, iglu::IGLU_STATIC | iglu::IGLU_DRAW );
	free(blockData);
}

int main(int argc, char** argv)
{
	// Standard OpenGL/GLUT setup
	glutInit(&argc, argv);
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
	glutInitWindowSize( 512, 512 );
	glutInitWindowPosition( 20, 20 );
	glutCreateWindow( "Basic IGLU Texture Loader" );
	glewInit();
	glutDisplayFunc( display );
	glutKeyboardFunc( keys );
	glutIdleFunc( idle );

	// Load the texture with IGLU
	printf("(+) Loading texture...\n" );
	tex2D = new IGLUTexture2D( "test.ppm" );

	// Load our shader
	shader = new IGLUShaderProgram( "test.vert.glsl", "test.frag.glsl" );
	shader["copyTex"] = tex2D;

	// Create our vertex array object
	vao = new IGLUVertexArray();
	vao->SetVertexArray( 20 * sizeof( float ), fullScreenQuadData );
	vao->EnableAttribute( shader[ "vertex" ], 3, GL_FLOAT, 5*sizeof(float), BUFFER_OFFSET(2*sizeof(float)) );
	vao->EnableAttribute( shader["texcoord"], 2, GL_FLOAT, 5*sizeof(float), BUFFER_OFFSET(0) );

	// Create our uniform buffer object
	InitializeUniformBuffer();

	// Start the GLUT interaction loop
	glutMainLoop();
	return 0;
}
