/***********************************
 * CC52B: proyecto estudio virtual *
 * Daniel Gomez M.         *
 ***********************************/

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

#define TRUE 1
#define FALSE 0
#define PI 3.14159

static float eyex = 0.0, eyey = 0.0, eyez = 3.0;
static float fyex = 0.0, fyey = 0.0, fyez = 3.0;
static float xcam = 0.0, zcam = 1.0, teta = 0.0;
static float xram = 0.0, zram = 1.0;
static float zoom    = 1;
static int rot       = 0;
static int status    = 0;
static int winIdShot = 0;
static int winIdMain;
static int luzAlfa   = FALSE;
static int luzBeta   = FALSE;

/* definicion de parametros iluminacion *********************************** */
    // definimos valores de luz difusa, máxima intensidad de luz blanca
    GLfloat luzdifusa[]   = { 0.05, 0.05, 0.05, 1 };
    // definimos valores de luz ambiente
    GLfloat luzambiente[] = { 0.5, 0.5, 0.5, 1 };
    // definimos el valor specular de la luz
    GLfloat luzspecular[] = { 0.0, 0.0, 0.0, 0.0};
    // definimos el valor de emisión de la luz
    GLfloat luzemision[]  = { 0.0, 0.0, 0.0, 0.0};
    // la posición de la luz
    GLfloat posicion[]    = { 0.0, 3.0, 0.0 , 1.0};

    GLfloat shininess [] = {2};
    GLfloat kc           = 1.0;
    GLfloat cutoff       = 60.0;

/* definicion funciones *************************************************** */
    void atril(void);
    void viga(void);
    void camara(void);
    void mesa(void);
    void shot(void);
    void reshape (int w, int h);
    void keyboard(unsigned char key, int x, int y);

/* funciones iluminacion ************************************************** */
/* foco alfa */
void alfaOn(void) {
    GLfloat light_alfa_position []       = {-1.2, 2.0, -2.0, 1.0};
    GLfloat light_alfa_difusse []        = {0.8, 0.8, 0.2, 1.0};
    GLfloat light_alfa_specular []       = {1.0, 0.8, 0.8, 1.0};
    GLfloat light_alfa_spot_direction [] = {-0.8, -1.0, -1.0};

    glLightfv(GL_LIGHT1, GL_POSITION, light_alfa_position);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light_alfa_difusse);
    glLightfv(GL_LIGHT1, GL_SPECULAR, light_alfa_specular);
    glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_alfa_spot_direction);
    glLightf( GL_LIGHT1, GL_SPOT_CUTOFF, cutoff);
    glLightf( GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);
    glEnable (GL_LIGHT1);
    luzAlfa = TRUE;
}

void alfaOff(void) {
    glDisable (GL_LIGHT1);
    luzAlfa = FALSE;
}

void incAlfa(void) {
        if (kc> 1.0) {
                kc = kc -0.5;
                glLightf(GL_LIGHT1,GL_CONSTANT_ATTENUATION, kc);
        }
}

void desAlfa (void) {
        if (kc< 10.0) {
                kc = kc +0.5;
                glLightf(GL_LIGHT1,GL_CONSTANT_ATTENUATION, kc);
        }
}


/* foco beta */
void betaOn(void) {
    GLfloat light_beta_position []       = {1.2, 2.0, -2.8, 1.0};
    GLfloat light_beta_difusse []        = {0.5, 0.5, 0.5, 1.0};
    GLfloat light_beta_specular []       = {1.0, 0.8, 0.8, 1.0};
    GLfloat light_beta_spot_direction [] = {1.0, -1.0, -1.0};

    glLightfv(GL_LIGHT2, GL_POSITION, light_beta_position);
    glLightfv(GL_LIGHT2, GL_DIFFUSE, light_beta_difusse);
    glLightfv(GL_LIGHT2, GL_SPECULAR, light_beta_specular);
    glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, light_beta_spot_direction);
    glLightf (GL_LIGHT2, GL_SPOT_CUTOFF, cutoff);
    glLightf (GL_LIGHT2, GL_SPOT_EXPONENT, 2.0);
    glEnable (GL_LIGHT2);
    luzBeta = TRUE;
}

void betaOff(void) {
    glDisable (GL_LIGHT2);
    luzBeta = FALSE;
}

