//***************************************************************************
#include <math.h>
#include <ctype.h>
#include <iostream.h>
#include <fstream.h>
#include <GL/glut.h>
#include <stdlib.h>
#include "color.h"
#include "query.h"
#include "readrgb.h"
#include "table.h"
#include <stdio.h>
//***************************************************************************
#define TOLERANCE (1.0e-6)
#define IS_EQUAL(x,y) (fabs((x)-(y)) < TOLERANCE)
#define MAX_EDGES 300
//***************************************************************************
typedef struct _myEdge {
        double x1, x2, y1, y2;
        } myEdge;
//***************************************************************************

// Global variables:
static int winIdMain;
static int winIdSub;
myColor colorDB;        // Color Database
GLfloat         yAngle, xPosition, yPosition, zPosition, tempAngle;
GLfloat         yAnglePopup, tempAnglePopup;
GLfloat         xBegin, zBegin, xEnd, zEnd, xCheck, zCheck;
GLint           xStart, yStart, zStart;
GLint           finished = 0;
GLfloat         strip[MAX_EDGES][10][3];
GLfloat         doorstrip[MAX_EDGES][10][3];
int             numDoorStrips;
GLfloat         stripColor[MAX_EDGES][10][4];
GLfloat         doorstripColor[MAX_EDGES][10][4];

GLfloat         mat_diffuse[] = { 0.6, 0.6, 0.6, 1.0 };
GLfloat         mat_specular[] = { 1.0, 0.5, 0.5, 1.0 };
GLfloat         mat_shininess[] = { 30.0 };
GLfloat         spinningSpherePos[] = { 1.0, 1.0, 4.0 };
GLfloat         bouncingSpherePos[] = { 3.0, 0.0, 6.0 };
GLfloat         ball_color[] = { 0.0, 0.0, 0.0, 0.0 };
GLfloat         floorColor[4];

