/******************************************************************/
/* This version of the Cornell Box program is now much cleaner,   */
/*    as it loads directly from an OBJ & MTL file, instead of     */
/*    storing geometry inside the code file.  This significnatly  */
/*    reduces the amount of code require for more complex geom!   */
/*                                                                */
/* Chris Wyman (03/07/2012)                                       */
/******************************************************************/

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

// Include IGLU and set it up to avoid the iglu:: prefix on types
#include "iglu.h"
using namespace iglu;

// IGLU shaders this program uses
IGLUShaderProgram::Ptr  shader        = 0;      // The shader we use to draw the Cornell Box geometry
IGLUShaderProgram::Ptr  toneMap       = 0;      // The shader used to tone & gamma correct the scene.

// Our geometry!  
IGLUOBJReader::Ptr      cboxReader    = 0;

// Stores intermediate framebuffer.
IGLUFramebuffer::Ptr    tempFBO       = 0;

// Setup 3D projection and default viewing matrix
IGLUMatrix4x4           proj = IGLUMatrix4x4::Perspective( 38.5, 1.0, 5, 20 );
IGLUMatrix4x4           view = IGLUMatrix4x4::LookAt( vec3(2.78, 2.73, -8.00), 
													  vec3(2.78, 2.73, 2.795), 
													  vec3(0.0, 1.0, 0.0) );

// IGLU variables that can be changed via a FLTK-window user-interface.
IGLUBool                displayFPS ( true,                                  "Display framerate" );
IGLUFloat               lightIntensity( 1.0, IGLURange<float>(0.1,10), 0.1, "Light intensity:" );
IGLUBool                useToneMap ( false,                                 "Apply tone mapping?" );
IGLUFloat               gammaValue ( 2.2, IGLURange<float>(1.0,3.0), 0.05,  "Image gamma:" );
IGLUBool                useGamma   ( false,                                 "Apply gamma correction?" );

// These are handles to the two windows, should you need to modify their
//   behavior after window creation.
IGLUWindow::Ptr         myWin = 0;
IGLUWidgetWindow::Ptr   uiWin = 0;


// The display callback that is invoked by our IGLUWindow with the screen is redrawn
void display ( void )
{
	// Clear the screen
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// Draw our geometry with an appropriate shader into the temporary buffer
	tempFBO->Bind();
	tempFBO->Clear();
		shader->Enable();
		shader["lightIntensity"] = float(lightIntensity);
		shader["model"]          = IGLUMatrix4x4::Identity();
			cboxReader->Draw( shader );
		shader->Disable();
	tempFBO->Unbind();

	// Now draw our buffer onto the screen, with tonemapping & gamma correction
	toneMap["useToneMap"] = bool(useToneMap) ? 1.0f : 0.0f;
	toneMap["useGamma"]   = bool(useGamma) ? 1.0f : 0.0f;
	toneMap["gamma"]      = float(gammaValue);
	IGLUDraw::Fullscreen( toneMap, tempFBO[0] );

	// Draw text with some instructions
	IGLUDraw::DrawText( IGLU_FONT_VARIABLE, 5, 738, 
		"Adds material properties (think OBJ .mtl files) to the Cornell Box....  Click (+) for options.", 0.8 );
}


// Code that initializes our OpenGL state.  This is guaranteed (by IGLUWindow) to be 
//    called after the OpenGL context & all extensions have been initialized
void OpenGLInitialization( void )
{            
	// Set up a temporary float buffer to store our non-tone mapped rendering
	tempFBO = IGLUFramebuffer::Create( GL_RGBA16F, 768, 768, true );

	// Set up our Cornell Box geometry.
	cboxReader = new IGLUOBJReader( "model/testCornellBox.obj" );

	// We've loaded all our materials, so prepare to use them in rendering
	IGLUOBJMaterialReader::FinalizeMaterialsForRendering();
	
	// The shader that does tone mapping & gamma correction
	toneMap = new IGLUShaderProgram( "shaders/toneMap.vert.glsl", "shaders/toneMap.frag.glsl" );
	toneMap->SetProgramDisables( IGLU_GLSL_DEPTH_TEST | IGLU_GLSL_BLEND ); 

	// Shader that draws the Cornell Box
	shader = new IGLUShaderProgram( "shaders/test.vert.glsl", "shaders/test.frag.glsl" );
	shader->SetProgramEnables( IGLU_GLSL_DEPTH_TEST ); 
	shader->SetProgramDisables( IGLU_GLSL_BLEND ); 
	shader["project"]         = proj;
	shader["model"]           = IGLUMatrix4x4::Identity();
	shader["view"]            = view;
	shader["lightIntensity" ] = float(lightIntensity);
	shader["matlInfoTex"]     = IGLUOBJMaterialReader::s_matlCoefBuf;
	shader["matlTextures"]    = IGLUOBJMaterialReader::s_matlTexArray;
}

void ToggleFramerate( void *uiBool )
{
	myWin->DisplayFramerate( ((IGLUBool *)uiBool)->GetValue() );
}

// Our main entry point
int main(int argc, char** argv)
{
	// Put together our main window
	myWin = new IGLUWindow( 768, 768, "Simple IGLU Example:  Draw a Cornell Box" );
	myWin->SetWindowProperties( IGLU_WINDOW_NO_RESIZE |	
								IGLU_WINDOW_DOUBLE |
								IGLU_WINDOW_REDRAW_ON_IDLE |
								IGLU_WINDOW_W_FRAMERATE ); 
	myWin->SetDisplayCallback( display );  
	myWin->SetIdleCallback( IGLUWindow::NullIdle );
	myWin->SetPreprocessOnGLInit( OpenGLInitialization );
	myWin->CreateWindow( argc, argv );     

	// Create a widget window to allow us to interact/change our IGLUVariables
	uiWin = new IGLUWidgetWindow( 300, 220, "UI Widget Window" );
	uiWin->AddWidget( &lightIntensity );
	uiWin->AddWidget( &useToneMap );
	uiWin->AddWidgetSpacer();
	uiWin->AddWidget( &gammaValue );
	uiWin->AddWidget( &useGamma );
	uiWin->AddWidgetSpacer();
	uiWin->AddWidget( &displayFPS );

	// Tell our main window about the widget window... 
	myWin->SetWidgetWindow( uiWin );

	// Turning off the framerate display is more complex and actually
	//    needs a callback, so we know when the variable changes
	displayFPS.SetCallback( ToggleFramerate );

	// Start the FLTK interaction loop
	IGLUWindow::Run();
	return 0;
}
