//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------

    #ifdef __WIN32__
    #include <windows.h>                        // Header pour les Applications Windows
    #endif
    #include <stdio.h>                          // Header d'Entrée/Sortie Standard
    #include <GL/glut.h>                        // Header OpenGL Utility Toolkit (GLUT)
    #include <math.h>                        // Header OpenGL Utility Toolkit (GLUT)    
    
    #define pi          3.1415926535897932384626433832795f // PI pour les angles    
    
    #ifndef __cplusplus
        typedef int bool;
        #define false (1==2)
        #define true (1==1)
    #endif

//------------------------------------------------------------------------------
// Variables & Constantes
//------------------------------------------------------------------------------    
    
    // Gestion du temps
    typedef struct
    {
        int tps;                // temps depuis le début
        int non_ecoule;         // temps non ecoulé pendant la pause
        float ecart;            // ecart de temps entre deux frames
        long int frame;         // nombre de frames depuis le début
        int frame_fps;          // nombre réinitialisé pour les FPS
        int base;               // temps réinitialisé pour les FPS
        int pause;              // pause du temps
        float fps;              // nombre de FPS
    } TEMPS;
    TEMPS temps;
    
    // pour la fenêtre
    int     WIDTH=640;                 // largeur de la fenêtre
    int     HEIGHT=480;                // hauteur de la fenêtre

    // pour le motion blur
    UINT    g_Texture[2];
    int     g_Viewport = 0;
    int     g_BlurRate = 50;
    
    // Gestion d'une tentacule
    typedef struct
    {
        float posx, posy, posz;
        float angle, vitesse, accel;
        float color;
    } TENTACULE;
    TENTACULE tentacule;
    
//------------------------------------------------------------------------------
// Fonctions & Procédures
//------------------------------------------------------------------------------

    void    Tentacule ( TENTACULE * );
    void    Branche ( float, float, float );
    void    ReSizeGLScene ( int, int );
    void    InitGL ( );
    void    DrawGL ( );
    void    Titre ( );
    int     main( int, char**, char** );

//------------------------------------------------------------------------------
// Peut-on afficher la frame suivante ? Et on calcule/affiche le nombre de FPS
//------------------------------------------------------------------------------

bool AnimateNextFrame ( int desiredFrameRate )
{
    static float temps_ecoule = 0.0f;
    temps_ecoule += temps.ecart;

    // on vérifie si on peut animer la prochaine frame (1 seconde / FPS)
    if( temps_ecoule > ( 1.0f / (float) desiredFrameRate ) )
    {
        temps_ecoule = 0.0f;
        return true;
    }
    else return false;               // on anime pas encore
}
          
//------------------------------------------------------------------------------
// Crée une texture vide
//------------------------------------------------------------------------------

void CreateRenderTexture(UINT textureArray[], int size, int channels, int type, int textureID)
{
    unsigned int *pTexture = NULL;  // crée un pointeur

    // Alloue de la mémoire et initialise la mémoire
    pTexture = ( unsigned int * ) malloc ( size * size * channels * sizeof ( unsigned int ) );
    memset(pTexture, 0, size * size * channels * sizeof(unsigned int));

    // Enregistre la texture dans OpenGL
    glGenTextures(1, &textureArray[textureID]);
    glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);
    glTexImage2D(GL_TEXTURE_2D, 0, channels, size, size, 0, type, GL_UNSIGNED_INT, pTexture);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

    // Since we stored the texture space with OpenGL, we can delete the image data
    free ( pTexture );
}

//------------------------------------------------------------------------------
// DrawMB : Dessine ce qui est nécessaire, avec Motion Blur
//------------------------------------------------------------------------------

void DrawMB()
{
    int x, y;
    float bass=0, aigu=0;

    glDisable(GL_TEXTURE_2D);
    glEnable(GL_COLOR);

    // MotionBlur
    Tentacule ( &tentacule );

    glDisable(GL_COLOR);
    glEnable(GL_TEXTURE_2D);
    glColor3d(1,1,1);
}

//------------------------------------------------------------------------------
// DrawNMB : Dessine ce qui est nécessaire, sans Motion Blur
//------------------------------------------------------------------------------

