/**************************************************
 *   File:     TwoSidedMaterial.cpp
 *   Author:   Dr. Dalton R. Hunkins     
 *   Date:     January 2009
 *
 *  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.
 *
 *    Special Keys
 *    Up Arrow   - increase near plane distance
 *    Down Arrow - decrease near plane distance
**************************************************/

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

#include <GL/utils/ImageMap.h>

double nearPlane;

string imageMapName = "hummer.bmp";
string imageMapPath;

string parentDirectory = "OpenGLTutorials";
string imageMapPath;

void setPath(string pathName) {
     if (pathName[0] == '/') {
         pathName = pathName.substr(10, pathName.length());
         imageMapPath = pathName.substr(0,1) + ":/";
	     pathName = pathName.substr(2, pathName.length());
     }   
     int position = pathName.find(parentDirectory, 0);
     pathName = pathName.substr(0, position+parentDirectory.length());
     imageMapPath += pathName + "/Resources/ImageMaps/";
}

void init() {
   glClearColor(0.0, 0.0, 0.0, 1.0);
   
   nearPlane = 1.4;

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(0.0, 1.0, 3.0,
             0.0, 0.0, 0.0,
             0.0, 1.0, 0.0);

   // Set Ambient Light
   float ambientColor[] = {0.3, 0.3, 0.3};
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

   // Set Directional Light
   float directionalPosition[] = { 3.0, 1.0, 3.0, 0.0};
   float directionalColor[] = {1.0, 1.0, 1.0};
   glLightfv(GL_LIGHT1, GL_DIFFUSE,  directionalColor);
   glLightfv(GL_LIGHT1, GL_POSITION, directionalPosition);

   // Set Spot Light   
   float spotPosition[]        = { 2.0,   2.0,  0.0, 1.0};
   float spotDirection[]       = {-2.0,  -2.0,  2.0, 1.0};
   float spotColor[]        = {1.0, 1.0, 1.0};
   glLightfv(GL_LIGHT2, GL_POSITION, spotPosition);
   glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, spotDirection);
   glLightf (GL_LIGHT2, GL_SPOT_CUTOFF, 40.0);
   glLightfv(GL_LIGHT2, GL_DIFFUSE, spotColor);
   
   // Establish Image Map   
   ImageMap *scene = new ImageMap(imageMapPath, imageMapName);  
   
   // Setup Texture Object
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   scene->setTexture();
     
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_LIGHTING);   
}

void displaySphere() {
    float outsideColor[]     = {0.8, 0.6, 0.3};
    float insideColor[]      = {0.5, 0.8, 0.8};
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, outsideColor);
    glMaterialfv(GL_BACK,  GL_AMBIENT_AND_DIFFUSE, insideColor);     
      
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glScaled(1.5, 1.5, 1.5);
    glutSolidSphere(1.0, 60, 60);
    glPopMatrix();     
}     

void displayCube() {
     float cubeColor[]        = {1.0, 1.0, 1.0};
     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cubeColor);
     
     glMatrixMode(GL_MODELVIEW);
     glPushMatrix();
     glScalef(0.7, 0.7, 0.7);
     glRotatef(-45.0, 0.0, 1.0,  0.0);
     glRotatef( 15.0, 1.0, 0.0, -1.0);
     glBegin(GL_QUADS);
        // Front Side
        glNormal3d(0.0, 0.0, 1.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(0.0, 0.0, 1.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(1.0, 0.0, 1.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(1.0, 1.0, 1.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(0.0, 1.0, 1.0);

        // Top
        glNormal3d(0.0, 1.0, 0.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(0.0, 1.0, 1.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(1.0, 1.0, 1.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(1.0, 1.0, 0.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(0.0, 1.0, 0.0);
        
        // Right Side
        glNormal3d(1.0, 0.0, 0.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(1.0, 0.0, 1.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(1.0, 0.0, 0.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(1.0, 1.0, 0.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(1.0, 1.0, 1.0);

        // Back Side
        glNormal3d(0.0, 0.0, 1.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(0.0, 0.0, 0.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(1.0, 0.0, 0.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(1.0, 1.0, 0.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(0.0, 1.0, 0.0);

        // Bottom
        glNormal3d(0.0, -1.0, 0.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(0.0, 0.0, 1.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(1.0, 0.0, 1.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(1.0, 0.0, 0.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(0.0, 0.0, 0.0);
        
        // Left Side
        glNormal3d(-1.0, 0.0, 0.0);
        glTexCoord2d(0.0, 0.0);
        glVertex3d(0.0, 0.0, 1.0);
        glTexCoord2d(1.0, 0.0);
        glVertex3d(0.0, 0.0, 0.0);
        glTexCoord2d(1.0, 1.0);
        glVertex3d(0.0, 1.0, 0.0);
        glTexCoord2d(0.0, 1.0);
        glVertex3d(0.0, 1.0, 1.0);
     glEnd();

     glPopMatrix();
}     

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

    // Set Projection Method
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0, nearPlane, 100.0);

    // Call Sphere
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glEnable(GL_LIGHT1);
    displaySphere();    
    glDisable(GL_LIGHT1);
    
    // Call Cube
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHT2);
    displayCube();
    glDisable(GL_LIGHT2);
    glDisable(GL_TEXTURE_2D);
            
    glFlush();
}

void specialHandler(int key, int x, int y) {
     switch (key) {
        case GLUT_KEY_UP :
            nearPlane += 0.1;
            break;
        case GLUT_KEY_DOWN :
            nearPlane -= 0.1;
            break;
     }
     if (nearPlane < 1.0)
        nearPlane = 1.0;
        
     glutPostRedisplay();                        
}          

int main (int argc, char** argv) {
    string pathName = argv[0];
    setPath(pathName);

    glutInit(&argc, argv);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Two Sided Material");
    glutInitDisplayMode(GLUT_RGB);

    glutDisplayFunc(display);
    glutSpecialFunc(specialHandler);
    
    init();

    glutMainLoop();
    return 0;
}