/* Examen n°1                                   */
/*                                              */
/* Auteur: Nicolas JANEY                        */
/* nicolas.janey@univ-fcomte.fr                 */
/* Fevrier 2021                                 */

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

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

/* Variables et constantes globales             */

static int mode = 1;
static int anim = 0;
static int question = 1;

static float py = -9.0F;
static float dy = 0.01F;
static float rz = 0.0F;

static const float noir[4] = { 0.0F,0.0F,0.0F,1.0F };
static const float rouge[4] = { 1.0F,0.0F,0.0F,1.0F };
static const float vert[4] = { 0.0F,1.0F,0.0F,1.0F };
static const float bleu[4] = { 0.0F,0.0F,1.0F,1.0F };
static const float jaune[4] = { 1.0F,1.0F,0.0F,1.0F };
static const float blanc[4] = { 1.0F,1.0F,1.0F,1.0F };

static float r1 = 0.0F;
static float r2 = 0.0F;
static float r3 = 0.0F;

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

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

static void init(void) {
  glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
  glEnable(GL_CULL_FACE);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, noir);
}

/* Scenes dessinees                             */

static void pyramide() {
  //glColor3f(1.0F, 1.0F, 0.0F);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, blanc);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, blanc);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blanc);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 120.0F);
  glPushMatrix();
  glNormal3f(0.0F, -1.0F, 0.0F);
  glBegin(GL_QUADS);
  glVertex3f(1.0F, 0.0F, 1.0F);
  glVertex3f(-1.0F, 0.0F, 1.0F);
  glVertex3f(-1.0F, 0.0F, -1.0F);
  glVertex3f(1.0F, 0.0F, -1.0F);
  glEnd();
  for (int i = 0; i < 4; i++) {
    glPushMatrix();
    glRotatef(90.0F * i, 0.0F, 1.0F, 0.0F);
    glNormal3f(0.0F, 1.5F, 1.0F);
    glBegin(GL_TRIANGLES);
    glVertex3f(-1.0F, 0.0F, 1.0F);
    glVertex3f(1.0F, 0.0F, 1.0F);
    glVertex3f(0.0F, 1.5F, 0.0F);
    glEnd();
    glPopMatrix();
  }
  glPopMatrix();
}

static void animation() {
  glPushMatrix();
  glRotatef(rz, 0.0F, 0.0F, 1.0F);
  glPushMatrix();
  glPushMatrix();
  glScalef(0.2F, 22.0F, 0.2F);
  //glColor3f(1.0F, 1.0F, 0.0F);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, jaune);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, jaune);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blanc);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 120.0F);
  glRotatef(4.0F, 0.0F, 1.0F, 0.0F);
  glutSolidCube(1.0);
  glPopMatrix();
  glPushMatrix();
  glTranslatef(0.0F, 11.0F, 0.0F);
  //glColor3f(1.0F, 0.0F, 0.0F);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rouge);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, rouge);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blanc);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 110.0F);
  glutSolidSphere(1.0, 36, 18);
  glPopMatrix();
  glPushMatrix();
  glTranslatef(0.0F, -11.0F, 0.0F);
  //glColor3f(0.0F, 0.0F, 1.0F);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, bleu);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, bleu);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blanc);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0F);
  glutSolidSphere(1.0, 36, 18);
  glPopMatrix();
  glPushMatrix();
  glTranslatef(0.0F, py, 0.0F);
  //glColor3f(0.0F, 1.0F, 0.0F);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, vert);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, vert);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blanc);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0F);
  glutSolidSphere(1.0, 36, 18);
  glPopMatrix();
  glPopMatrix();
  glPopMatrix();
}

/* 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();
  switch (question) {
    case 0:
    glRotatef(r1, 1.0F, 1.0F, 0.0F);
    glRotatef(r2, 0.0F, 1.0F, 1.0F);
    glRotatef(r3, 1.0F, 0.0F, 1.0F);
    glScalef(8.0F, 8.0F, 8.0F);
    pyramide();
    break;
    case 1:
    animation();
    break;
  }
  glPopMatrix();
  glFlush();
  glutSwapBuffers();
  int error = glGetError();
  if (error != GL_NO_ERROR)
    printf("Erreur OpenGL: %d\n", error);
}

/* Desallocation de la texture                  */

static void clean(void) {
}

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

/* Fonction executee lors d'un changement       */
/* de la taille 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)
    gluPerspective(15.0, ratio, 0.01, 200.0);
  else
    gluPerspective(15.0 / ratio, ratio, 0.01, 200.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

static void idle(void) {
  switch (question) {
    case 0:
    r1 += 0.111F;
    r2 += 0.044F;
    r3 += 0.077F;
    break;
    case 1:
    rz += 0.1F;
    py += dy;
    if ((py >= 9.0F) || (py <= -9.0F)) {
      dy = -dy;
    }
    break;
  }
  glutPostRedisplay();
  //temporisation
  long j = 0;
  for (int i = 0; i < 500000; i++) {
    j++;
  }
}

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

static void keyboard(unsigned char key, int x, int y) {
  switch (key) {
    case 'm':
    mode = !mode;
    glutPostRedisplay();
    break;
    case ' ':
    anim = !anim;
    glutIdleFunc((anim) ? idle : NULL);
    break;
    case 0x0D:
    question = (question + 1) % 2;
    glutPostRedisplay();
    break;
    case 0x1B:
    exit(0);
    break;
  }
}

/* Fonction principale                          */

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

  atexit(clean);

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowSize(640, 640);
  glutInitWindowPosition(50, 50);
  glutCreateWindow("Examen 1");
  init();
  glutIdleFunc((anim) ? idle : NULL);
  glutKeyboardFunc(keyboard);
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMainLoop();
  return(0);
}
