#include <GL4D/gl4du.h>
#include <GL4D/gl4dp.h>
#include <GL4D/gl4dg.h>
#include <GL4D/gl4dh.h>
#include "animations.h"
#include "audioHelper.h"
#include <SDL_image.h>
#include <assert.h>

/* Prototypes des fonctions statiques contenues dans ce fichier C */
static void init(void);
static void draw(void);
static void quit(void);

/*!\brief largeur et hauteur de la texture */
static int _tW = 1024, _tH = 1024;
/*!\brief identifiant du (futur) GLSL program */
static GLuint _pId = 0;
/*!\brief identifiant du (futur) GLSL program */
static GLuint _pId2 = 0;
/*!\brief identifiant de framebuffer */
static GLuint _fbo = 0;
/*!\brief identifiant du plan */
static GLuint _quad = 0;
/*!\brief identifiant de textures */
static GLuint _texId[] = { 0 };

void spi(int state) {
  switch(state) {
  case GL4DH_INIT: init(); return;
  case GL4DH_FREE: quit(); return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default: draw(); return;
  }
}

static void init(void) {
  _quad = gl4dgGenQuadf();
  _pId = gl4duCreateProgram("<vs>shaders/basic.vs", "<fs>shaders/spi.fs", NULL);
  _pId2 = gl4duCreateProgram("<vs>shaders/basic.vs", "<fs>shaders/br.fs", NULL);

  glGenTextures(sizeof _texId / sizeof *_texId, _texId);
  glBindTexture(GL_TEXTURE_2D, _texId[0]);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, _tW, _tH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  glBindTexture(GL_TEXTURE_2D, 0);  

  glGenFramebuffers(1, &_fbo);

  /* black/red */
  glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texId[0],  0);

  GLint vp[4];
  glGetIntegerv(GL_VIEWPORT, vp);
  glViewport(0, 0, _tW, _tH);
  /* effacement du buffer de couleur */
  glClear(GL_COLOR_BUFFER_BIT);
  /* activation du programme _pId2 */
  glUseProgram(_pId2);
  gl4duBindMatrix("pm");
  gl4duPushMatrix();
  gl4duLoadIdentityf();
  gl4duSendMatrices();
  gl4duPopMatrix();
  gl4dgDraw(_quad);
  glViewport(0, 0, vp[2], vp[3]);
  /* fin, plus besoin de fbo */
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

/*!\brief Cette fonction dessine dans le contexte OpenGL actif. */
static void draw(void) {
  GLint vp[4];
  const GLfloat tt = 20.0f;
  static GLfloat t0 = -1.0;
  if(t0 < 0.0) {
    t0 = (GLfloat)(gl4dGetElapsedTime() / 1000.0);
    ahSetStep(3);
  }
  GLfloat t = (GLfloat)(gl4dGetElapsedTime() / 1000.0) - t0;
  t = (tt - t) / tt;
  t = t < 0.0f ? 0.0f : t;
  t = t < 0.5f ? 1.0f / (1.0f + exp(-(12.0f * t - 6.0f))) : t;
  glGetIntegerv(GL_VIEWPORT, vp);
  glViewport(0, 0, vp[2], vp[3]);
  /* effacement du buffer de couleur */
  glClear(GL_COLOR_BUFFER_BIT);
  /* activation du programme _pId */
  glUseProgram(_pId);
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, _texId[0]);
  glUniform1i(glGetUniformLocation(_pId, "tx"), 0);
  glUniform1f(glGetUniformLocation(_pId, "tt"), powf(tt * t, 4.0f));
  glUniform1f(glGetUniformLocation(_pId, "t"), t);

  gl4duBindMatrix("pm");
  gl4duPushMatrix();
  gl4duLoadIdentityf();
  gl4duSendMatrices();
  gl4duPopMatrix();
  gl4dgDraw(_quad);
  glBindTexture(GL_TEXTURE_2D, 0);  

  /* désactiver le programme shader */
  glUseProgram(0);
}

static void quit(void) {
  /* suppression de la texture */
  if(_texId[0]) {
    glDeleteTextures(sizeof _texId / sizeof *_texId, _texId);
    _texId[0] = 0;
  }
  /* suppression du fbo */
  if(_fbo) {
    glDeleteFramebuffers(1, &_fbo);
    _fbo = 0;
  }
}