void DrawNMB()
{
    glEnable(GL_COLOR);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);

    // Non Motion Blur
    Tentacule ( &tentacule );

    glDisable(GL_COLOR);
    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glColor3d(1,1,1);
}
    
//------------------------------------------------------------------------------
// Rendu du Motion Blur
//------------------------------------------------------------------------------

void RenderMotionBlur ( int textureID )
{
    int add=10;

    glPushMatrix();

    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);

    glBindTexture(GL_TEXTURE_2D, g_Texture[textureID]);
    glColor4f(1, 1, 1, 0.95f);

    // OrthoMode
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho( 0, WIDTH, HEIGHT, 0, 0, 1 );
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 1.0f); glVertex2f(-add, -add);
        glTexCoord2f(0.0f, 0.0f); glVertex2f(-add, HEIGHT+add);
        glTexCoord2f(1.0f, 0.0f); glVertex2f(WIDTH+add, HEIGHT+add);
        glTexCoord2f(1.0f, 1.0f); glVertex2f(WIDTH+add, -add);
    glEnd();

    // PerspectiveMode
    glMatrixMode( GL_PROJECTION );
    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );

    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);

    glPopMatrix();
}

//------------------------------------------------------------------------------
// Modifie le titre de la fenêtre
//------------------------------------------------------------------------------

void    Titre ( )
{
    char    title[255];

    if ( temps.tps - temps.base > 83 )                     // Si ca fait une seconde
    {
        temps.fps = temps.frame_fps * 1000.0 / ( temps.tps - temps.base );
        temps.base = temps.tps;                            // Le temps de base est actualisé
        temps.frame_fps = 0;                                  // On recommence à compter
        sprintf ( title, "%02d:%02d:%03d (%.0f FPS) (%d)", temps.tps/1000/60, temps.tps/1000%60, temps.tps%1000, temps.fps, temps.frame );
        glutSetWindowTitle ( title );
    }
}
       
//------------------------------------------------------------------------------
// Resize Function
//------------------------------------------------------------------------------

void ReSizeGLScene ( int width, int height )
{
    // Pour éviter une prochaine division par zéro
    if ( height == 0 ) height=1;

    glViewport (0, 0, width, height );      // Notre fenêtre est redimensionnée
    glMatrixMode ( GL_PROJECTION );         // Sélection d'une matrice de projection
    glLoadIdentity ( );                     // Initialisation de cette matrice

    // Calcule de l'aspect de la fenêtre ( angle, ratio, z proche, z loin )
    gluPerspective ( 45.0f, ( float ) width / ( float ) height, 0.1f, 300.0f );
    glMatrixMode ( GL_MODELVIEW );          // Sélection d'une matrice modelview
    glLoadIdentity ( );                     // Initialisation de cette matrice
    
    WIDTH = width;
    HEIGHT = height;
}

//------------------------------------------------------------------------------
// Initialisation Function
//------------------------------------------------------------------------------

void    InitGL ( )
 {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);           // Arrière Plan Noir
    glClearDepth(1.0f);         // Configuration de la profondeur du buffer

    ReSizeGLScene(WIDTH, HEIGHT);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);    // Test sur la profondeur (évite des bugs)

    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_BLEND);

    glEnable(GL_TEXTURE_2D);    // Enable Texture Mapping

    glShadeModel(GL_SMOOTH);    // Smooth Shading Activé, il doit lisser les formes (?)
    glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);

    CreateRenderTexture(g_Texture, 512, 3, GL_RGB, 0);
    glEnable(GL_COLOR);
    
    int     n=0;
    while ((pow(2,n) <= WIDTH) && (pow(2,n) <= HEIGHT)) n++;
    g_Viewport = (int)pow(2,n-1);
 }

//------------------------------------------------------------------------------
// Calcule l'angle à un temps précis et sans dépassement d'un angle max (pour tentacule)
// vitesse   : vitesse de la rotation
// max       : angle maximal à ne pas dépasser
// angle_old : angle déjà affiché
// Retourne l'angle calculé
//------------------------------------------------------------------------------

float CalculAngle ( float vitesse, float max, float angle_old )
{
    float angle = vitesse * 25.0f;
    if ( max != 0 )
    {
        if ( angle < -max ) angle = -max;
        if ( angle > max )  angle = max;
    }
    return angle;
}

