/* Mathematiques de l'Infographie               */
/* Test des classes TG3D, Tr3D, Rt3D et Sc3D    */
/*                                              */
/* Auteur: Nicolas JANEY                        */
/* nicolas.janey@univ-fcomte.fr                 */
/* Mars 2020                                    */

#if defined(WIN32) || defined(WIN64)
  #define _CRTDBG_MAP_ALLOC
  #if defined(_DEBUG)
    #define _AFXDLL
    #include <afx.h>
  #endif
#endif
#include <stdlib.h>
#if defined(WIN32) || defined(WIN64)
  #include <crtdbg.h>
#endif

#include <stdio.h>
#include <math.h>

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

#include "Pos3D.h"
#include "Dir3D.h"
#include "Tr3D.h"
#include "Rt3D.h"
#include "Sc3D.h"
#include "TG3D.h"

//////////////////////////////////////////////////

static Dir3D tr;
static float angle = 0.0F;
static Dir3D axeR(1.0F,0.0F,0.0F);
static float scx = 1.0F;
static float scy = 1.0F;
static float scz = 1.0F;

/* Modelisation en fil de fer d'un cube         */
/* transforme par la TG3D tg                    */

static void modeliseCubeEnFilDeFer(TG3D *tg) {
  Pos3D pCube[8] = { Pos3D( 0.5F, 0.5F, 0.5F),
                     Pos3D(-0.5F, 0.5F, 0.5F),
                     Pos3D( 0.5F,-0.5F, 0.5F),
                     Pos3D( 0.5F, 0.5F,-0.5F),
                     Pos3D( 0.5F,-0.5F,-0.5F),
                     Pos3D(-0.5F, 0.5F,-0.5F),
                     Pos3D(-0.5F,-0.5F, 0.5F),
                     Pos3D(-0.5F,-0.5F,-0.5F) };
  int iCube [12][2] = { { 0,1 },
                        { 0,2 },
                        { 0,3 },
                        { 1,6 },
                        { 1,5 },
                        { 2,4 },
                        { 2,6 },
                        { 3,4 },
                        { 3,5 },
                        { 7,6 },
                        { 7,5 },
                        { 7,4 } } ;
  glBegin(GL_LINES);
  for ( int i = 0 ; i < 12 ; i++ ) {
    Pos3D p0(pCube[iCube[i][0]]);
    Pos3D p1(pCube[iCube[i][1]]);
    p0.transformation(tg);
    p1.transformation(tg);
    glVertex3f(p0.x,p0.y,p0.z);
    glVertex3f(p1.x,p1.y,p1.z); } 
  glEnd();
}

/* Calcul et retour de la TG3D obtenue          */
/* par composition                              */
/* de la translation de direction tr,           */
/* de la rotation de angle degres               */
/* autour de l'axe de direction axe             */
/* passant par l'origine,                       */
/* et du zoom de rapports rx, ry et rz          */

static TG3D *transformationModelisation(Dir3D *tr,
                                        float angle,Dir3D *axe,
                                        float rx,float ry,float rz) {
  Tr3D translation(tr);
  Rt3D rotation(angle,axe);
  Sc3D scale(rx,ry,rz);
  TG3D tg(&translation,&rotation);
  return(new TG3D(&tg,&scale));
}

/* Fonction executee lors d'un rafraichissement */
/* de la fenetre de dessin                      */

static void display(void) {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  TG3D *trz = transformationModelisation(&tr,angle,&axeR,scx,scy,scz);
  modeliseCubeEnFilDeFer(trz);
  delete(trz);
  glPopMatrix();
  glutSwapBuffers();
  int error = glGetError();
  if ( error != GL_NO_ERROR )
    printf("Erreur OpenGL: %d\n",error);
}

//////////////////////////////////////////////////

/* Fonction d'initialisation des parametres     */
/* OpenGL ne changeant pas au cours de la vie   */
/* du programme                                 */

void initialisationsOpenGL(void) {
  glClearColor(1.0F,1.0F,1.0F,1.0F);
  glColor3f(0.0F,0.0F,0.0F);
}

/* Fonction executee lors de la frappe          */
/* d'une touche du special clavier              */

static void special(int code,int x,int y) {
  switch ( code ) {
    case GLUT_KEY_RIGHT :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scx += 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.x += 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.x += 0.01F;
          break;
        default :
          angle += 1.0F; }
      glutPostRedisplay();
      break;
    case GLUT_KEY_LEFT :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scx -= 0.01F;
          if ( scx <= 0.0F )
            scx = 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.x -= 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.x -= 0.01F;
          break;
        default :
          angle -= 1.0F; }
      glutPostRedisplay();
      break;
    case GLUT_KEY_UP :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scy += 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.y += 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.y += 0.01F;
          break;
        default :
          angle += 1.0F; }
      glutPostRedisplay();
      break;
    case GLUT_KEY_DOWN :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scy -= 0.01F;
          if ( scy <= 0.0F )
            scy = 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.y -= 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.y -= 0.01F;
          break;
        default :
          angle -= 1.0F; }
      glutPostRedisplay();
      break;
    case GLUT_KEY_PAGE_UP :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scz += 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.z += 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.z += 0.01F;
          break;
        default :
          angle += 1.0F; }
      glutPostRedisplay(); 
      break;
    case GLUT_KEY_PAGE_DOWN :
      switch ( glutGetModifiers() ) {
        case GLUT_ACTIVE_ALT :
          scz -= 0.01F;
          if ( scz <= 0.0F )
            scz = 0.01F;
          break;
        case GLUT_ACTIVE_SHIFT :
          tr.z -= 0.01F;
          break;
        case GLUT_ACTIVE_CTRL :
          axeR.z -= 0.01F;
          break;
        default :
          angle -= 1.0F; }
      glutPostRedisplay();
      break; }
}

/* Fonction executee lors de la frappe          */
/* d'une touche du clavier                      */

static void keyboard(unsigned char key,int x,int y) {
  switch ( key ) {
    case 0x1B :
      exit(0); 
      break; }
}

/* Fonction executee lors d'un changement       */
/* de la taille de la fenetre OpenGL            */

void reshape(int wx,int wy) {
  glViewport(0,0,wx,wy); 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-wx/200.0,wx/200.0,-wy/200.0,wy/200.0,-1000.0,1000.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static void clean() {
}

/* Fonction principale                          */

int main(int argc,char **argv) {

#if defined(WIN32) || defined(WIN64)
  #if defined(_DEBUG)
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    //_crtBreakAlloc = 119;
  #endif
#endif

  atexit(clean);

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  glutInitWindowSize(450,300);
  glutInitWindowPosition(50,50); 
  glutCreateWindow("Transformations géométriques sur un cube en fil de fer");
  initialisationsOpenGL();
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
  glutSpecialFunc(special);
  glutDisplayFunc(display);
  glutInitWindowPosition(250,100);
  glutMainLoop();
  return(0);
}

