// Basiques
# include <iostream>
# include <cmath>
# include <vector>

// Random
# include <ctime>
# include <cstdlib>

// SDL
# include <SDL/SDL.h>
# undef main
# include <SDL/SDL_rotozoom.h>
# include <SDL/SDL_gfxPrimitives.h>

// bBox2D
# include <box2d/box2d.h>
# define MULTI 100.0f

// Local
# include "Car.h"
#include "Terrain.h"

int main(int argc, char** argv)
{
	/// [1] Démarrage
    // [1.0] Démarrage aléatoire
    srand( time(0) );

    // [1.1] Démarrages SDL
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0)
    {
        std::cout << "Impossible d'initialiser la SDL: " << SDL_GetError() << std::endl;
        return 1;
    }

    // [1.2] Préparation de fermeture
    atexit(SDL_Quit);

    // [1.3] Para-fenêtre
    SDL_WM_SetCaption("Box2DTests", 0);

    /// [2] Préparation des composants SDL
    // [2.1] Préparation de la fenêtre
    SDL_Surface* screen = SDL_SetVideoMode(1200, 600, 32, SDL_HWSURFACE|SDL_DOUBLEBUF);
    if ( !screen )
    {
        std::cout << "Bug à l'initialisation: " << SDL_GetError() << std::endl;
        return 1;
    }

    // [2.2] Préparation variables
    SDL_Rect mouse({ 400, 200, 0, 0 });
    SDL_Rect pxpos({ 400, 200, 0, 0 });

    // [2.3] Préparation surfaces
    SDL_Surface* tempo( 0x0 );

    // [2.4] Préparation du temps
    Uint32 frameRate( 60 );
    Uint32 tprev(0), twait( 1000 / frameRate );
    bool pause( false );

    // [2.5] Préparation des messages
    std::string msg("Victor");

    /// [3] Box2D
    // Trucs
    B2_NOT_USED(argc);
    B2_NOT_USED(argv);

    // Construct a world object
    b2Vec2 gravity( 0.0f, 10.0f );
    b2World world(gravity);

    // Simulation settings
    float timeStep = 1.0f / frameRate;
    int32 velocityIterations = 8;
    int32 positionIterations = 3;
    float angle ;
    b2Vec2 position, ref ;

	// Define the edge body
	float areah( (float)screen->h / MULTI );
	float areaw( (float)screen->w / MULTI );

    # define NB_PLOT 400
    Terrain myTerrain( NB_PLOT );
    myTerrain.addPlot( 0.0f, 0.0f );
    myTerrain.addPlot( 0.0f, areah );

    for ( unsigned int i(2); i < NB_PLOT ; i ++ )
    {
        myTerrain.addPlot( 1.0f * i, areah + 0.03f * i * sin( 0.35f * i ) ) ;
    }

    myTerrain.build( world );

    // Voiture
    Car myCar;
    myCar.init( world, 3.0f, 1.0f );

    /// [4] Boucle principale
    bool done = false;
    SDL_Event event;
    while (!done)
    {
        // [4.1] Gestion évènements
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                done = true;
                break;
            case SDL_KEYDOWN:
                switch( event.key.keysym.sym )
                {
                case SDLK_ESCAPE :
                    done = true;
                    break;
                case SDLK_BACKSPACE :
                    // Destroy joints first of all

                    std::cout << std::endl << "Deleting all entities." << std::endl << std::endl;

                    break;
                case SDLK_SPACE :
                    pause = !pause ;
                    break;
                case SDLK_RIGHT :
                    myCar.drive( GO );
                    break;
                case SDLK_LEFT :
                    myCar.drive( REVERSE );
                    break;
                case SDLK_DOWN :
                    myCar.drive( BREAK );
                    break;
                }
                break;
            case SDL_KEYUP:
                switch( event.key.keysym.sym )
                {
                case SDLK_LEFT :
                case SDLK_RIGHT :
                case SDLK_DOWN :
                    myCar.drive( FREE );
                    break;
                }
                break;
            case SDL_MOUSEMOTION:
                mouse.x = event.motion.x ;
                mouse.y = event.motion.y ;
                break;
            case SDL_MOUSEBUTTONDOWN:
                std::cout << "SDL_MOUSEBUTTONDOWN" << std::endl;
                break;
            case SDL_MOUSEBUTTONUP:
                std::cout << "SDL_MOUSEBUTTONUP" << std::endl;
                break;
            } // end switch event type
        } // end of message processing

        // [4.2] Calculs
        // It is generally best to keep the time step and iterations fixed.
        /*motor->SetMotorSpeed( speed );
        motor->SetMaxMotorTorque( torque );*/

        if ( !pause )
        {
			world.Step(timeStep, velocityIterations, positionIterations);
            myCar.update();
        }

        ref = myCar.GetPosition();
        ref.Set( ref.x - areaw / 4.0f , ref.y - areah / 2.0f );

        // [4.3] Dessin des composants
        SDL_FillRect(screen, 0, 0x444444);

        myTerrain.draw( screen, ref );

        for ( b2Body* b = world.GetBodyList(); b; b = b->GetNext() )
        {
            position = b->GetPosition() - ref ;
            angle = b->GetAngle();

            tempo = (SDL_Surface*)b->GetUserData().pointer;
            if ( tempo == 0x0 )
                continue ;

            tempo = rotozoomSurface( tempo, -angle * 180.0f / b2_pi, 1.0f, 0 );


            pxpos.x = position.x * MULTI - tempo->w / 2 ;
            pxpos.y = position.y * MULTI - tempo->h / 2 ;
            SDL_BlitSurface( tempo, 0x0, screen, &pxpos);

            SDL_FreeSurface( tempo );
            tempo = 0x0 ;
        }

        // Messages
        msg = "Ratio CPU : " ;
        msg += std::to_string( ((float)SDL_GetTicks() - tprev) * 100 / twait ) ;
        msg += " pourcents." ;
        stringRGBA( screen, 16, 16, msg.c_str(), 0, 255, 0, 255 );

        msg = "Position : " ;
        msg += std::to_string( ref.x ) ;
        msg += " m." ;
        stringRGBA( screen, 16, 25, msg.c_str(), 0, 255, 0, 255 );

        /*msg = "Torque : " ;
        msg += std::to_string( torque ) ;
        msg += " N.m." ;
        stringRGBA( screen, 16, 34, msg.c_str(), 0, 255, 0, 255 );*/

        SDL_Flip(screen);

        // [4.4] Temps
        while( SDL_GetTicks() - tprev < twait )
        {
        	if ( SDL_GetTicks() - tprev < twait/2 )
        		SDL_Delay( twait/3 );
        }
        tprev = SDL_GetTicks();

    } //fin bcl principale

    ///[5] Destruction des composants

	return 0;
}