double          deg2rad = 3.141592654 / 180.0;
double          rad2deg = 180.0 / 3.141592654;
float           safe = 0.1;
int             numEdges, numObjects;
int             tick = 0, tock = 0, tack = 0;
int             noWalls = 0;
float           edgelist[MAX_EDGES][2][2];
float           dooredge[MAX_EDGES][2][2];
myEdge          myEdges[MAX_EDGES];
float           objects[MAX_EDGES][2];
int             objectType[MAX_EDGES];
float           Xmin, Zmin, Xmax, Zmax;
int             popView = 0;
float           zoom = 25.0;
int             initialMovement = 0;
long            startTime, endTime, totalTime, score;
int             popCount;
int             whichMaze = 1;
int             numBoundingEdges;
float           xVec, zVec;
char            inpFile[30];
int             inpFlag = 0;
int             helpflag = 0;
int             textureflag = 1;
double x_scaling_factor = +3.0;
double z_scaling_factor = -3.0;
int tableOrientation = 0;       // 0 -> North, 1 -> South
GLfloat         lightZeroPosition[] = { 27.0*x_scaling_factor, 3.2, 2.7*z_scaling_factor, 1.0 };
GLfloat         lightZeroColor[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat         lightOnePosition[] = { 0.0, 3.2, 2.7*z_scaling_factor, 1.0 };
GLfloat         lightOneColor[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat         lightTwoPosition[] = { 13.0*x_scaling_factor, 4.0, 2.7*z_scaling_factor, 1.0 };
GLfloat         lightTwoColor[] = { 1.0, 1.0, 1.0, 1.0 };
float xGlobal, yGlobal;
char filename[][30] = {"plan.dat"};
double wallheight = 4.0;
int numfiles = sizeof(filename)/sizeof(filename[30]);
int WIDTH = 700, HEIGHT = 500;
void drawString(char *s);
char infoString[200];
//***************************************************************************
static int compareEdge(const void *e1, const void *e2);
void addColors();
float OrientPointer(float xVec, float zVec);
void print_help();
void readMazeFromFile();
int addTextureFromFile(char *filename);

void printRoom(Room *ptr);
void printhelpScreen(void);
void reshape(int width, int height);
void initMazeColor(void);
void setBoundingBox(float x, float z);
void facecube(float tx, float tz, int texnum);
void subDisplay(void);
void subreshape(int w, int h);
void renderRoom(Room *room);
//***************************************************************************
// idle routine that updates clock for spinning sphere
void idle(void)
{
tick = (tick+1)%120;

tack++;
if (tack < 10 && tack > 4)
        {
         if (tack > 8)
                tack = 0;
         tock = (tock > 0) ? -1 : tock - 1;
        } 
else
        tock = (tock < 0) ? 1 : tock + 1;

glutPostRedisplay();
}
//***************************************************************************
//  Convert maze description to quadralaterals 
void initMaze()
{
int i, k;
float dx, dy, nx, ny, norm;
float r, g, b;
char name[30];

finished = 0;

readMazeFromFile();

xPosition = xBegin;
zPosition = zBegin;
yPosition = 1.0;
if (whichMaze == 2)
        yAngle = 180.0;
else
        yAngle = 0.0;
yAnglePopup = yAngle;

// We take each edge and extend it into a wall of thickness 0.1 
// We also extend the edges a bit extra in the "long" direction 
// so that we don't get faces of adjoining walls right on top of 
// other.  Note that we have made no effort to get the surface 
// normals correct. 

double wallextension = 1.0;
double wallwidth = 0.10;

for (i = 0; i < numEdges; i++)
        {
         dx = edgelist[i][1][0] - edgelist[i][0][0];
         dy = edgelist[i][1][1] - edgelist[i][0][1];
         norm = sqrt(dx*dx + dy*dy);
         dx = wallwidth * dx / norm;
         dy = wallwidth * dy / norm;
         nx = -dy;
         ny = dx;

         // convert each edge to quad strip 
         strip[i][0][0] = edgelist[i][0][0] - wallextension * dx - nx;
         strip[i][0][1] = 0.0;
         strip[i][0][2] = edgelist[i][0][1] - wallextension * dy - ny;

         strip[i][1][0] = strip[i][0][0];
         strip[i][1][1] = wallheight;
         strip[i][1][2] = strip[i][0][2];

         strip[i][2][0] = edgelist[i][1][0] + wallextension * dx - nx;
         strip[i][2][1] = 0.0;
         strip[i][2][2] = edgelist[i][1][1] + wallextension * dy - ny;

         strip[i][3][0] = strip[i][2][0];
         strip[i][3][1] = wallheight;
         strip[i][3][2] = strip[i][2][2];

         strip[i][4][0] = edgelist[i][1][0] + wallextension * dx + nx;
         strip[i][4][1] = 0.0;
         strip[i][4][2] = edgelist[i][1][1] + wallextension * dy + ny;

         strip[i][5][0] = strip[i][4][0];
         strip[i][5][1] = wallheight;
         strip[i][5][2] = strip[i][4][2];

         strip[i][6][0] = edgelist[i][0][0] - wallextension * dx + nx;
         strip[i][6][1] = 0.0;
         strip[i][6][2] = edgelist[i][0][1] - wallextension * dy + ny;

         strip[i][7][0] = strip[i][6][0];
         strip[i][7][1] = wallheight;
         strip[i][7][2] = strip[i][6][2];

         strip[i][8][0] = strip[i][0][0];
         strip[i][8][1] = strip[i][0][1];
         strip[i][8][2] = strip[i][0][2];

         strip[i][9][0] = strip[i][1][0];
         strip[i][9][1] = strip[i][1][1];
         strip[i][9][2] = strip[i][1][2];

         // generate some different colors for the vertices 
         for (k = 0; k < 10; k++)
                {
                 colorDB.setColor4f("Blue",1.0,stripColor[i][k]);
                }
        }

for (i = 0; i < numDoorStrips; i++)
        {
         dx = dooredge[i][1][0] - dooredge[i][0][0];
         dy = dooredge[i][1][1] - dooredge[i][0][1];
         norm = sqrt(dx*dx + dy*dy);
         dx = wallwidth * dx / norm;
         dy = wallwidth * dy / norm;
         nx = -dy;
         ny = dx;

         // convert each edge to quad doorstrip 
         doorstrip[i][0][0] = dooredge[i][0][0] - wallextension * dx - nx;
         doorstrip[i][0][1] = 0.67*wallheight;
         doorstrip[i][0][2] = dooredge[i][0][1] - wallextension * dy - ny;

         doorstrip[i][1][0] = doorstrip[i][0][0];
         doorstrip[i][1][1] = wallheight;
         doorstrip[i][1][2] = doorstrip[i][0][2];

         doorstrip[i][2][0] = dooredge[i][1][0] + wallextension * dx - nx;
         doorstrip[i][2][1] = 0.67*wallheight;
         doorstrip[i][2][2] = dooredge[i][1][1] + wallextension * dy - ny;

         doorstrip[i][3][0] = doorstrip[i][2][0];
         doorstrip[i][3][1] = wallheight;
         doorstrip[i][3][2] = doorstrip[i][2][2];

         doorstrip[i][4][0] = dooredge[i][1][0] + wallextension * dx + nx;
         doorstrip[i][4][1] = 0.67*wallheight;
         doorstrip[i][4][2] = dooredge[i][1][1] + wallextension * dy + ny;

         doorstrip[i][5][0] = doorstrip[i][4][0];
         doorstrip[i][5][1] = wallheight;
         doorstrip[i][5][2] = doorstrip[i][4][2];

         doorstrip[i][6][0] = dooredge[i][0][0] - wallextension * dx + nx;
         doorstrip[i][6][1] = 0.67*wallheight;
         doorstrip[i][6][2] = dooredge[i][0][1] - wallextension * dy + ny;

         doorstrip[i][7][0] = doorstrip[i][6][0];
         doorstrip[i][7][1] = wallheight;
         doorstrip[i][7][2] = doorstrip[i][6][2];

         doorstrip[i][8][0] = doorstrip[i][0][0];
         doorstrip[i][8][1] = doorstrip[i][0][1];
         doorstrip[i][8][2] = doorstrip[i][0][2];

         doorstrip[i][9][0] = doorstrip[i][1][0];
         doorstrip[i][9][1] = doorstrip[i][1][1];
         doorstrip[i][9][2] = doorstrip[i][1][2];

         // generate some different colors for the vertices 
         for (k = 0; k < 10; k++)
                {
                 colorDB.setColor4f("Blue",1.0,doorstripColor[i][k]);
                }
        }
}
//***************************************************************************
// draw the maze and floor 
void drawMaze()
{
int i, j;
float x, z;

if (helpflag)
        {
         printhelpScreen();
         return;
        }
initMazeColor();

if (textureflag)
        {
         glEnable(GL_TEXTURE_2D);
         glBindTexture(GL_TEXTURE_2D, 4);
        }

for (i = 0; i < numEdges; i++)
        {
         glBegin(GL_QUAD_STRIP);
         for (j = 0; j < 10; j++)
                {
                 if (textureflag)
                        {
                         if ((strip[i][j][0] == strip[i][j+1][0]) && (strip[i][j][0] == strip[i][j+2][0]))
                                glTexCoord2f(strip[i][j][2], strip[i][j][1]);
                         else
                                glTexCoord2f(strip[i][j][0], strip[i][j][1]);
                        }
                 else
                        {
                         glMaterialfv(GL_FRONT, GL_DIFFUSE, stripColor[i][j]);
                         glColor3fv(stripColor[i][j]);
                        }
                 glVertex3fv(strip[i][j]);
                }
         glEnd();
        }

for (i = 0; i < numDoorStrips; i++)
        {
         glBegin(GL_QUAD_STRIP);
         for (j = 0; j < 10; j++)
                {
                 if (textureflag)
                        {
                         if ((doorstrip[i][j][0] == doorstrip[i][j+1][0]) && (doorstrip[i][j][0] == doorstrip[i][j+2][0]))
                                glTexCoord2f(doorstrip[i][j][2], doorstrip[i][j][1]);
                         else
                                glTexCoord2f(doorstrip[i][j][0], doorstrip[i][j][1]);
                        }
                 else
                        {
                         glMaterialfv(GL_FRONT, GL_DIFFUSE, doorstripColor[i][j]);
                         glColor3fv(doorstripColor[i][j]);
                        }
                 glVertex3fv(doorstrip[i][j]);
                }
         glEnd();

         glBegin(GL_QUADS);
                 glVertex3fv(doorstrip[i][0]);
                 glVertex3fv(doorstrip[i][2]);
                 glVertex3fv(doorstrip[i][4]);
                 glVertex3fv(doorstrip[i][6]);
         glEnd();
        }

if (textureflag)
        glDisable(GL_TEXTURE_2D);

// Draw the Floor
colorDB.setColor4f("White",1.0,floorColor);

glBegin(GL_POLYGON);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, floorColor);
glVertex3f(Xmin, 0.0, Zmin);
glVertex3f(Xmax, 0.0, Zmin);
glVertex3f(Xmax, 0.0, Zmax);
glVertex3f(Xmin, 0.0, Zmax);
glVertex3f(Xmin, 0.0, Zmin);
glEnd();

if (!popView)   // Draw the Ceiling
        {
         glBegin(GL_POLYGON);
                glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, floorColor);
                glVertex3f(Xmin, wallheight, Zmin);
                glVertex3f(Xmax, wallheight, Zmin);
                glVertex3f(Xmax, wallheight, Zmax);
                glVertex3f(Xmin, wallheight, Zmax);
                glVertex3f(Xmin, wallheight, Zmin);
         glEnd();

         for (i = 0; i < numObjects; i++)
                {
                 glEnable(GL_TEXTURE_2D);
                 glBindTexture(GL_TEXTURE_2D, i+1);
                 if (tableOrientation == 0)     // North
                        drawTableAndChair(objects[i][0], objects[i][1], 0.0);
                 else                           // South
                        drawTableAndChair(objects[i][0], objects[i][1], 180.0);
                 glDisable(GL_TEXTURE_2D);
                }

         facecube(17.6,4.6, 5); // Dr. Sharma's face
         facecube(24.5, 1.5, 6);        // Dr. Plassmann's face
        }
}
//***************************************************************************
// display function resets MODELVIEW matrix and draws everything 
void display(void)
{
GLfloat angle, ybounce;

glutSetWindow(winIdMain);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (!popView)   // Normal view
        {
         gluLookAt(0.0, 0.0, 0.0, /// eye is at (0,0,0) 
                        0.0, 0.0, -100.0, //center is at (0,0,-100) 
                        0.0, 1.0, 0.); // up is in positive Y direction 
         glPushMatrix();
         glRotatef(yAngle, 0.0, 1.0, 0.0);
         glTranslatef(-xPosition, -yPosition, -zPosition);
        }
else            // Pop-up view
        {
         gluLookAt(Xmax / 2, zoom, Zmax / 2, // eye is at (0,0,0) 
                        0.0, 0.0, 0.0, // center is at (0,0,-100) 
                        0.0, 1.0, 0.); // up is in positive Y direction 
         glRotatef(yAnglePopup, 0.0, 1.0, 0.0);
         glTranslatef(-Xmax / 2, 0.0, -Zmax / 2);
        }

// draws objects in popup mode, start, finish, your position, and direction
if (popView)
        facecube(xGlobal+0.1, yGlobal+0.1, 7);

glLightfv(GL_LIGHT1, GL_AMBIENT, lightOneColor);
glLightfv(GL_LIGHT1, GL_POSITION, lightOnePosition);
glEnable(GL_LIGHT1);

glLightfv(GL_LIGHT2, GL_DIFFUSE, lightTwoColor);
glLightfv(GL_LIGHT2, GL_POSITION, lightTwoPosition);
glEnable(GL_LIGHT2);

drawMaze();
glPopMatrix();

glutSwapBuffers();
subDisplay();
}
//***************************************************************************
// mouse click initiates movement 
void mouse(int button, int state, int x, int y)
{
if (state == GLUT_DOWN)
        {
         if (button == GLUT_LEFT_BUTTON)
                {
                 xStart = x;
                 yStart = y;
                }
        }
}
//***************************************************************************
// Make sure that we don't run into walls 
// Note that we aren't doing the wall in the middle! 
void checkIntersections(GLfloat xVec, GLfloat zVec)
{
int i;
GLfloat xCollision = -1.0, zCollision = -1.0;

for (i = 0; i < numEdges + numBoundingEdges; i++)
        {
         if (edgelist[i][0][0] == edgelist[i][1][0]) // vertical Z wall
                {
                 if (edgelist[i][0][1] - safe < zPosition
                        && zPosition < edgelist[i][1][1] + safe
                        || edgelist[i][0][1] + safe > zPosition
                        && zPosition > edgelist[i][1][1] - safe)
                        {
                         if ((xPosition < edgelist[i][0][0]
                                && xPosition - xVec > edgelist[i][0][0] - safe)
                                || (xPosition > edgelist[i][0][0]
                                && xPosition - xVec < edgelist[i][0][0] + safe))
                                {
                                 xCollision = edgelist[i][0][0];
                                 break;
                                }
                        }
                }
        }
for (i = 0; i < numEdges + numBoundingEdges; i++)
        {
         if (edgelist[i][0][1] == edgelist[i][1][1]) // horizontal X wall
                {
                 if (edgelist[i][0][0] - safe < xPosition
                        && xPosition < edgelist[i][1][0] + safe
                        || edgelist[i][0][0] + safe > xPosition
                        && xPosition > edgelist[i][1][0] - safe)
                        {
                         if ((zPosition < edgelist[i][0][1]
                                && zPosition + zVec > edgelist[i][0][1] - safe)
                                || (zPosition > edgelist[i][0][1]
                                && zPosition + zVec < edgelist[i][0][1] + safe))
                                {
                                 zCollision = edgelist[i][0][1];
                                 break;
                                }
                        }
                }
        }
if (IS_EQUAL(xCollision,-1.0))
        xPosition -= xVec;
else
        xPosition = (xVec < 0) ? xCollision - .25 : xCollision + .25;

if (IS_EQUAL(zCollision,-1.0))
        zPosition += zVec;
else
        zPosition = (zVec < 0) ? zCollision + .25 : zCollision - .25;

xCheck = fabs(xPosition - xEnd);
zCheck = fabs(zPosition - zEnd);
}
//***************************************************************************
// motion function -- we "pan" with an x motion, and we move forward/backward 
// based on y motion 
void motion(int x, int y)
{
double dx, dy, c, s;
static int motioncounter = 0;

++motioncounter;

if (initialMovement == 0)
        {
         initialMovement = 1;
         startTime = glutGet((GLenum) GLUT_ELAPSED_TIME);
         }
dx = (double) (x - xStart);
dy = (double) (y - yStart);

yAngle += 0.3 * dx;
yAnglePopup += 0.3 * dx;

if (!popView)
        {
         xVec = 0.04 * sin(deg2rad * yAngle) * dy;
         zVec = 0.04 * cos(deg2rad * yAngle) * dy;
         if (!noWalls)
                checkIntersections(xVec, zVec);
         else
                {
                 xPosition -= xVec;
                 zPosition += zVec;
                }
        }

/*
cout.setf(ios::fixed,ios::floatfield);
cout.precision(2);
cout << "\r(" << xPosition/x_scaling_factor << "," << zPosition/z_scaling_factor << ")";
cout.flush();
*/

int typeRequest = 0;
char keyWords[10] = "302";
Room *roomResult;

xGlobal = xPosition/x_scaling_factor;
yGlobal = zPosition/z_scaling_factor;

roomResult = (Room *) queryXML(typeRequest,keyWords, xGlobal, yGlobal);

if (roomResult->inRoom)
        {
         sprintf(infoString, "Room %d (%s)", roomResult->roomNumber, roomResult->introText);
         renderRoom(roomResult);
        }
else
        {
         sprintf(infoString, "You are in the corridor");
        }

xStart = x;
yStart = y;
glutPostRedisplay();
}
//***************************************************************************
// Initialize background, lights, materials, some global variables. 
void init(void)
{
sprintf(infoString, "You are in the corridor");

glClearColor(0.0, 0.05, 0.0, 0.0);
//glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);

// Enable a red OpenGL light. 
glLightfv(GL_LIGHT0, GL_AMBIENT, lightZeroColor);
glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
glEnable(GL_LIGHT0);

// Enable lighting 
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glEnable(GL_LIGHTING);

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

// initial angles and positions 
yAngle = 0.0;
yAnglePopup = 0.0;
xPosition = 1.0;
yPosition = 1.0;
zPosition = 8.0;

// Enable a green OpenGL light. 
addTextureFromFile("texture2.rgb");             // Texture #2
addTextureFromFile("tabletexture.rgb");         // Texture #1
addTextureFromFile("walltexture.rgb");          // Texture #3
addTextureFromFile("walltexture2.rgb");         // Texture #4
addTextureFromFile("sharma.rgb");               // Texture #5
addTextureFromFile("plassmann.rgb");            // Texture #6
addTextureFromFile("person.rgb");               // Texture #7

initMaze();
}
//***************************************************************************
// Window reshape function 
void reshape(int width, int height)
{
WIDTH = width; HEIGHT = height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (float) width / (float) height, 0.05, 100.0);

