/* Auteur: Nicolas JANEY      */
/* nico@univ-fcomte.fr        */
/* Avril 2001                 */
/* Un manipulateur souris     */
/* et clavier                 */

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

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

#define MAX_FEN 20

struct descripteur {
  float rotx;
  float roty;
  float rotz;
  float transx;
  float transy;
  float zoom;
  float manipulatorDistance;
  float fact;
  int anim;
  float view_rx;
  float view_ry;
  float view_rz; } ;

static descripteur *dsc;
static int mouvement = 0 ;
static int button = 0 ;
static int modifier = 0 ;
static int px ;
static int py ;
static int tx ;
static float mt[16];
static int max;
static int *fen;

void finModuleManipulateur(void) {
  free(dsc);
}

void initModuleManipulateur(int m,int *f) {
  max = m;
  fen = f;
  dsc =(descripteur *) calloc(max,sizeof(descripteur));
  for ( int i = 0 ; i < max ; i++ ) {
    dsc[i].manipulatorDistance = 1.0F;
    dsc[i].rotx = 0.0F;
    dsc[i].roty = 0.0F;
    dsc[i].rotz = 0.0F;
    dsc[i].transx = 0.0F;
    dsc[i].transy = 0.0F;
    dsc[i].view_rx = 0.0F;
    dsc[i].view_ry = 0.0F;
    dsc[i].view_rz = 0.0F;
    dsc[i].anim = 0;
    dsc[i].fact = 1.0F;
    dsc[i].zoom = 1.0F; }
}

static descripteur *desc(void) {
  int num = glutGetWindow();
  int f;
  for ( f = 0 ; f < max && fen[f] != num ; f++ );
  if ( f < max )
    return(&dsc[f]);
    else
    return(NULL);
}

static int getModifier() {
  int modif = 0;
  if ( (glutGetModifiers() & GLUT_ACTIVE_SHIFT) != 0 ) {
    if ( (glutGetModifiers() & GLUT_ACTIVE_CTRL) != 0 )
      modif = 4;
      else
      modif = 1; }
    else
    if ( (glutGetModifiers() & GLUT_ACTIVE_CTRL) != 0 )
      modif = 3;
      else
      if ( (glutGetModifiers() & GLUT_ACTIVE_ALT) != 0 )
        modif = 2;
  return(modif);
}

int sourisManipulateur(int bouton,int etat,int x,int y) {
  glGetFloatv(GL_PROJECTION_MATRIX,mt);
  tx = glutGet(GLUT_WINDOW_WIDTH);
  button = bouton;
  if ( bouton == GLUT_LEFT_BUTTON ) {
    modifier = getModifier();
    if ( modifier ) {
      px = x ;
      py = y ; 
      if ( etat == GLUT_DOWN ) {
        mouvement = 1 ;}
      if ( etat == GLUT_UP ) {
        mouvement = 0 ; } 
      return(1); } }
  return(0);
}

int motionManipulateur(int x,int y) {
  switch ( button ) {
    case GLUT_LEFT_BUTTON  : if ( mouvement == 1 ) {
                               switch ( modifier ) {
                                 case 1 : desc()->roty += (x-px) ;
                                          desc()->rotx += (y-py) ;
                                          px = x; 
                                          py = y;
                                          break;
                                 case 4 : desc()->rotz += (x-px) ;
                                          desc()->rotx += (y-py) ;
                                          px = x; 
                                          py = y;
                                          break;
                                 case 3 : if ( ( y-py ) > 0 )
                                            desc()->zoom *= pow(1.02F,(y-py)/5.0F) ;
                                          if ( ( y-py ) < 0 )
                                            desc()->zoom /= pow(1.02F,(py-y)/5.0F) ;
                                          py = y;
                                          break;
                                 case 2 : desc()->transx += (x-px)*2.0F/tx/mt[0]*desc()->manipulatorDistance ;
                                          desc()->transy -= (y-py)*2.0F/tx/mt[0]*desc()->manipulatorDistance ;
                                          px = x; 
                                          py = y;
                                          break; }
                               return(1); }
                             break;}
  return(0);
}

void setManipulateurSourisAngle(float ax,float ay,float az) {
  desc()->rotx = ax;
  desc()->roty = ay;
  desc()->rotz = az;
}

void manipulateurSouris() {
  glTranslatef(desc()->transx,desc()->transy,0.0F);
  glRotatef(desc()->rotx,1.0f,0.0F,0.0F);
  glRotatef(desc()->roty,0.0f,1.0F,0.0F);
  glRotatef(desc()->rotz,0.0f,0.0F,1.0F);
  glScalef(desc()->zoom,desc()->zoom,desc()->zoom);
}

void setManipulateurDistance(float d) {
  desc()->manipulatorDistance = d;
}

