/**************************************************
 *   File:     LightsCamera.cpp
 *   Author:   Dr. Dalton R. Hunkins     
 *   Date:     December 2008
 *
 *  Modes of User Interaction
 *    Although the standard popup
 *    menu to show code and to exit
 *    are provided in this example,
 *    its code is not included here.
 *
 *    Keyboard
 *    Three character keys are used as
 *    "toggle switches." They are:
 *    o, O - enable, disable Omni light
 *    d, D - enable, disable Directional light
 *    s, S - enable, disable Spot light
 *
 *    Special Keys
 *    Up Arrow   - increase shininess
 *    Down Arrow - decrease shininess
**************************************************/

#include <GL/Gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

float white[]  = {1.0, 1.0, 1.0};
float yellow[] = {1.0, 1.0, 0.0};
float cyan[]   = {0.0, 1.0, 1.0};

float shininess;
bool  enableOmni;
bool  enableDirectional;
bool  enableSpot;

void setLighting( ) {
     // Ambient Light
     float ambientColor[4]    = {0.2, 0.2, 0.2, 1.0};
     glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
    
     // Omni Light
     float omniColor[4]       = { 0.5, 1.0, 0.5, 1.0};
     float omniCoordinates[4] = {-1.0, 1.0, 1.0, 1.0};
     glLightfv(GL_LIGHT1, GL_DIFFUSE,  omniColor);
     glLightfv(GL_LIGHT1, GL_POSITION, omniCoordinates);
     
     // Directional Light
     float directionalColor[4]       = {0.6, 0.6, 0.6, 1.0};
     float directionalCoordinates[4] = {5.0, 1.0, 1.0, 0.0};
     glLightfv(GL_LIGHT2, GL_DIFFUSE,  directionalColor);
     glLightfv(GL_LIGHT2, GL_POSITION, directionalCoordinates);
         
     // Spot Light
     float specularcolor[4]  = {1.0,   0.0,  1.0, 1.0};
     float spotDirection[4]  = {0.0,  -0.1, -5.0, 1.0};
     float positioncoords[4] = {0.25,  0.1,  5.0, 1.0};
     glLightfv(GL_LIGHT3, GL_SPOT_DIRECTION, spotDirection);
     glLightf (GL_LIGHT3, GL_SPOT_CUTOFF, 1.0);
     glLightfv(GL_LIGHT3, GL_SPECULAR, specularcolor);
     glLightfv(GL_LIGHT3, GL_POSITION, positioncoords);
}

void init() { 
   glClearColor(0.0, 0.0, 0.0, 1.0);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(0.0, 0.75, 3.0, 
             0.0, 0.0, 0.0,
             0.0, 1.0, 0.0);
                 
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(30.0, 1.0, 0.1, 10.0);  
   
   // Set Lighting
   setLighting();            
   glEnable(GL_LIGHTING);
   glEnable(GL_DEPTH_TEST);
   
   // Set Variables
   shininess = 0.0;
   enableOmni        = false;
   enableDirectional = false;
   enableSpot        = false;
}

void displayCube() {
     // Define Cube Material
     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cyan);

     // Construct and Place Cube
     glMatrixMode(GL_MODELVIEW);
     glPushMatrix();
     glTranslated(0, .25, 0);
     glRotated(45.0, 1.0, 0.0,  1.0);
     glScaled(0.3, 0.3, 0.3);
     
     glBegin(GL_QUADS);
        glNormal3f(0.0,0.0,1.0);
        glVertex3d(0.0,0.0,1.0);
        glVertex3d(1.0,0.0,1.0);
        glVertex3d(1.0,1.0,1.0);
        glVertex3d(0.0,1.0,1.0);

        glNormal3f(0.0,1.0,0.0);
        glVertex3d(0.0,1.0,0.0);
        glVertex3d(1.0,1.0,0.0);
        glVertex3d(1.0,1.0,1.0);
        glVertex3d(0.0,1.0,1.0);

        glNormal3f(1.0,0.0,0.0);
        glVertex3d(1.0,0.0,0.0);
        glVertex3d(1.0,1.0,0.0);
        glVertex3d(1.0,1.0,1.0);
        glVertex3d(1.0,0.0,1.0);
     glEnd();     

     glPopMatrix();
}

void displayTeapot() { 
     // Define Teapot Material
     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow);
     glMaterialf(GL_FRONT, GL_SHININESS, shininess);
     glMaterialfv(GL_FRONT, GL_SPECULAR, white);

     // Construct and Place Teapot
     glMatrixMode(GL_MODELVIEW);
     glPushMatrix();
     glTranslated(0.1, 0, 0.7);
     glRotated(20.0, 1.0, 0.0, 0.0);

     glutSolidTeapot(0.2);
    
     glPopMatrix();
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if (enableOmni)
       glEnable(GL_LIGHT1);
    else
       glDisable(GL_LIGHT1);
    if (enableDirectional)
       glEnable(GL_LIGHT2);
    else
       glDisable(GL_LIGHT2);
    if (enableSpot)              
       glEnable(GL_LIGHT3);
    else
       glDisable(GL_LIGHT3);
    
    displayCube();
    displayTeapot();
    
    glFlush();
}

void keyboardHandler(unsigned char key, int x, int y) {
     switch (key) {
        case 'o' :
        case 'O' :
             enableOmni = not enableOmni;
             break;
        case 'd' :
        case 'D' :
             enableDirectional = not enableDirectional;
             break;
        case 's' :
        case 'S' :
             enableSpot = not enableSpot;
             break;
     }
     glutPostRedisplay();                                    
}

void specialHandler(int key, int x, int y) {
     switch (key) {
        case GLUT_KEY_UP :
            shininess += 0.5;
            break;
        case GLUT_KEY_DOWN :
            shininess -= 0.5;
            break;
     }
     if (shininess < 0.0)
        shininess = 0.0;
     if (shininess > 128.0)
        shininess = 128.0;
        
     glutPostRedisplay();                        
}          

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Lights");
    glutInitDisplayMode(GLUT_RGB);

    glutDisplayFunc(display);
    glutKeyboardFunc(keyboardHandler);
    glutSpecialFunc(specialHandler);
    
    init();
   
    glutMainLoop();
    return 0;
}