glMatrixMode(GL_MODELVIEW);
glutSetWindow(winIdSub);
glutReshapeWindow(width-10, height/10);
glutPositionWindow(5, 5);
glutSetWindow(winIdMain);

glutPostRedisplay();
}
//***************************************************************************
// Exit on escape from keyboard 
void keyboard(unsigned char c, int x, int y)
{
switch (tolower(c))
        {
         case 27:       // ESC character
         case 'q':
                cout << endl;
                exit(0);
         break;

         case '1':
                whichMaze = 1;
                init();
                glutPostRedisplay();
         break;

         case '2':
                whichMaze = 2;
                init();
                glutPostRedisplay();
         break;

         case '3':
                whichMaze = 3;
                init();
                glutPostRedisplay();
         break;

         case 'v':      // To change to popup or back
                glutSetWindow(winIdSub);
                if (!popView)
                        {
                         popCount++;
                         tempAngle = yAngle;
                         yAnglePopup = tempAnglePopup;
                         glutHideWindow();
                        } 
                else
                        {
                         yAngle = tempAngle;
                         tempAnglePopup = yAnglePopup;
                         glutShowWindow();
                         glutSetWindow(winIdMain);
                         reshape(WIDTH, HEIGHT);
                        }
                popView = (!popView);
                break;

         case 'w':
                noWalls = (noWalls + 1) % 2;
         break;

         case 't':
                textureflag = (!textureflag);
         break;

         default:
         break;
        }
}
//***************************************************************************
void key(int key, int x, int y)
{
switch(key)
        {
         case GLUT_KEY_F1:
                helpflag = (!helpflag);
                glutSetWindow(winIdSub);
                if (helpflag)
                        {
                         glutHideWindow();
                        }
                else
                        {
                         glutShowWindow();
                         glutSetWindow(winIdMain);
                         reshape(WIDTH, HEIGHT);
                        }
         break;

         case GLUT_KEY_LEFT:
                xStart = 0;
                yStart = 0;
                motion(-20, 0);
         break;

         case GLUT_KEY_RIGHT:
                xStart = 0;
                yStart = 0;
                motion(20, 0);
         break;

         case GLUT_KEY_UP:
                if (!popView)
                        {
                         xStart = 0;
                         yStart = 0;
                         motion(0, -10);
                        }
                else
                        {
                         if (zoom <= 5.0)
                                zoom = 5.0;
                         else
                                zoom -= 5.0;
                        }
         break;

         case GLUT_KEY_DOWN:
                if (!popView)
                        {
                         xStart = 0;
                         yStart = 0;
                         motion(0, 10);
                        }
                else
                        {
                         if (zoom >= 100.0)
                                zoom = 100.0;
                         else
                                zoom += 5.0;
                        }
         break;
        }
}
//***************************************************************************
int main(int argc, char **argv)
{
addColors();
glutInitWindowSize(WIDTH, HEIGHT);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glEnable(GL_DEPTH_TEST);

winIdMain = glutCreateWindow("Kiosk");

if (argc == 2)
        {
         inpFlag = 1;
         strcpy(inpFile, argv[1]);
        }
// print some directions to screen 
//print_help();

init();

glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutSpecialFunc(key);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutIdleFunc(idle);

winIdSub = glutCreateSubWindow(winIdMain, 5, 5, WIDTH-10, HEIGHT/10);
glutDisplayFunc(subDisplay);
glutReshapeFunc(subreshape);

glutSetWindow(winIdSub);
glutShowWindow();

//glutSetWindow(winIdMain);

glutMainLoop();

return 0;
}
//***************************************************************************
float OrientPointer(float xVec, float zVec)
{
return rad2deg * atan2(-zVec, -xVec);
}
//***************************************************************************
void addColors()
{
colorDB.init(20);

colorDB.addColor("Background", 255, 255, 255);
colorDB.addColor("White",      255, 255, 255);
colorDB.addColor("Black",        0,   0,   0);
colorDB.addColor("Red",        255,   0,   0);
colorDB.addColor("Green",        0, 255,   0);
colorDB.addColor("Blue",         0,   0, 255);
colorDB.addColor("Yellow",     255, 255,   0);
colorDB.addColor("Orange",     255, 128,   0);
colorDB.addColor("Brown",      165,  42,  42);
colorDB.addColor("RedTinted",  255,  77,  51);
}
//***************************************************************************
void print_help()
{
cout << "====================Keyboard Help===========================" << endl;
cout << "          F1: help" << endl;
cout << "           1: Switch to maze 1 (maze1.dat)" << endl;
cout << "           2: Switch to maze 2 (maze2.dat)" << endl;
cout << "           3: Switch to maze 3 (maze3.dat)" << endl;
cout << "           V: Toggle between POP-UP view of maze" << endl;
cout << "           W: Allows to go through the walls" << endl;
cout << "  arrow keys: Move around the maze" << endl;
cout << "        Esc/Q: Quit" << endl;
cout << "============================================================" << endl;
cout << "Using the LEFT MOUSE BUTTON: maneuvers you through the maze." << endl;
cout << "============================================================" << endl;
}
//***************************************************************************
void readMazeFromFile()
{
int i;
char name[30];

if (inpFlag)            // Filename specified as argument to program
        {
         ifstream inp(inpFile);
         if (!inp)
                {
                 cout << "Cannot read maze from file \"" << inpFile
                        << "\"....exiting!" << endl;
                 exit(-1);
                }
         strcpy(name,inpFile);
         inpFlag = 0;
        }
else
        strcpy(name,filename[whichMaze-1]);


ifstream inp(name);

if (!inp)
        {
         cerr << "Couldn't open file \"" << name << "\"!!!" 
                << endl;
         return;
        }
cout << "Reading plan from file \"" << name << "\".....";
cout.flush();

inp >> Xmin;
inp >> Zmin;
inp >> Xmax;
inp >> Zmax;
inp >> numEdges;
Xmin *= x_scaling_factor;
Zmin *= z_scaling_factor;
Xmax *= x_scaling_factor;
Zmax *= z_scaling_factor;

for (i = 0; i < numEdges; i++)
        {
         inp >> edgelist[i][0][0];
         inp >> edgelist[i][0][1];
         inp >> edgelist[i][1][0];
         inp >> edgelist[i][1][1];
         edgelist[i][0][0] *= x_scaling_factor;
         edgelist[i][0][1] *= z_scaling_factor;
         edgelist[i][1][0] *= x_scaling_factor;
         edgelist[i][1][1] *= z_scaling_factor;
        }

// Remove duplicate edges
int edgecount = 0;

for (i = 0; i < numEdges; i++)
        {
         myEdges[i].x1 = edgelist[i][0][0];
         myEdges[i].x2 = edgelist[i][1][0];
         myEdges[i].y1 = edgelist[i][0][1];
         myEdges[i].y2 = edgelist[i][1][1];
        }

qsort(myEdges,numEdges,sizeof(myEdge), compareEdge);

for (i = 1; i < numEdges; i++)
        {
         edgelist[edgecount][0][0] = myEdges[i-1].x1;
         edgelist[edgecount][1][0] = myEdges[i-1].x2;
         edgelist[edgecount][0][1] = myEdges[i-1].y1;
         edgelist[edgecount][1][1] = myEdges[i-1].y2;
         ++edgecount;

         if ((myEdges[i].x1 == myEdges[i-1].x1) && 
                (myEdges[i].x2 == myEdges[i-1].x2) && 
                (myEdges[i].y1 == myEdges[i-1].y1) && 
                (myEdges[i].y2 == myEdges[i-1].y2))
                --edgecount;
        }
/*
cout << "Number of given edges = " << numEdges;
cout << " (Number of unique edges = " << edgecount << ")" << endl;
cout.flush();
*/
numEdges = edgecount;

inp >> numDoorStrips;
for (i = 0; i < numDoorStrips; i++)
        {
         inp >> dooredge[i][0][0];
         inp >> dooredge[i][0][1];
         inp >> dooredge[i][1][0];
         inp >> dooredge[i][1][1];
         dooredge[i][0][0] *= x_scaling_factor;
         dooredge[i][0][1] *= z_scaling_factor;
         dooredge[i][1][0] *= x_scaling_factor;
         dooredge[i][1][1] *= z_scaling_factor;
        }

inp >> numObjects;

numBoundingEdges = 0;

for (i = 0; i < numObjects; i++)
        {
         inp >> objects[i][0];
         inp >> objects[i][1];
         inp >> objectType[i];

         objects[i][0] *= x_scaling_factor;
         objects[i][1] *= z_scaling_factor;
         if (objectType[i] == 2) // table and chair
                setBoundingBox(objects[i][0], objects[i][1]);
        }

inp >> xBegin;
inp >> zBegin;
inp >> xEnd;
inp >> zEnd;
xBegin *= x_scaling_factor;
zBegin *= z_scaling_factor;
xEnd *= x_scaling_factor;
zEnd *= z_scaling_factor;

inp.close();
cout << "done!" << endl;

}
//***************************************************************************
void printRoom(Room *ptr)
{
//        printf("\n Room in = %d ", ptr->inRoom);
        printf("\n Room number = %d ", ptr->roomNumber);
        printf("\n Professor = %s",ptr->professorName);
        printf("\n introText = %s ", ptr->introText);
        printf("\n research = %s ", ptr->research);
        printf("\n publications = %s",ptr->publications);
        printf("\n texture = %s",ptr->texture);
        printf("\n image = %s",ptr->image);
        printf("\n");
/*
        printf("\n numWalls = %d ", ptr->numWalls);
        printf("\n roomX1 = %.2f",ptr->roomX1);
        printf("\n roomX2 = %.2f ", ptr->roomX2);
        printf("\n roomX3 = %.2f",ptr->roomX3);
        printf("\n roomX4 = %.2f ", ptr->roomX4);
        printf("\n roomX5 = %.2f",ptr->roomX5);
        printf("\n roomX6 = %.2f ", ptr->roomX6);
        printf("\n roomX7 = %.2f",ptr->roomX7);
        printf("\n roomX8 = %.2f ", ptr->roomX8);
        printf("\n roomX9 = %.2f",ptr->roomX9);
        printf("\n roomX10= %.2f ",ptr->roomX10);
        printf("\n roomY1 = %.2f",ptr->roomY1);
        printf("\n roomY2 = %.2f ", ptr->roomY2);
        printf("\n roomY3 = %.2f",ptr->roomY3);
        printf("\n roomY4 = %.2f ", ptr->roomY4);
        printf("\n roomY5 = %.2f",ptr->roomY5);
        printf("\n roomY6 = %.2f ", ptr->roomY6);
        printf("\n roomY7 = %.2f",ptr->roomY7);
        printf("\n roomY8 = %.2f ",ptr->roomY8);
        printf("\n roomY9 = %.2f",ptr->roomY9);
        printf("\n roomY10= %.2f ", ptr->roomY10);
        printf("\n centerX= %.2f",ptr->centerX);
        printf("\n centerY= %.2f ", ptr->centerY);
        printf("\n tableX= %.2f",ptr->tableX);
        printf("\n tableY= %.2f ", ptr->tableY);
        printf("\n normalTable= %s",ptr->normalTable);
        printf("\n lightX= %f ", ptr->lightX);
        printf("\n lightY= %f",ptr->lightY);
*/
}
//***************************************************************************
int addTextureFromFile(char *filename)
{
int width, height, n_comp;
static int numTex = 0;

ifstream inp(filename);
if (!inp)
        {
         cout << "Cannot read texture file \"" << filename
                << "\"....skipping!" << endl;
         return -1;
        }
inp.close();

GLubyte *imageData = (GLubyte *) read_texture(filename, &width, &height,
                &n_comp);

cout << "Reading texture from file \"" << filename << "\" (" << width
                << "x" << height << "x" << n_comp << ")" << endl;
#define WIDTH 256
#define HEIGHT 256
GLubyte newimg[WIDTH][HEIGHT][4];
gluScaleImage(GL_RGBA, width, height, GL_UNSIGNED_BYTE, imageData,
                  WIDTH, HEIGHT, GL_UNSIGNED_BYTE, newimg);

++numTex;
glBindTexture(GL_TEXTURE_2D, numTex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA,
                GL_UNSIGNED_BYTE, newimg);

#undef WIDTH
#undef HEIGHT
}
/****************************************************************************/
static int compareEdge(const void *e1, const void *e2)
{
myEdge *E1, *E2;
E1 = (myEdge *) e1;
E2 = (myEdge *) e2;

double cmp = 1000*(E1->x1 - E2->x1) + 100*(E1->x2 - E2->x2) + 
                10*(E1->y1 - E2->y1) + (E1->y2 - E2->y2);

if ((E1->x1 == E2->x1) && (E1->x2 == E2->x2) && (E1->y1 == E2->y1)
                && (E1->y2 == E2->y2))
        return 0;
else if (cmp > 0.0)
        return +1;
else if (cmp < 0.0)
        return -1;
}
/****************************************************************************/
void printstring(void *font, char *string)
{
  int len,i;

  len=(int)strlen(string);
  for(i=0;i<len;i++)
    glutBitmapCharacter(font,string[i]);
}
/****************************************************************************/
void printhelpScreen(void)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5,639.5,-0.5,479.5,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//glColor4f(255,255,255,1.0);
//glClearColor(255,255,255,1.0);


