////////////////////////////////////////////////////
// Demonstrates texture mapping on a rotating cube
////////////////////////////////////////////////////

#include <glut.h>
#include "texture.h"

#define MAX_VERTICES 2000
#define MAX_POLYGONS 2000

//Vertex type
typedef struct{
    float x,y,z;
}vType;

//Triangle type
typedef struct{
    int a,b,c;
}tType;

//Texture coordinates type
typedef struct{
    float u,v;
}texType;

//Object type
typedef struct { 
    vType vertex[MAX_VERTICES]; 
    tType triangle[MAX_POLYGONS];
    texType texCoord[MAX_VERTICES];
    GLuint textureID;
} oType, *oTypePtr;

//Window size
int width=640;
int height=480;

//Starting rotation angles and increments
double rotation_x = 0, rotation_x_increment = 0.11;
double rotation_y = 0, rotation_y_increment = 0.07;
double rotation_z = 0, rotation_z_increment = 0.05;
 
//Flag for rendering as lines or filled polygons
int fill = 1; //0=OFF 1=ON

//Filename
char * fileName;

oType cube = 
{
    {
        -10, -10, 10,   // vertex v0
        10,  -10, 10,   // vertex v1
        10,  -10, -10,  // vertex v2
        -10, -10, -10,  // vertex v3
        -10, 10,  10,   // vertex v4
        10,  10,  10,   // vertex v5
        10,  10,  -10,  // vertex v6 
        -10, 10,  -10   // vertex v7
    }, 
    {
        0, 1, 4,  // polygon v0,v1,v4
        1, 5, 4,  // polygon v1,v5,v4
        1, 2, 5,  // polygon v1,v2,v5
        2, 6, 5,  // polygon v2,v6,v5
        2, 3, 6,  // polygon v2,v3,v6
        3, 7, 6,  // polygon v3,v7,v6
        3, 0, 7,  // polygon v3,v0,v7
        0, 4, 7,  // polygon v0,v4,v7
        4, 5, 7,  // polygon v4,v5,v7
        5, 6, 7,  // polygon v5,v6,v7
        3, 2, 0,  // polygon v3,v2,v0
        2, 1, 0   // polygon v2,v1,v0
    },
    {
        0.0, 0.0,  // mapping coordinates for vertex v0
        1.0, 0.0,  // mapping coordinates for vertex v1
        1.0, 0.0,  // mapping coordinates for vertex v2
        0.0, 0.0,  // mapping coordinates for vertex v3
        0.0, 1.0,  // mapping coordinates for vertex v4
        1.0, 1.0,  // mapping coordinates for vertex v5
        1.0, 1.0,  // mapping coordinates for vertex v6 
        0.0, 1.0   // mapping coordinates for vertex v7
    },
    0, 
};

void  init() {
    glClearColor(0.0, 0.0, 0.2, 0.0);
    glShadeModel(GL_SMOOTH);
   	
    //Set viewport
    glViewport(0, 0, width, height);  

    //Set perspective
    glMatrixMode(GL_PROJECTION);
    gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 1.0f, 1000.0f);
   
    glEnable(GL_DEPTH_TEST);
    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
    
    glEnable(GL_TEXTURE_2D);

    cube.textureID = loadPPM(fileName); 
}

void resize(int w, int h) {
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, (GLfloat)w/(GLfloat)h, 1.0f, 1000.0f);
    glutPostRedisplay();
}

void keyboard (unsigned char key, int x, int y) {
    switch (key) {
        case ' ':
            rotation_x_increment=0;
            rotation_y_increment=0;
            rotation_z_increment=0;
        break;
        case 'r': case 'R':
            if (fill == 0) {
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                fill = 1;
            }   
            else {
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                fill = 0;
            }
        break;
    }
}

void keyboard_s (int key, int x, int y) {  
    switch (key) {
        case GLUT_KEY_UP:
            rotation_x_increment = rotation_x_increment +0.005;
        break;
        case GLUT_KEY_DOWN:
            rotation_x_increment = rotation_x_increment -0.005;
        break;
        case GLUT_KEY_LEFT:
            rotation_y_increment = rotation_y_increment +0.005;
        break;
        case GLUT_KEY_RIGHT:
            rotation_y_increment = rotation_y_increment -0.005;
        break;
    }
}

void  display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//Set the view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-50);
 
	//Set the cube rotation
    rotation_x = rotation_x + rotation_x_increment;
    rotation_y = rotation_y + rotation_y_increment;
    rotation_z = rotation_z + rotation_z_increment;
    if (rotation_x > 359) rotation_x = 0;
    if (rotation_y > 359) rotation_y = 0;
    if (rotation_z > 359) rotation_z = 0;
    glRotatef(rotation_x,1.0,0.0,0.0);
    glRotatef(rotation_y,0.0,1.0,0.0);
    glRotatef(rotation_z,0.0,0.0,1.0);
    
	//Set active texture
    glBindTexture(GL_TEXTURE_2D, cube.textureID);
	//Draw the cube
    glBegin(GL_TRIANGLES);
    for (int i = 0; i < 12; i++) {
        // Texture coordinates of the first vertex
        glTexCoord2f( cube.texCoord[ cube.triangle[i].a ].u,
                      cube.texCoord[ cube.triangle[i].a ].v);
        // Coordinates of the first vertex
        glVertex3f( cube.vertex[ cube.triangle[i].a ].x,
                    cube.vertex[ cube.triangle[i].a ].y,
                    cube.vertex[ cube.triangle[i].a ].z);
        // Texture coordinates of the second vertex
        glTexCoord2f( cube.texCoord[ cube.triangle[i].b ].u,
                      cube.texCoord[ cube.triangle[i].b ].v);
        // Coordinates of the second vertex
        glVertex3f( cube.vertex[ cube.triangle[i].b ].x,
                    cube.vertex[ cube.triangle[i].b ].y,
                    cube.vertex[ cube.triangle[i].b ].z);
        // Texture coordinates of the third vertex
        glTexCoord2f( cube.texCoord[ cube.triangle[i].c ].u,
                      cube.texCoord[ cube.triangle[i].c ].v);
        // Coordinates of the third vertex
        glVertex3f( cube.vertex[ cube.triangle[i].c ].x,
                    cube.vertex[ cube.triangle[i].c ].y,
                    cube.vertex[ cube.triangle[i].c ].z);
    }
    glEnd();
    glFlush();
    glutSwapBuffers();
}

int main(int argc, char **argv) {
	if (argc > 1) fileName = argv[1];
	else fileName = "fishermen.ppm";
    glutInit(&argc, argv);    
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(width, height);
    glutInitWindowPosition(0,0);
    glutCreateWindow("Texture Mapping Demo");    
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(resize);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(keyboard_s);
    init();
    glutMainLoop();
    return(0);    
}
