//------------------------------------------------------------------------------
// 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)
    
    #ifndef __cplusplus
        typedef int bool;
        #define false (1==2)
        #define true (1==1)
    #endif
        
//------------------------------------------------------------------------------
// Variables & Constantes
//------------------------------------------------------------------------------    

    // Gestion du temps
    struct TEMPS
    {
        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
    };
    struct TEMPS temps;
    
    int WIDTH  = 400;           // largeur (initiale) de l'écran
    int HEIGHT = 150;           // hauteur (initiale) de l'écran
    
    bool    bFrame = false;     // false : gauche / true : droit
    int     Fcube  = 0;
    // 0 : wireframe
    // 1 : arc en ciel
    // 2 : les deux
    
//------------------------------------------------------------------------------
// Fonctions & Procédures
//------------------------------------------------------------------------------        
    
    void    ReshapeGL ( int, int );
    void    ReshapeScreen ( int, int, int, int );
    void    InitGL ( );
    void    DrawGL ( );
    int     main( int, char**, char** );
    void    Titre();
    void    KeyGL ( unsigned char, int, int );
    
//------------------------------------------------------------------------------
// Modifie le titre de la fenêtre
//------------------------------------------------------------------------------

void    Titre ( )
 {
    char    title[255];
    char    forme[255];    

    if ( Fcube == 0 ) sprintf( forme, "%s", "WireFrame" );
    if ( Fcube == 1 ) sprintf( forme, "%s", "Colors" );
    if ( Fcube == 2 ) sprintf( forme, "%s", "WireFrame & Colors" );
    
    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, "True 3D (by BeLZeL) | %s | %02d:%02d:%03d (%.0f FPS) (%d frames)", forme, temps.tps/1000/60, temps.tps/1000%60, temps.tps%1000, temps.fps, temps.frame );
        glutSetWindowTitle ( title );
    }
 }    
    
//------------------------------------------------------------------------------
// Gestion des touches standard
//------------------------------------------------------------------------------

void    KeyGL ( unsigned char key, int x, int y )
 {  
    switch ( key )
     {
        // changement d'aspect du cube
        case ' ' :
            ( Fcube >= 2 ) ? ( Fcube=0 ) : ( Fcube++ );
            Titre ( );
            break;

        // quitter
        case 27 :
            exit ( 0 );
            break;
     }
 }    
       
//------------------------------------------------------------------------------
// Fonction de Redimensionnement de la fenêtre
//------------------------------------------------------------------------------

void    ReshapeGL ( int Width, int Height )
 {
    if ( Height == 0 ) Height = 1;
    WIDTH = Width;
    HEIGHT = Height;
    glMatrixMode ( GL_MODELVIEW );
    glLoadIdentity ( );
    glViewport ( 0, 0, Width, Height );
 }
 
//------------------------------------------------------------------------------
// Redimensionnement manuel de la fenêtre (partie gauche et droite)
//------------------------------------------------------------------------------

void    ReshapeScreen ( int OrX, int OrY, int Width, int Height )
 {
    if ( Height == 0 ) Height = 1;
    glViewport ( OrX, OrY, Width, Height );
    glMatrixMode ( GL_PROJECTION );
    glLoadIdentity ( );
    gluPerspective ( 45, (float)(Width)/(float)(Height), 0.1, 10 );
    glMatrixMode ( GL_MODELVIEW );
    glLoadIdentity ( );    
 } 

//------------------------------------------------------------------------------
// Fonction d'Initialisation
//------------------------------------------------------------------------------

void    InitGL ( )
 {
    // -- Anti Aliasing
    glEnable (GL_LINE_SMOOTH);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
    glLineWidth (1.5);
    // -- Fin Anti Aliasing

    glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );        // Black Background
    glEnable ( GL_DEPTH_TEST );
    glEnable ( GL_COLOR );                          // Colors Enabled
 }

//------------------------------------------------------------------------------
// Fonction d'affichage
//------------------------------------------------------------------------------

