/**************************************************
 *   File:     ImageMap.cpp
 *   Author:   Dr. Dalton R. Hunkins     
 *   Date:     July 2009
 *  
**************************************************/

#include <GL/Gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/Utils/imageMap.h>
#include <GL/Utils/MathFunctions.h>

#include <iostream>
using std::cout;
using std::endl;

# include <cmath>

class ProgramExit {};

// Menu Constants 
static const int menuBarn      = 900;
static const int menuMountain  = 901;
static const int menuFishes    = 902;

static const int menuFullImage = 910;
static const int menuSquare1   = 911;
static const int menuSquare2   = 912;
static const int menuHexagon   = 913;
static const int menuOctagon   = 914;

static const int menuShowCode     = 921;
static const int menuCloseCode    = 922;
static const int menuCloseProgram = 923;

// Image Map Declarations
ImageMap *barn;
ImageMap *mountain;
ImageMap *fishes;

string parentDirectory = "ComputerGraphicsTutorials";
string imageMapPath;

string barnName        = "barn.bmp";
string mountainName    = "mountain.bmp";
string fishesName      = "fishes.bmp";

int displayChoice  = menuFullImage;
int textureChoice  = menuBarn;

static GLuint textureName[3];

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);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
   
    // Establish  Image Maps
   barn     = new ImageMap(imageMapPath, barnName);
   mountain = new ImageMap(imageMapPath, mountainName);
   fishes   = new ImageMap(imageMapPath, fishesName);
   
   // Generate Texture Names
   glGenTextures(3, textureName);
   
    // Create Texture Objects with Properties  
  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);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  
   barn->setTexture();

   glBindTexture(GL_TEXTURE_2D, textureName[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_DECAL);  
   mountain->setTexture();
   
   glBindTexture(GL_TEXTURE_2D, textureName[2]);
   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_DECAL);  
   fishes->setTexture();
   
   glEnable(GL_TEXTURE_2D);
}

void drawFullImage() {
    glBegin(GL_POLYGON);
      glTexCoord2f(0.0, 0.0);
      glVertex2d(-1.0, -1.0);
      glTexCoord2f(1.0, 0.0);
      glVertex2d(1.0, -1.0);
      glTexCoord2f(1.0, 1.0);
      glVertex2d(1.0, 1.0);
      glTexCoord2f(0.0, 1.0);
      glVertex2d(-1.0, 1.0);
    glEnd();
}

void drawSquare1() {
    glBegin(GL_POLYGON);
      glTexCoord2f(0.0, 0.0);
      glVertex2d(0.0, -0.95);
      glTexCoord2f(1.0, 0.0);
      glVertex2d(0.95, 0.0);
      glTexCoord2f(1.0, 1.0);
      glVertex2d(0.0, 0.95);
      glTexCoord2f(0.0, 1.0);
      glVertex2d(-0.95, 0.0);
    glEnd();
  
}

void drawSquare2() {
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glTranslated(-0.5, -0.5, 0.0);
    glScaled(0.5, 0.5, 1.0);
    
    glBegin(GL_POLYGON);
       glTexCoord2f(cos(0*PI/4.0), sin(0*PI/4.0));
       glVertex2d(  cos(0*PI/4.0), sin(0*PI/4.0));
       glTexCoord2f(cos(2*PI/4.0), sin(2*PI/4.0));
       glVertex2d(  cos(2*PI/4.0), sin(2*PI/4.0));
       glTexCoord2f(cos(4*PI/4.0), sin(4*PI/4.0));
       glVertex2d(  cos(4*PI/4.0), sin(4*PI/4.0));
       glTexCoord2f(cos(6*PI/4.0), sin(6*PI/4.0));
       glVertex2d(  cos(6*PI/4.0), sin(6*PI/4.0));
    glEnd();
    glPopMatrix();
}               

void drawHexagon() {
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glTranslated(-0.5, -0.5, 0.0);
    glScaled(0.5, 0.5, 1.0);
    
    glBegin(GL_POLYGON);
       glTexCoord2f(cos(0*PI/3.0), sin(0*PI/3.0));
       glVertex2d(  cos(0*PI/3.0), sin(0*PI/3.0));
       glTexCoord2f(cos(1*PI/3.0), sin(1*PI/3.0));
       glVertex2d(  cos(1*PI/3.0), sin(1*PI/3.0));
       glTexCoord2f(cos(2*PI/3.0), sin(2*PI/3.0));
       glVertex2d(  cos(2*PI/3.0), sin(2*PI/3.0));
       glTexCoord2f(cos(3*PI/3.0), sin(3*PI/3.0));
       glVertex2d(  cos(3*PI/3.0), sin(3*PI/3.0));
       glTexCoord2f(cos(4*PI/3.0), sin(4*PI/3.0));
       glVertex2d(  cos(4*PI/3.0), sin(4*PI/3.0));
       glTexCoord2f(cos(5*PI/3.0), sin(5*PI/3.0));
       glVertex2d(  cos(5*PI/3.0), sin(5*PI/3.0));
    glEnd();
    glPopMatrix();
}               