int keyManipulateur(unsigned char key,int x,int y) {
  modifier = getModifier();
  if ( modifier == 1 ) {
    switch ( key ) {
      case 0x0D : desc()->anim = (desc()->anim+1)%2;
                  return(1);
                  break; } }
  switch ( key ) {
    case 'x'    : desc()->view_rx +=2 ;
                  return(1);
                  break;
    case 'X'    : desc()->view_rx -=2 ;
                  return(1);
                  break;
    case 'y'    : desc()->view_ry +=2 ;
                  return(1);
                  break;
    case 'Y'    : desc()->view_ry -=2 ;
                  return(1);
                  break;
    case 'z'    : desc()->view_rz +=2 ;
                  return(1);
                  break;
    case 'Z'    : desc()->view_rz -=2 ;
                  return(1);
                  break;
    case '\033' : exit(0);
                  break ; }
  return(0);
}

int specialManipulateur(int k,int x,int y) {
  modifier = getModifier();
  if ( modifier == 1 ) {
    switch (k) {
      case GLUT_KEY_LEFT      : 
      case GLUT_KEY_PAGE_UP   : 
      case GLUT_KEY_UP        : desc()->fact *= 1.1F;
                                return(1);
                                break;
      case GLUT_KEY_RIGHT     : 
      case GLUT_KEY_PAGE_DOWN : 
      case GLUT_KEY_DOWN      : desc()->fact /= 1.1F;
                                return(1);
                                break; } }
  if ( modifier == 3 ) {
    switch (k) {
      case GLUT_KEY_UP        : desc()->view_rx = 0.0F;
                                desc()->view_ry = 0.0F;
                                desc()->view_rz = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break;
      case GLUT_KEY_DOWN      : desc()->view_rx = 180.0F;
                                desc()->view_ry = 0.0F;
                                desc()->view_rz = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break;
      case GLUT_KEY_LEFT      : desc()->view_ry = 0.0F;
                                desc()->view_rx = 0.0F;
                                desc()->view_rz = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break;
      case GLUT_KEY_RIGHT     : desc()->view_ry = 180.0F;
                                desc()->view_rx = 0.0F;
                                desc()->view_rz = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break;
      case GLUT_KEY_PAGE_DOWN : desc()->view_rz = 0.0F;
                                desc()->view_rx = 0.0F;
                                desc()->view_ry = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break;
      case GLUT_KEY_PAGE_UP   : desc()->view_rz = 180.0F;
                                desc()->view_rx = 0.0F;
                                desc()->view_ry = 0.0F;
                                desc()->rotx = 0;
                                desc()->roty = 0;
                                desc()->rotz = 0;
                                return(1);
                                break; }
    return(0); }
  float dif;
  switch(modifier) {
    case 0 : dif = 1.0F;
             break;
    case 4 : dif = 10.0F;
             break; }
  switch (k) {
    case GLUT_KEY_UP        : desc()->view_rx -= dif;
                              return(1);
                              break;
    case GLUT_KEY_DOWN      : desc()->view_rx += dif;
                              return(1);
                              break;
    case GLUT_KEY_LEFT      : desc()->view_ry -= dif;
                              return(1);
                              break;
    case GLUT_KEY_RIGHT     : desc()->view_ry += dif;
                              return(1);
                              break;
    case GLUT_KEY_PAGE_DOWN : desc()->view_rz += dif;
                              return(1);
                              break;
    case GLUT_KEY_PAGE_UP   : desc()->view_rz -= dif;
                              return(1);
                              break; }
  return(0);
}

void manipulateurClavier() {
  glRotatef(desc()->view_rx,1.0,0.0,0.0);
  glRotatef(desc()->view_ry,0.0,1.0,0.0);
  glRotatef(desc()->view_rz,0.0,0.0,1.0);
}

void setManipulateurClavierAngle(float ax,float ay,float az) {
  desc()->view_rx = ax;
  desc()->view_ry = ay;
  desc()->view_rz = az;
}

void sourisBasique(int bouton,int etat,int x,int y) {
  if ( sourisManipulateur(bouton,etat,x,y) )
    glutPostWindowRedisplay(glutGetWindow());
}

void motionBasique(int x,int y) {
  if ( motionManipulateur(x,y) )
    glutPostWindowRedisplay(glutGetWindow());
}

void keyBasique(unsigned char key,int x,int y) {
  if ( keyManipulateur(key,x,y) )
    glutPostWindowRedisplay(glutGetWindow());
}

void specialBasique(int k,int x,int y) {
  if ( specialManipulateur(k,x,y) )
    glutPostWindowRedisplay(glutGetWindow());
}

void idleBasique(void) {
  descripteur *d = desc();
  if ( d )
    if ( d->anim ) {
      d->rotx += (1.1F*d->fact);
      d->roty += (2.1F*d->fact);
      d->rotz += (3.1F*d->fact);
      glutPostWindowRedisplay(glutGetWindow()); }
}
