/**************************************************
 *   File:     CGWorld.cpp
 *   Author:   Dr. Dalton R. Hunkins     
 *   Date:     February 2009
**************************************************/
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include <cmath>

#include "CGWorld.h"
#include <GL/Utils/MathFunctions.h>

  CGWorld::CGWorld() {
  }
 
  
  void CGWorld::createSubWindow(int parentHandle,
                                 int parentWidth, int parentHeight,
                                 int startX,      int startY,
                                 int width,        int height) {
      windowHandle = glutCreateSubWindow(parentHandle,
                                         startX*parentWidth/100, startY*parentHeight/100,
                                         width*parentWidth/100,  height*parentHeight/100);
      CGWorld::cornerX = startX;
      CGWorld::cornerY = startY;
      CGWorld::width   = width;
      CGWorld::height  = height;                                                                              
  }

  void CGWorld::init(float clearColor[]) { 
     	glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0);
     
     	enableLighting = true;
     	textureChoice =  NONE;

	imageMap = NULL;
     
	lightColor[0] = lightColor[1] = lightColor[2] = 1.0;
	materialColor[0] = materialColor[1] = materialColor[2] = materialColor[3] = 1.0;
	 
     	rgbSquare[0].setRGB(0.5, 0.6, 0.7);
     	rgbSquare[1].setRGB(0.9, 0.8, 0.7);
     	rgbSquare[2].setRGB(0.7, 0.6, 0.5);
     
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 0.5, 3.0,    // eyePoint
                0.0, 0.0, 0.0,    // lookAtPoint
                0.0, 1.0, 0.0);   // upVector
               
     	glMatrixMode(GL_PROJECTION);
     	glLoadIdentity();
     	gluPerspective(45.0, 1.0, 0.1, 100.0);
     
     	setLighting();
     
     	glGenTextures(1, textureName);
   
     	glBindTexture(GL_TEXTURE_2D, textureName[0]);
     	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);
  }
  
  void CGWorld::display() {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glEnable(GL_DEPTH_TEST);
      
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_LIGHTING);
      drawBoard();
    
    
      if (textureChoice == MODULATE) {
          glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  
          if (imageMap != NULL) {
             glEnable(GL_TEXTURE_2D);
             imageMap->setTexture();
          }
      }
      else if (textureChoice == DECAL) {
          glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  
          if (imageMap != NULL) {
             glEnable(GL_TEXTURE_2D);
             imageMap->setTexture();
          }
      }

      if (enableLighting) {
           glEnable(GL_LIGHTING);
           glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, materialColor);
      }
      else {
           glDisable(GL_LIGHTING);
           glColor4fv(materialColor);
      }
       
      drawCylinder();
                          
      glutSwapBuffers();
      glDisable(GL_DEPTH_TEST);
  }
  
  void CGWorld::drawCylinder() {
      double point[3];
      double vector[3];

      glBegin(GL_QUAD_STRIP);
          for (int i=0; i<=72; i++) {
              double angle = (2.0 * PI * i)/72.0;
              point[0] = 0.5*cos(angle); point[1] = 0.0; point[2] = 0.5*sin(angle);
              normalize(point, vector);
              
              glTexCoord2d((double)i/72.0, 0.0);
              glNormal3dv(vector);
              glVertex3dv(point);
              
              glTexCoord2d((double)i/72.0, 1.0);
              point[1] = 1.0;
              glNormal3dv(vector);
              glVertex3dv(point);
          }      
      glEnd();
  }       
  
    void CGWorld::xzSquare(int which) { 
      if (which == 1) {       
         glBegin(GL_TRIANGLE_FAN);
           glColor3fv(rgbSquare[0].getRGB());
           glVertex3d(0.5, 0.0, 0.5);
           glColor3fv(rgbSquare[1].getRGB());
           glVertex3d(0.0, 0.0, 0.0);
           glColor3fv(rgbSquare[2].getRGB());
           glVertex3d(1.0, 0.0, 0.0);
           glColor3fv(rgbSquare[1].getRGB());
           glVertex3d(1.0, 0.0, 1.0);
           glColor3fv(rgbSquare[2].getRGB());
           glVertex3d(0.0, 0.0, 1.0);
           glColor3fv(rgbSquare[1].getRGB());
           glVertex3d(0.0, 0.0, 0.0);
         glEnd();
      }         
      else {
         glBegin(GL_TRIANGLE_FAN);
           glColor3f(0.8, 0.8, 0.8);
           glVertex3d(0.5,0.0,  0.5);
           glColor3f(0.4, 0.4, 0.4);
           glVertex3d(0.0, 0.0, 0.0);
           glColor3f(0.6, 0.6, 0.6);
           glVertex3d(1.0, 0.0, 0.0);
           glColor3f(0.4, 0.4, 0.4);
           glVertex3d(1.0, 0.0, 1.0);
           glColor3f(0.6, 0.6, 0.6);
           glVertex3d(0.0, 0.0, 1.0);
           glColor3f(0.4, 0.4, 0.4);
           glVertex3d(0.0, 0.0, 0.0);
         glEnd();
      }           
  }
  
  void CGWorld::xzRow(int first, int second) { 
     for (int i=0; i<numberOfSquares; i+=2) {
   	    glPushMatrix();
   	    glTranslated(i*sizeOfSquare, 0.0, 0.0);
   	    glScaled(sizeOfSquare, 1.0, sizeOfSquare);
   	    xzSquare(first); 
   	    glPopMatrix();
     }   	
   
     for (int i=1; i<numberOfSquares; i+=2) {
   	    glPushMatrix();
   	    glTranslated(i*sizeOfSquare, 0.0, 0.0);
   	    glScaled(sizeOfSquare, 1.0, sizeOfSquare);
   	    xzSquare(second); 
   	    glPopMatrix();
     }	 
  }		
  
  void CGWorld::drawBoard() {
     glMatrixMode(GL_MODELVIEW);
     glPushMatrix();
     glTranslated(-1.5, 0.0, -1.0);
     for (int i = 0; i<numberOfSquares; i+=2) {
     	   glPushMatrix();
     	   glTranslated(0.0, 0.0, i*sizeOfSquare);
     	   xzRow(2, 1); 
     	   glTranslated(0.0, 0.0, sizeOfSquare);
     	   xzRow(1, 2); 
        glPopMatrix();
     }    
     glPopMatrix();  	
  }	

  void CGWorld::setLighting() {
       float directionalPosition[] = { 1.0, 0.0, 1.0, 0.0};

	 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, darkGray);
	 glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor);
	 glLightfv(GL_LIGHT1, GL_POSITION, directionalPosition);
	 glEnable(GL_LIGHT1);
  }

   void CGWorld::setImageMap(string directory, string fileName) {
        if (imageMap != NULL)
           delete imageMap;
        imageMap = new ImageMap(directory, fileName);
        textureChoice = DECAL;
        glutSetWindow(windowHandle);
        glutPostRedisplay();
   }
           
 
   void CGWorld::setRed(float red) {
       materialColor[0] = red;
       glutSetWindow(windowHandle);
       glutPostRedisplay();
   }
   
   void CGWorld::setGreen(float green) {
       materialColor[1] = green;
       glutSetWindow(windowHandle);
       glutPostRedisplay();
   }
   
   void CGWorld::setBlue(float blue) {
       materialColor[2] = blue;
       glutSetWindow(windowHandle);
       glutPostRedisplay();
   }        

   void CGWorld::setTextureMethod(int which) {
       textureChoice = which;
       glutSetWindow(windowHandle);
       glutPostRedisplay();
   }
   
   void CGWorld::setLightingMode(bool state) {
        enableLighting = state;
        glutSetWindow(windowHandle);
        glutPostRedisplay();
   }
   
  int CGWorld::getWindowHandle() {
   	  return windowHandle;
  }
  
  int CGWorld::getCornerX() {
  	  return cornerX;
  }
  
  int CGWorld::getCornerY() {
  	  return cornerY;
  }
  
  int CGWorld::getWidth() {
  	  return width;
  }
  
  int CGWorld::getHeight() {
  	  return height;
  }