// BRT.cpp : Defines the entry point for the console application.
//

/************************
*	Peter Lohrmann		*
*	July 18,2005		*
*	MS Thesis			*
*	Advised by Prof Agu	*
*	WPI					*
*	Raytracing			*
************************/

#ifdef MACOSX
#include <gl.h>
#include <glu.h>
#include <glut.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif

#include "Camera.h"
#include "Scene.h"

Camera cam;
Scene scn;
string scnName = "ball.dat";

static const int OPENGL = 0;
static const int RAYTRACE = 1;
int renderStyle = OPENGL;

//Point3 eye(0, 3, 8);
Point3 eye(7, 7, 10);
Point3 look(0, 0, 0);
Vector3 up(0, 1, 0);

// Viewing parameters.
float nPlane = 1;
float fPlane = 400;
float vAngle = 90;
float wAspect = 4/3;

void keyCommands( void );

//int blockSize = 1;

/************
*	INIT	*
************/
void myInit(void){
	glClearColor(1.0, 1.0, 1.0, 0.0);	// white background
	glColor3f(0.9f, 0.1f, 0.1f);		// object color
	glPointSize(4.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	cam.setShape(vAngle, wAspect, nPlane, fPlane);
	cam.set(eye, look, up);

        scn.read(scnName);
}

/****************
*	myDisplay	*
****************/
void myDisplay(void){

	if (renderStyle == OPENGL){
		glEnable(GL_LIGHTING);
		scn.makeLightsOpenGL();

		glClear(GL_COLOR_BUFFER_BIT);	//clear the screen
		glClear(GL_DEPTH_BUFFER_BIT);
		scn.drawSceneOpenGL();			// send output to display
	}
        else {
  	  if (renderStyle == RAYTRACE){
	        cam.raytrace(scn,scn.pixelSize);
                keyCommands( );
	  }
	}
        glutSwapBuffers( );
}

/****************
*	usage	*
****************/
void usage( int argc, char *argv[] )  {
  printf( "use: %s [options]*\n\n", &argv[0][0] );
  printf( "options:\n" );
  printf( "  -sSceneFile: Load \"SceneFile\" as the scene to display.\n" );
  printf( "  -?         : Print this help message.\n\n" );
}

/****************
*	keyCommands	*
****************/
void keyCommands( void )  {
  printf( "Keyboard Commands:\n" );
  printf( "  o: Move camera OUT.\n" );
  printf( "  u: Move camera IN.\n" );
  printf( "  i: Move camera UP.\n" );
  printf( "  k: Move camera DOWN.\n" );
  printf( "  l: Move camera RIGHT.\n" );
  printf( "  j: Move camera LEFT.\n\n" );

  printf( "  n: Yaw camera LEFT.\n" );
  printf( "  m: Yaw camera RIGHT.\n" );
  printf( "  .: Pitch camera UP.\n" );
  printf( "  ,: Pitch camera DOWN.\n\n" );

  printf( "  z: Render using OpenGL.\n" );
  printf( "  x: Render using Ray Tracing.\n\n" );

  printf( "  <: Decrease the pixel blockiness (for Ray Tracing).\n" );
  printf( "  >: Increase the pixel blockiness (for Ray Tracing).\n" );
  printf( "  r: Decrease ray recursion depth (for Ray Tracing).\n" );
  printf( "  R: Increase ray recursion depth (for Ray Tracing).\n\n" );

  printf( "  a: Set eye-point to (7, 0, 0).\n" );
  printf( "  s: Set eye-point to (0, 7, 0).\n" );
  printf( "  d: Set eye-point to (0, 0, 7).\n" );
  printf( "  f: Set eye-point to (3, 3, 3).\n\n" );

  printf( "ESC: Exit.\n" );
  printf( "  q: Exit.\n" );
}


void myKeyboard(unsigned char k, int x, int y)  {
  int redraw = 0; // FLAG: Should we redraw?

  switch( k )  {
    case 'o':	//
      cam.slide(0,0,1);
      redraw = 1;
      break;
    case 'u':	//
      cam.slide(0,0,-1);
      redraw = 1;
      break;
    case 'i':	//
      cam.slide(0,1,0);
      redraw = 1;
      break;
    case 'k': //
      cam.slide(0,-1,0);
      redraw = 1;
      break;
    case 'l':	//
      cam.slide(1,0,0);
      redraw = 1;
      break;
    case 'j': //
      cam.slide(-1,0,0);
      redraw = 1;
      break;
    case 'n':	//turn left
      cam.yaw(5);
      redraw = 1;
      break;
    case 'm':	//turn right
      cam.yaw(-5);
      redraw = 1;
      break;
/*
    case 'C':	//
      cam.roll(5);
      break;
    case 'Z': //
      cam.roll(-5);
      break;
*/
    case ',':	//turn up
      cam.pitch(5);
      redraw = 1;
      break;
    case '.':	//turn down
      cam.pitch(-5);
      redraw = 1;
      break;
     case 'a':
      eye.set(7.0,0.0,0.0);
      up.set(0,1,0);
      cam.set(eye, look, up);
      redraw = 1;
      break;
    case 's':
      eye.set(0.0,7.0,0.0);
      up.set(0,0,-1);
      cam.set(eye, look, up);
      redraw = 1;
      break;
    case 'd':
      eye.set(0.0,0.0,7.0);
      up.set(0,1,0);
      cam.set(eye, look, up);
      redraw = 1;
      break;
    case 'f':
      eye.set(3.0,3.0,3.0);
      up.set(0,1,0);
      cam.set(eye, look, up);
      redraw = 1;
      break;
    case 'z':
      if( renderStyle != OPENGL )  {
        renderStyle = OPENGL;
        // Reset us back to Perspective Projection.
        cam.setShape(vAngle, wAspect, nPlane, fPlane);
        cam.slide(0,0,0); // Hack to trigger resetting of the current view.
        redraw = 1;
      }
      break;
    case 'x':
      if( renderStyle != RAYTRACE )  {
        renderStyle = RAYTRACE;
        redraw = 1;
      }
      break;
    case 'q':
    case 27:  /* Escape */
      exit(0);
      break;
    case '<': // Decrease pixel size.
      if( scn.pixelSize > 1 )   {
        scn.pixelSize--;
        printf( "Pixel size now: %d\n", scn.pixelSize );
        redraw = 1;
      }
      break;
    case '>': // Increase pixel size.
      scn.pixelSize++;
      printf( "Pixel size now: %d\n", scn.pixelSize );
      redraw = 1;
      break;
    case 'r': // Decrease recusion depth.
      if( scn.maxRecursionDepth > 0 )   {
        scn.maxRecursionDepth--;
        printf( "Recursion nepth now: %d\n", scn.maxRecursionDepth );
        redraw = 1;
      }
      break;
    case 'R': // Increase recusion depth.
      scn.maxRecursionDepth++;
      printf( "Recursion nepth now: %d\n", scn.maxRecursionDepth );
      redraw = 1;
      break;
    default:
      return;
  }

  // update the screen
  if( redraw )  {
    glutPostRedisplay();
  }
}

/********************
*		MAIN	 	*
*	major control	*
*	of the program	*
********************/
int main(int argc, char** argv){

  // Process command-line parameters.
  for( int i = 1; i < argc; i++ )  {
    if( argv[i][0] == '-' )  {
      switch( argv[i][1] )  {
        case 's': // Use this file as the scene file.
          scnName = &argv[i][2];
          break;
        case '?': // Output usage message, and exit.
          usage( argc, argv );
          exit( 0 );
          break;
        default:
          break;
      }
    }
  }
  // init window
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(640, 480);
  glutInitWindowPosition(50,50);
  glutCreateWindow("Basic Ray Tracing");
  
  // callback functions
  glutDisplayFunc(myDisplay);
  //	glutReshapeFunc(myReshape);
  //	glutMouseFunc(myMouse);
  glutKeyboardFunc(myKeyboard);
  
  //other options
  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);
  
  // init other values
  myInit();

  keyCommands( );
  
  glutMainLoop();
  return 0;
}

