/* Courbes B-Spline et de Bezier                */
/*                                              */
/* Auteur: Nicolas JANEY                        */
/* nicolas.janey@univ-fcomte.fr                 */
/* Mars 2019                                    */

#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 <stdlib.h>
#include <math.h>

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

#include "Pos3D.h"
#include "LignePolygonale3D.h"
#include "MatriceDeBase.h"

/* Variables et constantes globales             */

static const float blanc[] = { 1.0F,1.0F,1.0F,1.0F };
static const float gris[]  = { 0.7F,0.7F,0.7F,1.0F };
static float rx = 0.0F;
static float ry = 0.0F;
static float rz = 0.0F;
static int aff = 0;

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

static int nbp = 100;
static LignePolygonale3D *lp = NULL;

/* Scene dessinee                                */

static void scene(void) {
  switch (aff) {
    case 0 :
      glColor3f(0.75F,0.75F,0.75F);
      lp->drawOpenGL(GL_LINE_STRIP);
      break;
    case 1 :
      glColor3f(0.75F,0.75F,0.75F);
      glPointSize(3.0);
      lp->drawOpenGL(GL_LINE_STRIP);
      glColor3f(1.0F,1.0F,1.0F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp,&MatriceDeBase::NRUBS);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      break;
    case 2 :
      glColor3f(0.75F,0.75F,0.75F);
      lp->drawOpenGL(GL_LINE_STRIP);
      glPointSize(3.0);
      glColor3f(1.0F,1.0F,1.0F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp,&MatriceDeBase::CATMULL_ROM);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      break;
    case 3 :
      glColor3f(0.75F,0.75F,0.75F);
      glPointSize(3.0);
      lp->drawOpenGL(GL_LINE_STRIP);
      glColor3f(1.0F,1.0F,1.0F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      break;
    case 4 :
      glLineWidth(2.0F);
      glColor3f(0.75F,0.75F,0.75F);
      lp->drawOpenGL(GL_LINE_STRIP);
      glLineWidth(1.0F);
      glColor3f(0.0F,1.0F,1.0F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp,&MatriceDeBase::NRUBS);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      glColor3f(1.0F,0.75F,0.75F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp,&MatriceDeBase::CATMULL_ROM);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      glColor3f(0.0F,1.0F,0.0F);
      { LignePolygonale3D *lpl = new LignePolygonale3D(nbp,lp);
        lpl->drawOpenGL(GL_POINTS);
        delete(lpl); }
      break; }
}

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

/* Fonction executee lors de l'appui             */
/* d'une touche de curseur ou d'une touche       */
/* page up ou page down                          */

static void special(int key,int x,int y) {
  switch (key) {
    case GLUT_KEY_UP :
      rx++;
      glutPostRedisplay();
      break;
    case GLUT_KEY_DOWN :
      rx--;
      glutPostRedisplay();
      break;
    case GLUT_KEY_LEFT :
      ry++;
      glutPostRedisplay();
      break;
    case GLUT_KEY_RIGHT :
      ry--;
      glutPostRedisplay();
      break;
    case GLUT_KEY_PAGE_UP :
      rz++;
      glutPostRedisplay();
      break;
    case GLUT_KEY_PAGE_DOWN :
      rz--;
      glutPostRedisplay();
      break; }
}

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

static void init(void) {
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);
}

/* 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();
  glRotatef(rx,1.0F,0.0F,0.0F);
  glRotatef(ry,0.0F,1.0F,0.0F);
  glRotatef(rz,0.0F,0.0F,1.0F);
  glTranslatef(0.0F,-2.5F,0.0F);
  scene();
  glPopMatrix();
  glFlush();
  glutSwapBuffers();
  int error = glGetError();
  if ( error != GL_NO_ERROR )
    printf("Erreur OpenGL: %d\n",error);
}

/* Fonction executee lors d'un changement       */
/* de la nbPoints de la fenetre OpenGL            */
/* -> Ajustement de la camera de visualisation  */