//------------------------------------------------------------------------------
// Dessine une tentacule
// Utilise la fonction Branche (qui dessine les 4 branches de la tentacule)
//------------------------------------------------------------------------------


void Tentacule ( TENTACULE * tentacule )
{
    glPushMatrix ( );
    
    tentacule->accel += temps.ecart*100;                             // on accélère
    tentacule->vitesse = 2*cos(tentacule->accel*pi/180.0f);                   // on modifie la vitesse
    tentacule->angle += tentacule->vitesse*temps.ecart*100;                   // on modifie l'angle
    tentacule->color += temps.ecart * 0.6;

    // Transformations appliquées au cube
    glEnable ( GL_COLOR );
    glTranslatef ( tentacule->posx, tentacule->posy, tentacule->posz );      // On se recule pour voir la scène
    glRotatef ( tentacule->angle + tentacule->vitesse, 0, 0, 1 );   // On effectue une rotation sur l'axe z
    glColor3f ( ( 1.0f - ( ( ( int )( ( 1.0f + tentacule->color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    glBegin(GL_QUADS);                        // On dessine notre carré centrale
        glVertex3f( -1.0f, -1.0f, 0.0f );
        glVertex3f( -1.0f,  1.0f, 0.0f );
        glVertex3f(  1.0f,  1.0f, 0.0f );
        glVertex3f(  1.0f, -1.0f, 0.0f );
    glEnd();

    // Les 4 Tentacules
    Branche ( tentacule->vitesse, tentacule->angle, tentacule->color );
    glRotatef ( 90, 0, 0, 1 );   // On effectue une rotation sur l'axe z
    Branche ( tentacule->vitesse, tentacule->angle, tentacule->color );
    glRotatef ( 90, 0, 0, 1 );   // On effectue une rotation sur l'axe z
    Branche ( tentacule->vitesse, tentacule->angle, tentacule->color );
    glRotatef ( 90, 0, 0, 1 );   // On effectue une rotation sur l'axe z
    Branche ( tentacule->vitesse, tentacule->angle, tentacule->color );
    glDisable ( GL_COLOR );
    
    glPopMatrix ( );
}

//------------------------------------------------------------------------------
// Dessine une branche de tentacule
// angle : angle de départ de la rotation
// posx, posy : point de départ en x ou en y
//------------------------------------------------------------------------------
// N.B. : Couleur en dégradé : On utilse un modulo pour ne pas dépasser 1.0f
// 1.0f - ( ( ( int )( ( 0.8f + color ) * 100 ) ) % 100 ) / ( float ) 100 )
// Dégradé     Pour     Couleur  Coul    Pour     Modulo    Pour Modulo
// Inverse    Modulo    Depart   Modif   Modulo
//------------------------------------------------------------------------------

void Branche ( float vitesse, float angle, float color )
{
    float val;

    glPushMatrix ( );

    // Branche inclinée
    glTranslatef ( 0.0f, 2.0f, 0.0f );      // On se recule de 25 unités pour voir le cube
    glColor3f ( ( 1.0f - ( ( ( int )( ( 0.8f + color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    val = 1.13f; // sqrt(0.8f*0.8f+0.8f*0.8f);
    glBegin(GL_QUADS);
        glVertex3f( -0.8f, -0.8f, 0.0f );
        glVertex3f(  0.8f, -0.8f, 0.0f );
        glVertex3f(  val * cos ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
        glVertex3f(  val * cos ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
    glEnd();

    glRotatef ( -CalculAngle ( vitesse, 45, angle ), 0, 0, 1 );   // On effectue une rotation sur l'axe z
    glTranslatef ( 0.0f, 1.8f, 0.0f );      // On se recule de 25 unités pour voir le cube
    glColor3f ( ( 1.0f - ( ( ( int )( ( 0.6f + color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    val = 0.8f; //sqrt(0.6f*0.6f+0.6f*0.6f);
    glBegin(GL_QUADS);
        glVertex3f( -0.8f, -0.8f, 0.0f );
        glVertex3f(  0.8f, -0.8f, 0.0f );
        glVertex3f(  val * cos ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
        glVertex3f(  val * cos ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
    glEnd();

    glRotatef ( -CalculAngle ( vitesse, 45, angle ), 0, 0, 1 );   // On effectue une rotation sur l'axe z
    glTranslatef ( 0.0f, 1.4f, 0.0f );      // On se recule de 25 unités pour voir le cube
    glColor3f ( ( 1.0f - ( ( ( int )( ( 0.4f + color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    val = 0.56f; //sqrt(0.4f*0.4f+0.4f*0.4f);
    glBegin(GL_QUADS);
        glVertex3f( -0.6f, -0.6f, 0.0f );
        glVertex3f(  0.6f, -0.6f, 0.0f );
        glVertex3f(  val * cos ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
        glVertex3f(  val * cos ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
    glEnd();

    glRotatef ( -CalculAngle ( vitesse, 45, angle ), 0, 0, 1 );   // On effectue une rotation sur l'axe z
    glTranslatef ( 0.0f, 1.0f, 0.0f );      // On se recule de 25 unités pour voir le cube
    glColor3f ( ( 1.0f - ( ( ( int )( ( 0.2f + color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    val = 0.28f; //sqrt(0.2f*0.2f+0.2f*0.2f);
    glBegin(GL_QUADS);
        glVertex3f( -0.4f, -0.4f, 0.0f );
        glVertex3f(  0.4f, -0.4f, 0.0f );
        glVertex3f(  val * cos ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
        glVertex3f(  val * cos ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
    glEnd();

    glRotatef ( -CalculAngle ( vitesse, 45, angle ), 0, 0, 1 );   // On effectue une rotation sur l'axe z
    glTranslatef ( 0.0f, 0.6f, 0.0f );      // On se recule de 25 unités pour voir le cube
    glColor3f ( ( 1.0f - ( ( ( int )( ( 0.1f + color ) * 100 ) ) % 100 ) / ( float ) 100 ), 0.0f, 0.0f );
    val = 0.0f; //sqrt(0.0f*0.0f+0.0f*0.0f);
    glBegin(GL_QUADS);
        glVertex3f( -0.2f, -0.2f, 0.0f );
        glVertex3f(  0.2f, -0.2f, 0.0f );
        glVertex3f(  val * cos ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 - 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
        glVertex3f(  val * cos ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f),
                     val * sin ( ( 90 + 45 - CalculAngle ( vitesse, 45, angle ))*pi/180.0f), 0.0f );
    glEnd();

    glPopMatrix ( );
}

//------------------------------------------------------------------------------
// Draw Function
//------------------------------------------------------------------------------

void    DrawGL ( )
{
    temps.frame++;
    temps.frame_fps++;
    
    temps.ecart = (double)(( glutGet ( GLUT_ELAPSED_TIME ) - temps.tps - 0.0001) / 1000);
    temps.tps = glutGet ( GLUT_ELAPSED_TIME );
    Titre ( );

    glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    ReSizeGLScene(WIDTH, HEIGHT);

    if( AnimateNextFrame(g_BlurRate) )
    {
        glViewport(0, 0, g_Viewport, g_Viewport);
        RenderMotionBlur(0);
        DrawMB();

        glBindTexture(GL_TEXTURE_2D,g_Texture[0]);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, g_Viewport, g_Viewport, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glViewport(0, 0, WIDTH, HEIGHT);
    }

    RenderMotionBlur(0);
    DrawNMB();
    
    glutSwapBuffers ( );
    glutPostRedisplay ( );
}

//------------------------------------------------------------------------------
// Main Function
//------------------------------------------------------------------------------

int     main( int argc, char *argv[ ], char *envp[ ] )
 {
    // Create The Window
    glutInit ( &argc, argv );
    glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
    glutInitWindowSize ( WIDTH, HEIGHT );
    glutInitWindowPosition ( 50, 50 );
    glutCreateWindow ( "" );
    Titre ( );
    
    tentacule.posx = 0.0f;
    tentacule.posy = 0.0f;
    tentacule.posz = -15.0f;
    tentacule.angle = 0.0f;
    tentacule.vitesse = 0.0f;
    tentacule.accel = 0.0f;
    tentacule.color = 0.0f;

    // OpenGL Function
    glutReshapeFunc ( ReSizeGLScene );
    glutDisplayFunc ( DrawGL );
    InitGL ( );
    glutMainLoop ( );

    return 0;
 }

//------------------------------------------------------------------------------
// THE END
//------------------------------------------------------------------------------