glRasterPos2i(60,420);
printstring(GLUT_BITMAP_TIMES_ROMAN_24,"F1 -Help");

glRasterPos2i(60,370);
printstring(GLUT_BITMAP_TIMES_ROMAN_24,"V - Toggle Between POP-UP view of maze");
glRasterPos2i(60,320);
printstring(GLUT_BITMAP_TIMES_ROMAN_24,"T - To Toggle Texture");

glRasterPos2i(60,270);
printstring(GLUT_BITMAP_TIMES_ROMAN_24,"W - Allows to go through the walls ");
glRasterPos2i(60,220);

printstring(GLUT_BITMAP_TIMES_ROMAN_24,"Arrow Keys - Move Around the Maze");
glRasterPos2i(60,70);
printstring(GLUT_BITMAP_TIMES_ROMAN_24,"Esc/Q : QUIT");
glRasterPos2i(60,170);
printstring(GLUT_BITMAP_TIMES_ROMAN_24," Optional {Use The Left Mouse Button :");
glRasterPos2i(100,120);
printstring(GLUT_BITMAP_TIMES_ROMAN_24," To Maneuver  through the maze}");

}
//***************************************************************************
void initMazeColor(void)
{
glClearColor(0.0, 0.05, 0.0, 0.0);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);

// Enable a red OpenGL light. 
glLightfv(GL_LIGHT0, GL_AMBIENT, lightZeroColor);
glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
glEnable(GL_LIGHT0);