void    DrawGL ( )
 {
    static float theta = 0;                        // Your Angle 
    float size = 1.0f;
    
    // Mise à jour du temps
    temps.frame++;
    temps.frame_fps++;
    temps.ecart = (double)(( glutGet ( GLUT_ELAPSED_TIME ) - temps.tps - 0.0001) / 1000);
    temps.tps = glutGet ( GLUT_ELAPSED_TIME );
    Titre ( );
    
    // on change d'oeil à chaque raffraîchissement de l'image
    bFrame = !bFrame;
      
    // on efface l'écran lorsqu'on commence à recalculer la première image
    if (!bFrame)
     {
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        theta += temps.ecart * 100;
        ReshapeScreen ( 0, 0, (int)(WIDTH*0.5), HEIGHT );
     }
    else ReshapeScreen ( (int)(WIDTH*0.5), 0, (int)(WIDTH*0.5), HEIGHT );
      
    gluLookAt( (bFrame)?(-0.3):(0.3), 0, -5,           // xyz camera
               0, 0, 0,            // xyz cible
               0, 1, 0 );          // quel est l'axe qui va vers le haut (y)
               
    glRotatef ( theta, 1.0f, 2.0f, 3.0f );         // Rotation ( angle : theta | axis : z )               

    // LE CUBE ARC EN CIEL
    if (Fcube > 0 )
     {
        glBegin(GL_QUADS);
            // bas
            glColor3f(0,1,0); glVertex3f(+size,+size,+size);
            glColor3f(1,1,0); glVertex3f(-size,+size,+size);
            glColor3f(1,1,1); glVertex3f(-size,+size,-size);
            glColor3f(0,1,1); glVertex3f(+size,+size,-size);
            // devant
            glColor3f(0,0,1); glVertex3f(+size,-size,-size);
            glColor3f(1,0,1); glVertex3f(-size,-size,-size);
            glColor3f(1,1,1); glVertex3f(-size,+size,-size);
            glColor3f(0,1,1); glVertex3f(+size,+size,-size);
            // gauche
            glColor3f(0,0,0); glVertex3f(+size,-size,+size);
            glColor3f(0,1,0); glVertex3f(+size,+size,+size);
            glColor3f(0,1,1); glVertex3f(+size,+size,-size);
            glColor3f(0,0,1); glVertex3f(+size,-size,-size);
            // derrière
            glColor3f(0,0,0); glVertex3f(+size,-size,+size);
            glColor3f(1,0,0); glVertex3f(-size,-size,+size);
            glColor3f(1,1,0); glVertex3f(-size,+size,+size);
            glColor3f(0,1,0); glVertex3f(+size,+size,+size);
            // droite
            glColor3f(1,0,0); glVertex3f(-size,-size,+size);
            glColor3f(1,1,0); glVertex3f(-size,+size,+size);
            glColor3f(1,1,1); glVertex3f(-size,+size,-size);
            glColor3f(1,0,1); glVertex3f(-size,-size,-size);
            // haut
            glColor3f(0,0,0); glVertex3f(+size,-size,+size);
            glColor3f(1,0,0); glVertex3f(-size,-size,+size);
            glColor3f(1,0,1); glVertex3f(-size,-size,-size);
            glColor3f(0,0,1); glVertex3f(+size,-size,-size);
        glEnd ( );
     }
     
    // LE CUBE BLANC WIREFRAME
    if (Fcube != 1)
     {
        size += 0.1;
        glColor3d(1,1,1);
        glBegin(GL_LINES);
            // bas
            glVertex3f(+size,+size,+size); glVertex3f(+size,+size,-size);
            glVertex3f(+size,+size,-size); glVertex3f(+size,-size,-size);
            glVertex3f(+size,-size,-size); glVertex3f(+size,-size,+size);
            glVertex3f(+size,-size,+size); glVertex3f(+size,+size,+size);
            // haut
            glVertex3f(-size,+size,+size); glVertex3f(-size,+size,-size);
            glVertex3f(-size,+size,-size); glVertex3f(-size,-size,-size);
            glVertex3f(-size,-size,-size); glVertex3f(-size,-size,+size);
            glVertex3f(-size,-size,+size); glVertex3f(-size,+size,+size);
            // cotés
            glVertex3f(+size,+size,+size); glVertex3f(-size,+size,+size);
            glVertex3f(+size,+size,-size); glVertex3f(-size,+size,-size);
            glVertex3f(+size,-size,-size); glVertex3f(-size,-size,-size);
            glVertex3f(+size,-size,+size); glVertex3f(-size,-size,+size);
        glEnd ( );
     }

    // on affiche l'image qu'une fois sur deux
    // cad qu'on affiche lorsque les images gauche et droite sont calculées
    if (bFrame) glutSwapBuffers ( );
    glutPostRedisplay ( );
 }

//------------------------------------------------------------------------------
// Fonction Principale
//------------------------------------------------------------------------------

int     main( int argc, char *argv[ ], char *envp[ ] )
 {
    // Création de la fenêtre
    glutInit ( &argc, argv );
    glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
    glutInitWindowSize ( WIDTH, HEIGHT );
    glutInitWindowPosition ( 50, 50 );
    glutCreateWindow ( "" );
    Titre ( );

    // Fonction OpenGL
    glutReshapeFunc ( ReshapeGL );
    glutDisplayFunc ( DrawGL );
    glutKeyboardFunc ( KeyGL );    
    InitGL ( );
    glutMainLoop ( );

    return 0;
 }

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