
/*
   This is a sample main program for use with the object-oriented
   graphics system defined in Models.h/Models.cc.  In this version,
   there is a single world and a single view of the world.
   The user can rotate the world about the x and y axes by hitting
   the left and right arrow keys and Home key.  Hitting the "A" key
   will start an animation that makes the entire world tumble.  The 
   sample world consists of a teapot and a light.  Note that the light
   is subject to the world rotation.
*/

#include "Models.h"
#include <GL/glut.h>
#include <iostream>
using namespace std;


Model *world;     // Contains the entire 3D world.
View *worldView;  // Our view of the world.

Rotate *xWorldRotate;  // x-rotation applied to the world as a whole.
Rotate *yWorldRotate;  // y-rotation applied to the world as a whole.

bool animating = false;  // Is an animation in progress?
const int milliseconds_per_frame = 33;  // Time between animation frames.


/**
 * The createWorld() function is called before the OpenGL window is 
 * displayed.  This is where the whole 3D world is set up.  Here,
 * the world contains nothing but a teapot and a light.
 */
void createWorld() {

   world = new Model();
   
   world->addObject( (new BasicObject(TEAPOT))->setColor(.8,.8,1) );
   world->addObject( new Light(2,3,5) );

   xWorldRotate = new Rotate(0,1,0,0);
   yWorldRotate = new Rotate(0,0,1,0);
   world->addTransform(xWorldRotate)
        ->addTransform(yWorldRotate);

   worldView = new View(world);
   worldView->setViewEye(0,0,5);
   worldView->setViewVolume(-1.5,1.5,-1.5,1.5,-1.5,1.5);

}


/**
 *  This will be called over and over to drive the animation.
 *  Each time it is called, it updates any animation parameters
 *  to get ready for the next frame and posts a redisplay
 *  message.
 */
void timer(int id) {
   xWorldRotate->setAngle(xWorldRotate->getAngle() + 7);
   yWorldRotate->setAngle(yWorldRotate->getAngle() + 11);
   glutPostRedisplay();
}


/**
 *  The standard OpenGL display function.
 */
void display() { 
   if (animating)
      glutTimerFunc(milliseconds_per_frame,timer,1);  
   glClearColor(0,0,0,1);  // background color is black
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   
   worldView->render();
   glFlush();
   glutSwapBuffers();  // Makes the drawing appear on the screen!
}


/**
 *  The standard OpenGL display function.
 */
void reshape(int width, int height) { 
   glViewport(0,0,width,height);
   if (!animating)
      glutPostRedisplay();
}


/**
 *  The standard OpenGL keyboard function, for ordinary key presses.
 */
void keyboard(unsigned char key, int x, int y) {
   if (key == 27)
      exit(0);
   else if (key == 'a' || key == 'A') {
      animating = !animating;
      glutPostRedisplay();
   }
}


/**
 *  The standard OpenGL function for special key presses.
 */
void special(int key, int x, int y) {
   if (key == GLUT_KEY_HOME) {
      xWorldRotate->setAngle(0);
      yWorldRotate->setAngle(0);
   }
   else if (key == GLUT_KEY_UP) {
      xWorldRotate->setAngle(xWorldRotate->getAngle()-10);
   }
   else if (key == GLUT_KEY_DOWN) {
      xWorldRotate->setAngle(xWorldRotate->getAngle()+10);
   }
   else if (key == GLUT_KEY_RIGHT) {
      yWorldRotate->setAngle(yWorldRotate->getAngle()+10);
   }
   else if (key == GLUT_KEY_LEFT) {
      yWorldRotate->setAngle(yWorldRotate->getAngle()-10);
   }
   glutPostRedisplay();
}


int main(int argc, char **argv) {

   glutInit(&argc,argv);
   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
   glutInitWindowSize(500,500);       // Set window size here.
   glutInitWindowPosition(150,50);    // Set position of upper left corner.
   glutCreateWindow("OpenGL Models"); // Set window title.
   
   glutDisplayFunc(display);
   glutReshapeFunc(reshape);

   glutKeyboardFunc(keyboard); 
   glutSpecialFunc(special);   
   
   createWorld();
   glutMainLoop();
}