void incBeta(void) {
        if (kc> 1.0) {
                kc = kc -1.0;
                glLightf(GL_LIGHT2,GL_CONSTANT_ATTENUATION, kc);
        }
}

void desBeta (void) {
        if (kc< 10.0) {
                kc = kc +1.0;
                glLightf(GL_LIGHT2,GL_CONSTANT_ATTENUATION, kc);
        }
}


/* init & display ********************************************************* */
void init(void) {
    // ahora vamos a inicializar las luces.
    glLightfv(GL_LIGHT0, GL_DIFFUSE, luzdifusa);
    glLightfv(GL_LIGHT0, GL_AMBIENT, luzambiente);
    glLightfv(GL_LIGHT0, GL_SPECULAR, luzspecular);
    glLightfv(GL_LIGHT0, GL_EMISSION, luzemision);
    glLightfv(GL_LIGHT0, GL_SHININESS, shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, posicion);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel (GL_FLAT);
}

/* rutina principal de dibujo */
void display(void) {
    int k = 0;
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    
    //Luz ambiente blanca.
    GLfloat luz_ambiental[] = {0,0,0,0};
    glLightModelfv (GL_LIGHT_MODEL_AMBIENT, luz_ambiental);
    glShadeModel(GL_SMOOTH);

    GLfloat mat_R[] = { 0.8, 0.0, 0.0, 0.5f};
    GLfloat mat_G[] = {0.0, 0.8, 0.0, 0.5f};
    GLfloat mat_B[] = {0.2, 0.2, 0.4, 0.5f};
    GLfloat mat_0[] = {0.5, 0.5, 0.5, 0.5f};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glLoadIdentity ();
    glPushMatrix();
    glLoadIdentity ();
    gluLookAt(eyex, eyey, eyez, 0.0, -0.6, -3.0, 0.0, 1.0, 0.0);
    glRotatef ((GLfloat) rot, 1.0, 1.0, 1.0);

    /* suelo */
    glPushMatrix();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_B);
        glTranslatef (0, -1.25, -0.01);
        glScalef (3, 0.02, 6);
        glutSolidCube (1.0);
    glPopMatrix();


    /* muros */
    glPushMatrix();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_0);
        glTranslatef (-1.5, 0, 0);
        glScalef (0.05, 2.5, 6);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix();
        glTranslatef (0, 0, -3);
        glScalef (3, 2.5, 0.05);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix();
        glTranslatef (1.5, 0, 0);
        glScalef (0.05, 2.5, 6);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix();
        glTranslatef (0, 1, 3);
        glScalef (3, 0.5, 0.05);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix();
        glTranslatef (-0.65, -0.25, 3);
        glScalef (1.7, 2.0, 0.05);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix();
        glTranslatef (1.25, -0.25, 3);
        glScalef (0.5, 2.0, 0.05);
        glutSolidCube (1.0);
    glPopMatrix();

    /* vigas */
    glPushMatrix();
        glTranslatef(0.0, 1.3, -3.0);
        for(k=0; k<=10; ++k){
            glTranslatef(0.0, 0.0, k * 0.6);
            viga();
            glTranslatef(0.0, 0.0, -k * 0.6);
        }
    glPopMatrix();

    /* cuadros */
    glPushMatrix();
        glTranslatef(1.5, 0.0, 0.0);
        glScalef(0.1, 0.5, 1.0);
        glutSolidCube(1);
    glPopMatrix();
    glPushMatrix();
        glTranslatef(1.5, 0.0, 1.5);
        glScalef(0.1, 0.5, 1.0);
        glutSolidCube(1);
    glPopMatrix();
    glPushMatrix();
        glTranslatef(-1.5, -0.2, -0.0);
        glScalef(0.1, 1.0, 0.5);
        glutSolidCube(1);
    glPopMatrix();
    glPushMatrix();
        glTranslatef(-1.5, 0.3, 1.0);
        glScalef(0.1, 1.0, 0.5);
        glutSolidCube(1);
    glPopMatrix();

    /* atriles */
    glTranslatef(-1.2, -0.55, -2.0);
    glScalef(1.0, 2.5, 1.0);
    atril();
        glTranslatef(0.0, 0.08, 0.0);
        glPushMatrix();
            glScalef (0.03, 1.0, 0.03);
            glutSolidCube (0.3);
        glPopMatrix();
        glTranslatef(0.0, -0.08, 0.0);
    glScalef(1.0, 0.4, 1.0);
    glTranslatef(1.2, 0.55, 2.0);

    glPushMatrix();
        glTranslatef(-1.2, 0.0, -2.0);
        glScalef(1.5, 0.2, 0.2);
        glRotatef(90, -0.0, 1, 0);
        glutSolidTorus(0.1, 0.2, 8, 8);
    glPopMatrix();

    glTranslatef(1.2, -0.55, -2.8);
    glScalef(1.0, 2.5, 1.0);
    atril();
        glTranslatef(0.0, 0.08, 0.0);
        glPushMatrix();
        glScalef (0.03, 1.0, 0.03);
        glutSolidCube (0.3);
        glPopMatrix();
        glTranslatef(0.0, -0.08, 0.0);
    glScalef(1.0, 0.4, 1.0);
    glTranslatef(-1.2, 0.55, 2.8);

    glPushMatrix();
        glTranslatef(0.8, 0.0, -2.5);
        glRotatef(120, 0, 1, 0);
        glutWireCone(0.5, 0.4, 40, 20);
    glPopMatrix();

    /* objeto modelo */
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_0);
    glTranslatef(0.0, -1.0, -2.5);
    mesa();
    glTranslatef(0.0, 1.0, 2.5);

    glTranslatef(0.0, -0.60, -2.5);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_B);
    glColor3f(0.2f,0.6f,0.0f);
    glRotatef(180, 0, 1, 0);
    glutSolidTeapot(0.2);
    glRotatef(-180, 0, 1, 0);
    glTranslatef(0.0, 0.55, 2.5);

    glTranslatef(xcam, -0.25, zcam);
    glPushMatrix();
        glPushMatrix();
        glScalef(1.0, 3.5, 1.0);
        atril();
        glPopMatrix();
        glPushMatrix();
        glTranslatef(0.0, 0.0, 0.0);
        camara();
        glPopMatrix();
    glPopMatrix();
    glTranslatef(-xcam, 0.25, -zcam);


    glPopMatrix();
    glutSwapBuffers();
}

