
#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include "Simulador_Mart.h"
#include "Interficie_Mart.h"
#include "Texture.h"

extern Visualitzador_Mart *visor_mart;
extern Simulador_Mart *simulador_mart;

GLubyte *texturaBits;
BITMAPINFO *texturaInfo;
CTexture textura;

double x_eye, y_eye, z_eye;
double x_center, y_center, z_center;
double x_up, y_up, z_up;

int Visualitzador_Mart::get_amplada_imatge()
{
   return amplada;
}   

int Visualitzador_Mart::get_altura_imatge()
{
   return altura;
}   

void Visualitzador_Mart::set_amplada_imatge(int width)
{
   this -> amplada = width;
}   

void Visualitzador_Mart::set_altura_imatge(int height)
{
   this -> altura = height;   
}   

int Visualitzador_Mart::get_captura_fotografia()
{
   return captura_fotografia;
}   

void Visualitzador_Mart::set_captura_fotografia(int valor, int camera, unsigned char *referencia_foto)
{
   imatge = referencia_foto;
   camera_captura = camera;
   captura_fotografia = valor;
}   

int Visualitzador_Mart::get_camera_captura()
{
   return camera_captura;
}   

TPosicio *Visualitzador_Mart::get_PosCamara()
{
   return PosCamara;
}   

TPunt_Mapa **Visualitzador_Mart::get_mapa_grafic()
{
   return mapa_grafic;
}   

int Visualitzador_Mart::get_camera_activa()
{
   return camera_activa;
}   

void Visualitzador_Mart::set_camera_activa(int camera)
{
   //camera_activa = camera;
   set_actualitzar(camera);
}   

void Visualitzador_Mart::set_valor_camera(int camera)
{
   camera_activa = camera;
}

int Visualitzador_Mart::get_actualitzar()
{
   return actualitzar;
}   

void Visualitzador_Mart::set_actualitzar(int valor)
{
   mutex_pantalla.lock();
   actualitzar = valor;
   mutex_pantalla.unlock();
} 

Visualitzador_Mart::~Visualitzador_Mart()
{
   delete mapa_grafic;
}   

/* GLUT callback Handlers */

static void reshape(int width, int height)
{
    const float ar = (float) width / (float) height;
    
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)height/(GLfloat)width,1.0,128);
   
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    x_eye = 0.0, y_eye = 9.0, z_eye = 1.0;
    x_center = 0.0, y_center = 0.0, z_center = 0.0;
    x_up = 0.0, y_up = 1.0, z_up = 0.0;

    gluLookAt(x_eye,y_eye,z_eye,x_center,y_center,z_center,x_up,y_up,z_up);
    
    visor_mart -> set_amplada_imatge(width);
    visor_mart -> set_altura_imatge(height);
}

