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

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

#include "FenetreControles.h"
#include "Controle.h"
#include "Couleur.h"
#include "ModuleMenus.h"
#include "Vector.h"

#define NBCTRLMAX 500

static FenetreControles *fenCs[NBCTRLMAX];
static Controle ***ctrls[NBCTRLMAX];
static int *sps[NBCTRLMAX];

static void keyboardControle(unsigned char c,int x,int y) {
  FenetreControles *fen = fenCs[glutGetWindow()];
  Controle **ctrl = *ctrls[glutGetWindow()];
  int sp = *sps[glutGetWindow()];
  for ( int i = 0 ; i < sp ; i++ )
    if ( ctrl[i]->actif ) {
      int res = ctrl[i]->gereKeyboard(c,x,y);
      if ( res == 1 )
        if ( fen->fonction != NULL )
          fen->fonction(ctrl[i]);
      if ( res ) {
        glutPostWindowRedisplay(glutGetWindow());
        return; } }
  switch ( c ) {
    case 0x09 :
      { if ( fen->ctrlEnCours != -1 ) {
          ctrl[fen->ctrlEnCours]->actif = 0;
          int shft = ( (glutGetModifiers() & GLUT_ACTIVE_SHIFT) != 0 );
          if ( shft ) {
            fen->ctrlEnCours = (fen->ctrlEnCours+fen->sp-1)%fen->sp; }
            else {
            fen->ctrlEnCours = (fen->ctrlEnCours+1)%fen->sp; }
          ctrl[fen->ctrlEnCours]->actif = 1;
          fen->ctrlActif = fen->ctrlEnCours;
          glutPostWindowRedisplay(glutGetWindow()); } }
      break;
    case 0x1B :
      fen->annule();
      fen->init();
      fen->setAffichage(NULL);
      fen->reshapeWindow(290,50,"Action");
      if ( *fen->enCours )
        *fen->enCours = 0;
      break; }
}

static void specialControle(int c,int x,int y) {
  FenetreControles *fen = fenCs[glutGetWindow()];
  Controle **ctrl = *ctrls[glutGetWindow()];
  int sp = *sps[glutGetWindow()];
  for ( int i = 0 ; i < sp ; i++ )
    if ( ctrl[i]->actif ) {
      int res = ctrl[i]->gereSpecial(c,x,y);
      if ( res == 1 )
        if ( fen->fonction != NULL )
          fen->fonction(ctrl[i]);
      if ( res )
        glutPostWindowRedisplay(glutGetWindow()); }
}

static void redrawControle(void) {
  glClearColor(0.7F,0.7F,0.7F,1.0F);
  glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
  glPushMatrix();
  FenetreControles *fen = fenCs[glutGetWindow()];
  Controle **ctrl = *ctrls[glutGetWindow()];
  int sp = *sps[glutGetWindow()];
  for ( int i = 0 ; i < sp ; i++ ) {
    ctrl[i]->dessiner(); }
  glPopMatrix();
  glPushMatrix();
  if ( fen->display != NULL )
    fen->display();
  glPopMatrix();
  glFlush();
  glutSwapBuffers();
}