void drawOctagon() {
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glTranslated(-0.5, -0.5, 0.0);
    glScaled(0.5, 0.5, 1.0);
    
    glBegin(GL_POLYGON);
       glTexCoord2f(cos(0*PI/4.0), sin(0*PI/4.0));
       glVertex2d(  cos(0*PI/4.0), sin(0*PI/4.0));
       glTexCoord2f(cos(1*PI/4.0), sin(1*PI/4.0));
       glVertex2d(  cos(1*PI/4.0), sin(1*PI/4.0));
       glTexCoord2f(cos(2*PI/4.0), sin(2*PI/4.0));
       glVertex2d(  cos(2*PI/4.0), sin(2*PI/4.0));
       glTexCoord2f(cos(3*PI/4.0), sin(3*PI/4.0));
       glVertex2d(  cos(3*PI/4.0), sin(3*PI/4.0));
       glTexCoord2f(cos(4*PI/4.0), sin(4*PI/4.0));
       glVertex2d(  cos(4*PI/4.0), sin(4*PI/4.0));
       glTexCoord2f(cos(5*PI/4.0), sin(5*PI/4.0));
       glVertex2d(  cos(5*PI/4.0), sin(5*PI/4.0));
       glTexCoord2f(cos(6*PI/4.0), sin(6*PI/4.0));
       glVertex2d(  cos(6*PI/4.0), sin(6*PI/4.0));
       glTexCoord2f(cos(7*PI/4.0), sin(7*PI/4.0));
       glVertex2d(  cos(7*PI/4.0), sin(7*PI/4.0));
    glEnd();
    glPopMatrix();
}               

void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    
    switch(textureChoice) {
       case menuBarn :
           glBindTexture(GL_TEXTURE_2D, textureName[0]);
           break;
       case menuMountain :
           glBindTexture(GL_TEXTURE_2D, textureName[1]);
           break;
       case menuFishes :
           glBindTexture(GL_TEXTURE_2D, textureName[2]);
           break;
    }          
    
    switch(displayChoice) {
       case menuFullImage :
          drawFullImage();
          break;                          
       case menuSquare1 :
          drawSquare1();
          break;
       case menuSquare2 :
          drawSquare2();
          break;
       case menuHexagon :
          drawHexagon();
          break;
       case menuOctagon :
          drawOctagon();
          break;
    }                                            
    
    glFlush();
}
   
void menuHandler(int selection) {
    switch (selection) {
       case menuBarn     :
       case menuMountain :
       case menuFishes   :
          textureChoice = selection;
          glutPostRedisplay();
          break;
       case menuFullImage :          
       case menuSquare1   :
       case menuSquare2   :
       case menuHexagon   :
       case menuOctagon   :
          displayChoice = selection;
          glutPostRedisplay();
          break;          
       case menuCloseProgram :
          throw ProgramExit();
    }
}

void createMenu() {
    int mainMenuId     = glutCreateMenu(menuHandler);
    int textureMenuId  = glutCreateMenu(menuHandler);
    int displayMenuId  = glutCreateMenu(menuHandler);
    

    glutSetMenu(mainMenuId);
    glutAddSubMenu("Select Image Map",             textureMenuId);
    glutAddSubMenu("Select Geometry and Mapping",  displayMenuId);
    glutAddMenuEntry("", 0);
    glutAddMenuEntry("Close Program",        menuCloseProgram);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    glutSetMenu(textureMenuId);
    glutAddMenuEntry("Barn",      menuBarn);
    glutAddMenuEntry("Mountain ", menuMountain);
    glutAddMenuEntry("Fishes   ", menuFishes);

    glutSetMenu(displayMenuId);
    glutAddMenuEntry("Full Mapping    ", menuFullImage);
    glutAddMenuEntry("Square Mapping 1", menuSquare1);
    glutAddMenuEntry("Square Mapping 2", menuSquare2);
    glutAddMenuEntry("Hexagon Mapping ", menuHexagon);
    glutAddMenuEntry("Octagon Mapping ", menuOctagon);
}


int main(int argc, char **argv) {
    string pathName = argv[0];
    setPaths(pathName);
    
    glutInit(&argc, argv);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Image Map");
    createMenu();

    glutDisplayFunc(display);
    
    init();
    
    showCode();
   
    try {
      glutMainLoop();
    }
    catch (ProgramExit) { 
    	cout << "Closing program" << endl;}
	
    return 0;
}