void dibuixar_mapa()
{
   glEdgeFlag(GL_TRUE);

   TPunt_Mapa ** mapa_grafic = visor_mart -> get_mapa_grafic();

   glLoadIdentity();

   TPosicio *PosCamara = visor_mart -> get_PosCamara();
   int camera_activa = visor_mart -> get_camera_activa();

   if(camera_activa != 2 && camera_activa != 3 && camera_activa != 8) {
      glColor3f(1.0,0.0,0.0);
   }
   else {
      glColor3f(1.0,1.0,1.0);      
   } 

   int inc_X = 0;
   int inc_Z = 0;
   int orientacio = PosCamara[camera_activa].angle_orientacio;
   if(orientacio == 45 || orientacio == 90 || orientacio == 135) {
      inc_Z = 1;
   }
   else if(orientacio == 225 || orientacio == 270 || orientacio == 315) {
      inc_Z = -1;
   }
   
   if(orientacio == 0 || orientacio == 45 || orientacio == 315) {
      inc_X = 1;
   }
   else if(orientacio == 135 || orientacio == 180 || orientacio == 225) {
      inc_X = -1;
   }
   
/*   float distan2D = (float) sqrt(
       ((PosCamara[camera_activa].x - PosCamara[camera_activa].x + inc_X )*
        (PosCamara[camera_activa].x - PosCamara[camera_activa].x + inc_X )) + 
       ((PosCamara[camera_activa].z - PosCamara[camera_activa].z + inc_Z )*
        (PosCamara[camera_activa].z - PosCamara[camera_activa].z + inc_Z )));

   float Y_desti = tan(PosCamara[camera_activa].angle_horitzo)*distan2D - 
             PosCamara[camera_activa].y;*/
   
   float Y_desti = simulador_mart -> get_altura(int(PosCamara[camera_activa].x) + inc_X, int(PosCamara[camera_activa].z) + inc_Z);
   inc_X *= FACTOR_ESCALA_ALTURA;
   inc_Z *= FACTOR_ESCALA_ALTURA;

   gluLookAt(PosCamara[camera_activa].x, PosCamara[camera_activa].y*2, PosCamara[camera_activa].z,
             PosCamara[camera_activa].x + inc_X, Y_desti*2, PosCamara[camera_activa].z + inc_Z,
             0,1,0);
 
   for (int i = 0; i < simulador_mart -> get_num_files_mapa() - 1; i++) {
      for (int j = 0; j < simulador_mart -> get_num_columnes_mapa() - 1; j++) {
         for (int k = 0; k < 2; k ++) {
            if (k == 0) { //std::cout << "K = 0\n";
               glBegin(GL_TRIANGLES);
               //glColor3f(1.0,0.0,0.0);
               glTexCoord2f(0.0,1.0);
               glVertex3f(i,mapa_grafic[i][j].z*2,j);//visor_mart -> mapa_grafic[i][j].z);
               glTexCoord2f(1.0,1.0);
               glVertex3f(i,mapa_grafic[i][j + 1].z*2,(j+1));//visor_mart -> mapa_grafic[i][j + 1].z);
               glTexCoord2f(0.5,0.0);
               glVertex3f((i + 1),mapa_grafic[i + 1][j].z*2,j);//visor_mart -> mapa_grafic[i + 1][j].z);
               glEnd();
            }   
            else { 
               glBegin(GL_TRIANGLES);
               //glColor3f(1.0,0.0,0.0);
               glTexCoord2f(0.0,1.0);
               glVertex3f((i + 1),mapa_grafic[i + 1][j].z*2,j);//visor_mart -> mapa_grafic[i + 1][j].z);
               glTexCoord2f(1.0,1.0);
               glVertex3f(i,mapa_grafic[i][j + 1].z*2,(j + 1));//visor_mart -> mapa_grafic[i][j + 1].z);
               glTexCoord2f(0.5,0.0);
               glVertex3f((i + 1),mapa_grafic[i + 1][j + 1].z*2,(j + 1));//visor_mart -> mapa_grafic[i + 1][j + 1].z);
               glEnd();
            }   
         }   
      }   
   }

}

static void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   //glColor3d(1,0,0);

   glPushMatrix();
   dibuixar_mapa();
   glPopMatrix();

   glutSwapBuffers();
}

static void key(unsigned char key, int x, int y)
{
   switch (key) 
   {
      case '1':
         visor_mart -> set_camera_activa(0);
         break;
      case '2':
         visor_mart -> set_camera_activa(1);
         break;
      case '3':
         visor_mart -> set_camera_activa(2);
         break;
      case '4':
         visor_mart -> set_camera_activa(3);
         break;
      case '5':
         visor_mart -> set_camera_activa(4);
         break;                           
      case '6':
         visor_mart -> set_camera_activa(5);
         break;
      case '7':
         visor_mart -> set_camera_activa(6);
         break;         
      case '8':
         visor_mart -> set_camera_activa(7);
         break;
      case '9':
         visor_mart -> set_camera_activa(8);
         break;         
      case 27:
         exit(0);
         break;
    }
    //glutPostRedisplay();
}

static void idle(void)
{
    if (visor_mart -> get_captura_fotografia()) 
    {
       visor_mart -> capturar_pantalla(visor_mart -> get_camera_captura());
       visor_mart -> set_captura_fotografia(0,1,NULL);
    }
    int actualitzar = visor_mart -> get_actualitzar();
    if(actualitzar >= 0) {
       visor_mart -> set_valor_camera(actualitzar);
       textura.Destroy();
       if(actualitzar == 8) {
          textura.Create("xray.bmp");
       } 
       else if(actualitzar == 2 || actualitzar == 3) {
          textura.Create("black.bmp");
       } 
       else {
          textura.Create("textura_mart.bmp");
       } 
       //glutPostRedisplay();
       visor_mart -> set_actualitzar(-1);
       glutPostRedisplay();
    }
}