// Enable lighting 
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glEnable(GL_LIGHTING);

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
}
//***************************************************************************
void setBoundingBox(float x, float z)
{
numBoundingEdges++;
edgelist[numEdges + numBoundingEdges - 1][0][0] = x;
edgelist[numEdges + numBoundingEdges - 1][0][1] = z;
edgelist[numEdges + numBoundingEdges - 1][1][0] = x + 1.1;
edgelist[numEdges + numBoundingEdges - 1][1][1] = z;

numBoundingEdges++;
edgelist[numEdges + numBoundingEdges - 1][0][0] = x;
edgelist[numEdges + numBoundingEdges - 1][0][1] = z + 1.0;
edgelist[numEdges + numBoundingEdges - 1][1][0] = x + 1.1;
edgelist[numEdges + numBoundingEdges - 1][1][1] = z + 1.0;

numBoundingEdges++;
edgelist[numEdges + numBoundingEdges - 1][0][0] = x + 1.0;
edgelist[numEdges + numBoundingEdges - 1][0][1] = z + 1.0;
edgelist[numEdges + numBoundingEdges - 1][1][0] = x + 1.0;
edgelist[numEdges + numBoundingEdges - 1][1][1] = z;

numBoundingEdges++;
edgelist[numEdges + numBoundingEdges - 1][0][0] = x + 1.1;
edgelist[numEdges + numBoundingEdges - 1][0][1] = z;
edgelist[numEdges + numBoundingEdges - 1][1][0] = x;
edgelist[numEdges + numBoundingEdges - 1][1][1] = z;
}
//***************************************************************************
void facecube(float tx, float tz, int texnum)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texnum);