static void reshape(int tx,int ty) {
  glViewport(0,0,tx,ty); 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  double ratio =(double) tx/ty;
  if ( ratio >= 1.0 )
    glOrtho(-3.5*ratio,3.5*ratio,-3.5,3.5,-10.0,10.0);
    else
    glOrtho(-3.5,3.5,-3.5/ratio,3.5/ratio,-10.0,10.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

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

static void keyboard(unsigned char key,int x,int y) {
  switch (key) {
    case 45 :
      nbp--;
      if ( nbp == 1 )
        nbp = 2;
      glutPostRedisplay(); 
      break;
    case 43 :
      nbp++;
      glutPostRedisplay(); 
      break;
    case 0x20 :
      aff = (aff+1)%5;
      glutPostRedisplay();
      break;
    case 0x1B :
      exit(0); 
      break; }
}

static void clean(void) {
  delete(lp);
}

static void initLignePolygonale3D(void) {
  int nbPoints = 37;
  Pos3D *tPos[] = { new Pos3D( 6.5F, 4.9F,-3.00F), new Pos3D( 7.0F, 5.0F,-3.00F), new Pos3D( 7.5F, 4.9F,-2.00F),
                    new Pos3D( 8.0F, 4.6F, 0.00F), new Pos3D( 7.5F, 4.4F, 2.00F), new Pos3D( 7.0F, 4.2F, 3.50F),
                    new Pos3D( 6.0F, 3.8F, 4.00F), new Pos3D( 5.0F, 2.8F, 4.00F), new Pos3D( 4.0F, 1.8F, 4.00F),
                    new Pos3D( 3.0F, 0.8F, 4.00F), new Pos3D( 0.0F, 0.0F, 4.00F), new Pos3D(-1.4F, 0.7F, 3.75F),
                    new Pos3D(-2.0F, 2.0F, 3.50F), new Pos3D(-1.4F, 3.3F, 3.25F), new Pos3D( 0.0F, 4.0F, 3.00F),
                    new Pos3D( 1.4F, 3.3F, 2.75F), new Pos3D( 2.0F, 2.0F, 2.50F), new Pos3D( 1.0F, 0.7F, 2.25F),
                    new Pos3D(-1.0F, 0.0F, 2.00F), new Pos3D(-2.4F, 0.7F, 1.75F), new Pos3D(-3.0F, 2.0F, 1.50F),
                    new Pos3D(-2.4F, 3.3F, 1.25F), new Pos3D(-1.0F, 4.0F, 1.00F), new Pos3D( 0.4F, 3.3F, 0.75F),
                    new Pos3D( 1.0F, 2.0F, 0.50F), new Pos3D( 0.0F, 0.7F, 0.25F), new Pos3D(-2.0F, 0.2F, 0.00F),
                    new Pos3D(-7.0F, 0.0F, 1.50F), new Pos3D(-8.0F, 0.0F,-1.30F), new Pos3D(-7.0F, 0.3F,-1.90F),
                    new Pos3D(-6.0F, 1.8F,-2.40F), new Pos3D(-4.5F, 4.9F,-2.40F), new Pos3D(-3.0F, 3.2F,-2.40F),
                    new Pos3D( 0.5F, 2.4F,-2.80F), new Pos3D( 3.5F, 4.4F,-2.80F), new Pos3D( 4.5F, 4.7F,-2.90F),
                    new Pos3D( 5.5F, 4.8F,-3.00F) };
  lp = new LignePolygonale3D(37,tPos);
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    delete(tPos[i]); }
}

/* 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);

  initLignePolygonale3D();

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  glutInitWindowSize(600,250); 
  glutInitWindowPosition(50,50); 
  glutCreateWindow("Courbes B-Spline et de Bézier"); 
  init();
  glutKeyboardFunc(keyboard);
  glutSpecialFunc(special);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
  glutDisplayFunc(display);
  glutMainLoop();
  return(0);
}