static void reshapeControle(int w,int h) {
  glViewport(0,0,w,h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0,w,h,0,-100.0,100.0); 
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static void sourisControle(int bouton,int etat,int x,int y) {
  FenetreControles *fen = fenCs[glutGetWindow()];
  Controle **ctrl = *ctrls[glutGetWindow()];
  int sp = *sps[glutGetWindow()];
  fen->button = bouton;
  if ( ( bouton == GLUT_LEFT_BUTTON ) && ( etat == GLUT_DOWN ) ) {
    fen->mpx = x ;
    fen->mpy = y ; 
    int a = -1;
    for ( int i = 0 ; i < sp ; i++ ) {
      a = ctrl[i]->selection(x,y);
      if ( a == 2 ) {
        glutPostWindowRedisplay(glutGetWindow());
        if ( fen->fonction != NULL )
          fen->fonction(ctrl[i]);
        a = -1;
        i = sp; }
      if ( a == 1 ) {
        for ( int j = 0 ; j < sp ; j++ ) {
          if ( i != j )
            ctrl[j]->deselection(); }
        fen->ctrlActif = i;
        fen->ctrlEnCours = i;
        glutPostWindowRedisplay(glutGetWindow());
        if ( fen->fonction != NULL )
          fen->fonction(ctrl[i]);
        if ( ctrl[i]->actif )
          for ( int j = 0 ; j < sp ; j++ )
            if ( i != j )
              ctrl[j]->deselection();
        i = sp; } }
    if ( a < 0 )
      fen->ctrlActif = -1;
    if ( ( etat == GLUT_DOWN ) && ( a != -1 ) ) {
      fen->mouvement = 1 ;} }
  if ( etat == GLUT_UP ) {
    fen->mouvement = 0 ;
    if ( ( fen->ctrlActif >= 0 ) && ( fen->ctrlActif < fen->getNombreControles() ) ) {
      if ( ctrl[fen->ctrlActif]->finActivation ) {
        ctrl[fen->ctrlActif]->finActivation(); }
      fen->ctrlActif = -1; } }
}

static void motionControle(int x,int y) {
  FenetreControles *fen = fenCs[glutGetWindow()];
  Controle **ctrl = *ctrls[glutGetWindow()];
//  int sp = *sps[glutGetWindow()];
  switch ( fen->button ) {
    case GLUT_LEFT_BUTTON  : if ( ( fen->mouvement == 1 ) && ( fen->ctrlActif >= 0 ) ) {
                               int difx = x-fen->mpx ;
                               int dify = y-fen->mpy ;
                               if ( ctrl[fen->ctrlActif]->gereModification(x,y,difx,dify) ) {
                                 fen->mpx = x; 
                                 fen->mpy = y;
                                 glutPostWindowRedisplay(glutGetWindow());
                                 if ( fen->fonction != NULL )
                                   fen->fonction(ctrl[fen->ctrlActif]); } }
                             break;}
}

void FenetreControles::resizeWindow(int dtx,int dty) {
  int n = glutGetWindow();
  glutSetWindow(num);
  glutPopWindow();
  glutReshapeWindow(dtx,dty);
  glutPostWindowRedisplay(num);
  glutSetWindow(n);
}

void FenetreControles::reshapeWindow(int npx,int npy,int dtx,int dty,const char *s) {
  //int n = glutGetWindow();
  glutSetWindow(num);
  glutPopWindow();
  glutSetWindowTitle(s);
  glutPositionWindow(npx,npy);
  glutReshapeWindow(dtx,dty);
  glutPostWindowRedisplay(num);
  //glutPostRedisplay();
  //printf("  %d\n",n);
  //glutSetWindow(n);
}

void FenetreControles::reshapeWindow(int dtx,int dty,const char *s) {
  int n = glutGetWindow();
  glutSetWindow(num);
  glutPopWindow();
  glutSetWindowTitle(s);
  int x = glutGet(GLUT_WINDOW_WIDTH);
  int y = glutGet(GLUT_WINDOW_HEIGHT);
  if ( ( x != dtx ) || ( y != dty ) )
    glutReshapeWindow(dtx,dty);
    else
    glutPostWindowRedisplay(num);
  glutSetWindow(n);
}

void FenetreControles::reshapeWindow(int dtx,int dty) {
  int n = glutGetWindow();
  glutSetWindow(num);
  glutPopWindow();
  glutReshapeWindow(dtx,dty);
  glutPostWindowRedisplay(num);
  glutSetWindow(n);
}

int FenetreControles::creationFenetre(int dpx,int dpy,int dtx,int dty,char *dtitre) {
  glutInitWindowPosition(dpx,dpy);
  glutInitWindowSize(dtx,dty);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  int numW = glutCreateWindow(dtitre);
  fenCs[numW] = this;
  ctrls[numW] = &ctrl;
  sps[numW] = &sp;
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_NORMALIZE);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glutDisplayFunc(redrawControle);
  glutReshapeFunc(reshapeControle);
  glutMotionFunc(motionControle);
  glutMouseFunc(sourisControle);
  glutKeyboardFunc(keyboardControle);
  glutSpecialFunc(specialControle);
//  creationMenuBasique();
  return(numW);
}

FenetreControles::FenetreControles(void) {
  px = 50;
  py = 50;
  tx = 100;
  ty = 50;
  sp = 0;
  display = NULL;
  fonction = NULL;
  strcpy(this->titre,"Fenetre de contrôle");
  ctrl =(Controle **) calloc(NBCTRLMAX,sizeof(Controle *));
  num = creationFenetre(px,py,tx,ty,titre);
  enCours = NULL;
  backgroundColor = NULL;
  ctrlEnCours = -1;
}