/* captura de la "foto" */
void displayShot(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glLoadIdentity ();
    
    fyex = eyex; fyey = eyey; fyez = eyez;
    eyex = xram; eyey = -0.25; ; eyez = zram;

    if(luzAlfa == TRUE) alfaOn(); else alfaOff();
    if(luzBeta == TRUE) betaOn(); else betaOff();
    
    // dibujo de la ventana secundaria:
    display();
    eyex = fyex; eyey = fyey; eyez = fyez;
    glutSwapBuffers();
}

/* dibujo de un tripode */
void atril(void){
    glPushMatrix();
    glColor3f(0.2f,0.2f,0.2f);
    glRotatef (30, 0.0, 0.0, 1.0);
    glPushMatrix();
        glTranslatef(0.0, -0.18, 0.0);
        glScalef (0.03, 1.0, 0.03);
        glutSolidCube (0.3);
    glPopMatrix();
    glRotatef (-30, 0.0, 0.0, 1.0);
    
    glRotatef (120, 0.0, 1.0, 0.0);
    glRotatef (30, 0.0, 0.0, 1.0);
    glPushMatrix();
        glTranslatef(0.0, -0.18, 0.0);
        glScalef (0.03, 1.0, 0.03);
        glutSolidCube (0.3);
    glPopMatrix();
    glRotatef (-30, 0.0, 0.0, 1.0);
    glRotatef (-120, 0.0, 1.0, 0.0);

    glRotatef (240, 0.0, 1.0, 0.0);
    glRotatef (30, 0.0, 0.0, 1.0);
    glPushMatrix();
        glTranslatef(0.0, -0.18, 0.0);
        glScalef (0.03, 1.0, 0.03);
        glutSolidCube (0.3);
    glPopMatrix();
    glRotatef (-30, 0.0, 0.0, 1.0);
    glRotatef (-240, 0.0, 1.0, 0.0);
    glPopMatrix();
    glPushMatrix();
    glColor3f(0.0f,0.0f,0.0f);
    glRotatef(90, 1.0, 0.0, 0.0);
    glTranslatef(0.0, 0.0, 0.02);
    glScalef(0.1, 0.1, 0.1);
    glutSolidTorus(0.1, 0.2, 8, 8);
    glPopMatrix();
}