glPushMatrix();
glTranslatef(tx*x_scaling_factor,0.4,tz*z_scaling_factor);
cube(1.0,1.0,1.0,1.0,0.0,0.0,0.0);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
//***************************************************************************
void subDisplay(void)
{
  char label[100];

  glutSetWindow(winIdSub);
  glClearColor(0.25, 0.25, 0.25, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glColor3f(0.0, 1.0, 0.0);

  // Draw Box  
  glBegin(GL_LINE_LOOP);
  glVertex2f(-0.999F, -0.99F);
  glVertex2f(-0.999F, +0.99F);
  glVertex2f(+0.999F, +0.99F);
  glVertex2f(+0.999F, -0.99F);
  glEnd();

  // Draw Text info  
  colorDB.setglColor3f("White");
  float xf = (WIDTH-450)*1.0/WIDTH-1.0;
  float yf = 0.1*WIDTH/HEIGHT;
  if (xf < -0.97)
        xf = -0.97;
  xf = -0.5-strlen(infoString)*4.3/WIDTH + 0.2;
  glRasterPos2f(xf, yf);
  drawString(infoString);

  glutSwapBuffers();
}
//***************************************************************************
// SubWindow reshape function
void subreshape(int w, int h)
{
glViewport(0, 0, w, h);
}
//***************************************************************************
void drawString(char *s)
{
double curpos[4];

colorDB.setglColor3f("White");
glGetDoublev(GL_CURRENT_RASTER_POSITION, curpos);
glRasterPos2f(2*curpos[0]/(WIDTH-10)-1.0, 2*curpos[1]/(HEIGHT/10)-1.0);

unsigned int i;

for (i = 0; i < strlen(s); i++)
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, s[i]);
}
//***************************************************************************
void renderRoom(Room *room)
{
static int callonce = 0;
if (room->tableX == 0.0 && room->tableY == 0.0) // Compute table co-ordinates
                                                // automatically
        {
         room->tableX = (room->roomX1+room->roomX2+room->roomX3+room->roomX4+room->roomX5+room->roomX6)/6.0;
         room->tableY = (room->roomY1+room->roomY2+room->roomY3+room->roomY4+room->roomY5+room->roomY6)/6.0;
        }
else if (room->tableX == -1.0 && room->tableY == -1.0)  // No table
        return;

if (room->tableY > 2.7)
        tableOrientation = 1;   // South
else
        tableOrientation = 0;   // North

int i = 0;
numObjects = 1;
/*
if (strcmp(room->normalTable,"North") == 0)
        tableOrientation = 0;           // North
else
        tableOrientation = 1;           // South
*/

objects[i][0] = room->tableX;
objects[i][1] = room->tableY;

objectType[i] = 2;

objects[i][0] *= x_scaling_factor;
objects[i][1] *= z_scaling_factor;

if (objectType[i] == 2) // table and chair
        setBoundingBox(objects[i][0], objects[i][1]);

char command[200];
if (callonce == 0)
        {
         sprintf(command,"netscape-remote %s &",room->publications);
//       sprintf(command,"c:\\Progra~1\\Intern~1\\iexplore.exe %s &",room->publications);
         system(command);
        }
++callonce;
}
//***************************************************************************