Visualitzador_Mart::Visualitzador_Mart()
{
   camera_activa = 0;
   amplada = 640;
   altura = 480;
   captura_fotografia = 0;
   camera_captura = 1;
   actualitzar = -1;
   // std::cout << "files: " << simulador_mart -> get_num_files_mapa() << " columnes: " << simulador_mart -> get_num_columnes_mapa(); 
   mapa_grafic = (TPunt_Mapa **)malloc(simulador_mart -> get_num_files_mapa() * sizeof(TPunt_Mapa *));
   for (int i = 0; i < simulador_mart -> get_num_files_mapa(); i++) {
      mapa_grafic[i] = (TPunt_Mapa *)malloc(simulador_mart -> get_num_columnes_mapa() * sizeof(TPunt_Mapa));
   }
   for(int i = 0; i < simulador_mart -> get_num_files_mapa(); i++) {
      for(int j = 0; j < simulador_mart -> get_num_columnes_mapa(); j++) {
         mapa_grafic[i][j].z = simulador_mart -> get_altura(i,j);
         mapa_grafic[i][j].color = simulador_mart -> get_material(i,j);
      }   
   }   
}   

const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };

void Visualitzador_Mart::iniciar_visualitzacio(int principal_argc, char **principal_argv)
{
    glutInit(&principal_argc, principal_argv);
    glutInitWindowSize(640,480);
    glutInitWindowPosition(10,10);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutCreateWindow("Practica Sistemes Informatics I - Mars Rovers");

    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);

    glClearColor(0.656,0.695,0.247,0);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

    glPolygonMode(GL_FRONT, GL_FILL);

    // Inicialitzacio i configuracio del Mapeig de Textures
    bool ok = textura.Create("textura_mart.bmp");
    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_WRAP_S,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);    
    glEnable(GL_TEXTURE_2D);

    // Bucle infinit que manega els events de OpenGL
    glutMainLoop();
}   

void Visualitzador_Mart::inicialitzar_cameres(int num_camera, float x, float y, float z, 
                                            int orientacio, int horitzo)
{
   if (num_camera >= 1 && num_camera <= 9) 
   {
      PosCamara[num_camera-1].x = x;
      PosCamara[num_camera-1].y = y;
      PosCamara[num_camera-1].z = z;
      PosCamara[num_camera-1].angle_orientacio = orientacio;
      PosCamara[num_camera-1].angle_horitzo = horitzo;

      std::cout << "CAMERA " << num_camera << " - ";
      std::cout << " x: " << x << ", ";
      std::cout << " y: " << y << ", ";
      std::cout << " z: " << z << ", ";
      std::cout << " orientacio: " << orientacio << ", ";
      std::cout << " horitzo: " << horitzo << "\n";
   }   
}   
 

void Visualitzador_Mart::actualitzar_camera(int num_camera, float x, float y, float z, 
                                            int orientacio, int horitzo)
{
   if (num_camera >= 1 && num_camera <= 9) 
   {
      // Capturem nova posició per la camara
       PosCamara[num_camera-1].x = x;
       PosCamara[num_camera-1].y = y;
       PosCamara[num_camera-1].z = z;
       PosCamara[num_camera-1].angle_orientacio = orientacio;
       PosCamara[num_camera-1].angle_horitzo = horitzo;
          
       glutPostRedisplay();
    }
}    

void Visualitzador_Mart::capturar_pantalla(int camera)
{
   //int camera_anterior = visor_mart -> camera_activa;
   //visor_mart -> camera_activa = camera;

   // Refresc de pantalla
   //glutPostRedisplay();
   //set_actualitzar(1);
 
   int altura = visor_mart -> get_altura_imatge();
   int amplada = visor_mart -> get_amplada_imatge();
 
   glPixelStorei(GL_PACK_ALIGNMENT,1);
   glReadBuffer(GL_BACK_LEFT);
   glReadPixels(0,0,amplada,altura,GL_RGB,GL_UNSIGNED_BYTE,imatge);
   
   //visor_mart -> camera_activa = camera_anterior;

   // Refresc de pantalla
   //glutPostRedisplay();
   //set_actualitzar(1);
}   

void Visualitzador_Mart::set_Missatge(char *missatge)
{
   
}