/* viga */
void viga(void){
    glPushMatrix();
        glScalef(3.0, 0.2, 0.2);
        glutSolidCube(1.0);
    glPopMatrix();
}

/* dibuja la camara */
void camara(void){
    glPushMatrix(); // cuerpo camara
        glColor3f(0.2f,0.2f,0.2f);
        glScalef (0.3, 0.2, 0.1);
        glutSolidCube(1.0);
        glTranslatef(0.0, 0.5, 0.0);
        glScalef (0.3, 0.1, 0.1);
        glutSolidCube(1.0);
    glPopMatrix();
    glPushMatrix(); // pantalla camara
        glTranslatef(0.0, 0.0, 0.1);
        glColor3f(0.8, 0.8, 0.4);
        glScalef (0.07 * zoom, 0.04 * zoom, 0.01);
        glutSolidCube (1.0);
    glPopMatrix();
    glPushMatrix(); // lente
        glTranslatef(0.0, 0.0, -0.1);
        //glRotatef(90, 1.0, 0.0, 0.0);
        glColor3f(0.3, 0.2, 0.1);
        glScalef (0.4, 0.4, 1);
        glutSolidTorus(0.1, 0.2, 8, 16);
    glPopMatrix();
}

/* dibuja un cubo de "mesa" */
void mesa(void){
    glPushMatrix();
        glColor3f(0.6f,0.6f,0.0f);
        glScalef (0.5, 0.5, 0.5);
        glutSolidCube (1.0);
    glPopMatrix();
}

/* captura de imagen por la camara virtual */
void shot(void){
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize (320, 240);
    glutInitWindowPosition (200, 200);
    winIdShot = glutCreateWindow ("foto");
    init ();
    glutDisplayFunc(displayShot);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
}

void reshape(int w, int h) {
    glViewport (0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/* manejo del teclado con foco en la ventana principal */
void keyboard(unsigned char key, int x, int y) {
    int k;
    switch (key) {
        case 'x': eyex -= 0.1;
              if(eyex < -1.5) eyex = -1.5;
              glutPostRedisplay(); break;
        case 'X': eyex += 0.1;
              if(eyex > 1.5) eyex = 1.5;
              glutPostRedisplay(); break;
        case 'y': eyey -= 0.1;
              if(eyey < -1.2) eyey = -1.2;
              glutPostRedisplay(); break;
        case 'Y': eyey += 0.1;
              if(eyey > 4.0) eyey = 4.0;
              glutPostRedisplay(); break;
        case 'z': eyez -= 0.1;
              glutPostRedisplay(); break;
        case 'Z': eyez += 0.1;
              glutPostRedisplay(); break;
        case 'a': alfaOn();
                  glutPostRedisplay(); break;
        case 'A': alfaOff();
                  glutPostRedisplay(); break;
        case 'b': betaOn();
                  glutPostRedisplay(); break;
        case 'B': betaOff();
                  glutPostRedisplay(); break;
        case '+': incAlfa(); incBeta();
                  glutPostRedisplay(); break;
        case '-': desAlfa(); desBeta();
                  glutPostRedisplay(); break;
        case 27:  exit(0); break;
        case ' ': if(status == TRUE){ glutDestroyWindow(winIdShot); }
                  else { shot();} 
                  status = !status;
                  break;
        default: break;
    }
}

void especial(int key, int x, int y){
    switch (key) {
        case GLUT_KEY_DOWN: zoom -= 0.1;
                            if(zoom < 1) zoom = 1;
                            break;
        case GLUT_KEY_UP: zoom += 0.1;
                          if(zoom > 3) zoom = 3;
                          break;
        case GLUT_KEY_LEFT:  teta -= 1;
                             if(teta < -18) teta = -18;
                             break;
        case GLUT_KEY_RIGHT: teta += 1;
                             if(teta > 18) teta = 18;
                             break;
    }
    xcam = 4.0 * sin(PI * teta / 180);
    xram = (4.0/zoom) * sin(PI * teta / 180);
    zcam = -3.0 + 4.0 * cos(PI * teta / 180);
    zram = -3.0 + (4.0/zoom) * cos(PI * teta / 180);
    glutPostRedisplay();
}


int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize (800, 600);
    glutInitWindowPosition (100, 100);
    winIdMain = glutCreateWindow (argv[0]);
    init ();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(especial);
    glutMainLoop();
    return 0;
}
