// 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>

// Creater
# include "creater.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(600, 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* blc = 0x0;
    SDL_Surface* bar = 0x0;
    SDL_Surface* wheel = 0x0;
    SDL_Surface* tempo = 0x0;

    blc = SDL_LoadBMP("Mur.bmp");
    bar = SDL_LoadBMP("Bar.bmp");
    wheel = SDL_LoadBMP("Wheel.bmp");

    if ( !blc || !bar || !wheel )
        std::cout << "Pb avec la texture." << std::endl;

    // [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);

    // Define the dynamic body container
    std::vector<b2Body*> tbody;

    // Simulation settings
    float timeStep = 1.0f / frameRate;
    int32 velocityIterations = 8;
    int32 positionIterations = 3;

    // Variables
    b2Vec2 position;
    float angle;

	// Define the edge body
	b2BodyDef areaDef;
	areaDef.position.Set(0.0f, 0.0f);
	b2Body* areaBody = world.CreateBody(&areaDef);

	b2Vec2 vs[4];
	float areah( (float)screen->h / MULTI );
	float areaw( (float)screen->w / MULTI );
	vs[0].Set( 0.0f, 0.0f );
	vs[1].Set( 0.0f, areah );
	vs[2].Set( areaw, areah );
	vs[3].Set( areaw, 0.0f );
	b2ChainShape chain;
	chain.CreateLoop(vs, 4);
	areaBody->CreateFixture(&chain, 0.0f);

    // Joint stuff
    std::vector<b2Joint*> tjoint ;
    b2Vec2 posA, posB;
    Sint16 x1, y1, x2, y2;
    Uint16 constraint;


    /// [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
                    for ( unsigned int i(0); i < tjoint.size(); i ++ )
                    {
                        world.DestroyJoint( tjoint[i] );
                        tjoint[i] = 0x0 ;
                    }
                    tjoint.clear();

                    // Destroy bodies
                    for ( unsigned int i(0); i < tbody.size(); i ++ )
                    {
                        world.DestroyBody( tbody[i] );
                        tbody[i] = 0x0 ;
                    }
            		tbody.clear();

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

            		break;
            	case SDLK_SPACE :
                	pause = !pause ;
                	break;
                case SDLK_a :
                    createBloc( world, tbody, blc, (float)(rand() % 600) / MULTI, 1.5f + (float)(rand() % 100) / MULTI );
                    break;
                case SDLK_z :
                    link2ByDist( world, tbody, tjoint );
                    break;
                case SDLK_e :
                    link3ByDist( world, tbody, tjoint );
                    break;
            	case SDLK_UP :
                	std::cout << "SDLK_UP" << std::endl;
                	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.
        if ( !pause )
			world.Step(timeStep, velocityIterations, positionIterations);

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

        for ( unsigned int i(0); i < tbody.size(); i ++ )
        {
            position = tbody[i]->GetPosition();
            angle = tbody[i]->GetAngle();
            tempo = (SDL_Surface*) tbody[i]->GetUserData().pointer;

            tempo = rotozoomSurface( tempo, -angle * 180.0f / b2_pi, 2.15f, 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;
        }

        for ( unsigned int i(0); i < tjoint.size(); i ++ )
        {
            posA = tjoint[i]->GetAnchorA();
            posB = tjoint[i]->GetAnchorB();

            x1 = posA.x * MULTI ;
            y1 = posA.y * MULTI ;
            x2 = posB.x * MULTI ;
            y2 = posB.y * MULTI ;

            constraint = tjoint[i]->GetReactionForce( 1.0f / timeStep ).Length() * 100.0f;
            if ( constraint > 255 )
                constraint = 255 ;

            lineRGBA (screen, x1, y1, x2, y2, constraint, 0, 255 - constraint, 255);
        }

        // 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 = "Nombre de blocs : " ;
        msg += std::to_string( tbody.size() ) ;
        msg += "." ;
        stringRGBA( screen, 16, 25, 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
    SDL_FreeSurface(screen);
    SDL_FreeSurface(blc);
    SDL_FreeSurface(bar);
    SDL_FreeSurface(wheel);

	return 0;
}