FenetreControles::FenetreControles(int px,int py,int tx,int ty,const char *titre,void (*fonction)(Controle *),void (*display)(void),void (*menus)(void)) {
  this->px = px;
  this->py = py;
  this->tx = tx;
  this->ty = ty;
  this->sp = 0;
  this->fonction = fonction;
  this->display = display;
  strcpy(this->titre,titre);
  ctrl =(Controle **) calloc(NBCTRLMAX,sizeof(Controle *));
  num = creationFenetre(px,py,tx,ty,this->titre);
  if ( menus )
    menus();
  enCours = NULL;
  backgroundColor = NULL;
  ctrlEnCours = -1;
}

FenetreControles::FenetreControles(int px,int py,int tx,int ty,const char *titre,void (*fonction)(Controle *),void (*display)(void),void (*menus)(void),int *enCours) {
  this->px = px;
  this->py = py;
  this->tx = tx;
  this->ty = ty;
  this->sp = 0;
  this->fonction = fonction;
  this->display = display;
  strcpy(this->titre,titre);
  ctrl =(Controle **) calloc(NBCTRLMAX,sizeof(Controle *));
  num = creationFenetre(px,py,tx,ty,this->titre);
  if ( menus )
    menus();
  this->enCours = enCours;
  backgroundColor = NULL;
  ctrlEnCours = -1;
}

FenetreControles::~FenetreControles(void) {
  free(ctrl);
  if ( backgroundColor )
    delete(backgroundColor);
}

void FenetreControles::add(Controle *c) {
  this->ctrl[sp] = c;
  c->deselection();
  c->initialise();
  c->setFenetre(this);
  sp++;
}

void FenetreControles::reinit(void) {
  sp = 0;
  glutSetWindow(num);
  glutPositionWindow(px,py);
  this->display = NULL;
}

void FenetreControles::finalise(void) {
  for ( int i = 0 ; i < sp ; i++ ) {
    delete(ctrl[i]); }
}

void FenetreControles::sauve(void) {
  for ( int i = 0 ; i < sp ; i++ ) {
    ctrl[i]->sauve(); }
}

void FenetreControles::annule(void) {
  for ( int i = 0 ; i < sp ; i++ ) {
    ctrl[i]->annule(); }
}

void FenetreControles::init(void) {
  int n = glutGetWindow();
  sp = 0;
  glutSetWindow(num);
  glutPositionWindow(px,py);
  this->display = NULL;
  this->fonction = NULL;
  glutSetWindow(n);
}

void FenetreControles::setAffichage(void (*sdisplay)(void)) {
  this->display = sdisplay;
}

void FenetreControles::setFonction(void (*sfonction)(Controle *ctrl)) {
  this->fonction = sfonction;
}

int FenetreControles::getNombreControles(void) {
  return(sp);
}

void FenetreControles::setNombreControles(int nb) {
  sp = nb;
}

void FenetreControles::setBackgroundColor(Couleur *c) {
  if ( backgroundColor == NULL )
    backgroundColor = new Couleur(c);
    else
    backgroundColor->set(c);
}

Couleur *FenetreControles::getBackgroundColor(void) {
  return(backgroundColor);
}

int FenetreControles::sauveFichierConfiguration(const char *filename,const char *tagName) {
  FILE *f = fopen(filename,"wt");
  if ( f ) {
    fprintf(f,"<%s>\n",tagName);
    for ( int i = 0 ; i < sp ; i++ ) {
      ctrl[i]->sauve(f); }
    fprintf(f,"</%s>\n",tagName);
    fclose(f);
    return(1); }
  return(0);
}

int FenetreControles::sauveFichierConfiguration(const char *filename,const char *tagName,Vector *v) {
  FILE *f = fopen(filename,"wt");
  if ( f ) {
    fprintf(f,"<%s>\n",tagName);
    Controle **t =(Controle **) v->getArray();
    for ( int i = 0 ; i < v->size() ; i++ ) {
      t[i]->sauve(f); }
    fprintf(f,"</%s>\n",tagName);
    fclose(f);
    delete(t);
    return(1); }
  return(0);
}
