// Basiques
# include <iostream>

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

// bBox2D
# include <Box2D/Box2D.h>

// Missiles
# define MULTI 200.0f
# include "Missiles.h"

// Graphismes
# define SCREEN_H 810
# define SCREEN_W 810

// Weapon
struct Weapon
{
    b2Vec2 loc;
    Identity missile;
    Uint32 prev;
    Uint32 del;
};

// Main
int main(int argc, char** argv)
{
	/// [1] Démarrage
    // [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("Box2D - Balistique", 0);

    /// [2] Préparation des composants SDL
    // [2.1] Préparation de la fenêtre
    SDL_Surface* screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 32, SDL_HWSURFACE|SDL_DOUBLEBUF);
    if ( !screen )
    {
        std::cout << "Bug à l'initialisation: " << SDL_GetError() << std::endl;
        return 1;
    }
    SDL_Surface* persistant = SDL_CreateRGBSurface( SDL_HWSURFACE, SCREEN_W, SCREEN_H, 32, 0, 0, 0, 0);
    SDL_Surface* blackout = SDL_CreateRGBSurface( SDL_HWSURFACE, SCREEN_W, SCREEN_H, 32, 0, 0, 0, 0);
    SDL_SetAlpha(blackout, SDL_SRCALPHA, 25);
    bool superVisual( true );

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

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

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

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

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

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

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

    // Variables
    b2Vec2 position, force;
    float32 angle;
    Entity* descr( nullptr );

	// Define the edge body
	b2BodyDef bodyDef;
	bodyDef.position.Set(0.0f, 0.0f);
    bodyDef.userData = new Entity( {0.0f, LAND} );
	b2Body* areaBody = world.CreateBody(&bodyDef);

	b2Vec2 vs[4];
	float32 areah( (float32)screen->h / MULTI );
	float32 areaw( (float32)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);

    // Define the plane body
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(areaw * 0.5f, areaw * 0.5f );
    bodyDef.userData = new Entity( {0.05f, PLANE} );
    bodyDef.linearDamping = 0.01f;
    bodyDef.fixedRotation = true ;
    b2Body* plane = world.CreateBody(&bodyDef);

    b2CircleShape dynamicCircle;
    dynamicCircle.m_radius = 0.05f ;
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicCircle;

    fixtureDef.density = 10.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;

    plane->CreateFixture(&fixtureDef);

    // Define the tourel list
    std::vector<Weapon> tourelle;
    Identity missile(INERT);
    Uint32 weap_color[8] = { 0xffffffff,
                             0xffffffff, 
                             0xff0000ff, 
                             0x00ff00ff, 
                             0x0000ffff, 
                             0xffff00ff, 
                             0xff00ffff, 
                             0x00ffffff };

    // Define thruster
    bool thrust_up(false);
    bool thrust_down(false);
    bool thrust_left(false);
    bool thrust_right(false);
    bool foresee(false);

    /// [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 :
                    std::cout << std::endl << "Destruction des tourelles." << std::endl << std::endl;
                    tourelle.clear();
            		break;
            	case SDLK_SPACE :
                	pause = !pause ;
                	break;
                case SDLK_KP1:
                    std::cout << "Tir basique." << std::endl;
                    missile = INERT ;
                    break;
                case SDLK_KP2:
                    std::cout << "Tir d'anticipation rectiligne." << std::endl;
                    missile = ANG ;
                    break;
                case SDLK_KP3:
                    std::cout << "Missile d'accélération linéaire." << std::endl;
                    missile = ARROW ;
                    break;
                case SDLK_KP4:
                    std::cout << "Missile d'accélération électrique (1/r^2)." << std::endl;
                    missile = CHARGE ;
                    break;
                case SDLK_a :
                    superVisual = !superVisual;
                    std::cout << "Super visual : " << superVisual << std::endl;
                    break;
                case SDLK_z :
                    weap_wait += 25 ;
                    std::cout << "weap_wait += 25" << std::endl;
                    break;
                case SDLK_s :
                    weap_wait -= 25 ;
                    std::cout << "weap_wait -= 25" << std::endl;
                    break;
                case SDLK_e :
                    vie = 30;
                    std::cout << "vie = 30" << std::endl;
                    break;
                case SDLK_r :
                    foresee = !foresee;
                    if ( foresee )
                        std::cout << "Anticipation activée." << std::endl;
                    else
                        std::cout << "Les missiles auront du retard." << std::endl;
                    break;
                case SDLK_LEFT :
                    thrust_left = true ;
                    break;
                case SDLK_RIGHT :
                    thrust_right = true ;
                    break;
                case SDLK_UP :
                    thrust_up = true ;
                    break;
                case SDLK_DOWN :
                    thrust_down = true ;
                    break;
            	}
                break;
            case SDL_KEYUP:
                switch( event.key.keysym.sym )
                {
                case SDLK_LEFT :
                    thrust_left = false ;
                    break;
                case SDLK_RIGHT :
                    thrust_right = false ;
                    break;
                case SDLK_UP :
                    thrust_up = false ;
                    break;
                case SDLK_DOWN :
                    thrust_down = false ;
                    break;
                }
                break;
            case SDL_MOUSEMOTION:
                mouse.x = event.motion.x ;
                mouse.y = event.motion.y ;
                break;
            case SDL_MOUSEBUTTONDOWN:
                std::cout << "Création tourelle." << std::endl;
                tourelle.push_back({b2Vec2( (float32)mouse.x/MULTI, (float32)mouse.y/MULTI),
                                    missile,
                                    SDL_GetTicks(),
                                    weap_wait});
                break;
            case SDL_MOUSEBUTTONUP:
                break;
            } // end switch event type
        } // end of message processing

        // [4.2] Calculs
        // Reset graphismes
        if ( superVisual ) {
            SDL_BlitSurface( blackout, 0x0, persistant, 0x0 );
            SDL_BlitSurface( persistant, 0x0, screen, 0x0 );
        }
        else {
            SDL_FillRect( screen, 0x0, 0x00000000 );
        }

        // Tirs tourelle
        for ( unsigned int k(0); k < tourelle.size() && vie > 0; k++ )
        {
            if ( SDL_GetTicks() - tourelle[k].prev > tourelle[k].del )
            {
                tourelle[k].prev = SDL_GetTicks();
                createInert( tourelle[k].loc, plane, world, tourelle[k].missile );
            } 
        }

        // Déplacement
        if ( vie > 0 )
        {
            force = b2Vec2_zero;

            if ( thrust_left )
                force.x -= 1.0f;
            if ( thrust_right )
                force.x += 1.0f;
            if ( thrust_up )
                force.y -= 1.0f;
            if ( thrust_down )
                force.y += 1.0f;

            plane->ApplyLinearImpulseToCenter( 0.004f * force, true );
        }

        // It is generally best to keep the time step and iterations fixed.
        if ( !pause )
			world.Step(timeStep, velocityIterations, positionIterations);

        // Contact sur plane
        position = plane->GetPosition();
        pxpos.x = position.x * MULTI ;
        pxpos.y = position.y * MULTI ;

        for ( b2ContactEdge* ce( plane->GetContactList() ); ce && vie > 0; ce = ce->next )
            if ( ce->contact->IsTouching() )
            {
                vie -- ;
                filledCircleRGBA( screen, pxpos.x, pxpos.y, 13, 255, 255, 255, 255 );
                circleRGBA( persistant, pxpos.x, pxpos.y, 13, 255, 128, 0, 255 );
            }

        // Mise à jour des trajectoires
        updateMissiles( world, plane, foresee );

        // [4.3] Dessin des composants
        for ( b2Body* b( world.GetBodyList() ); b; b = b->GetNext() )
        {
            // Besoin d'afficher ?
            descr = (Entity*)b->GetUserData();

            if ( descr == nullptr || descr->id == LAND )
                continue ;

            // Affichage classique
            position = b->GetPosition();

            pxpos.x = position.x * MULTI ;
            pxpos.y = position.y * MULTI ;

            circleColor( screen, pxpos.x, pxpos.y, descr->rayon * MULTI, 0xddddddff );

            // Affichage persistant
            position -= timeStep * b->GetLinearVelocity();

            pxpos2.x = position.x * MULTI ;
            pxpos2.y = position.y * MULTI ;

            lineColor( persistant, pxpos.x, pxpos.y, pxpos2.x, pxpos2.y, weap_color[descr->id] );
            lineColor( screen, pxpos.x, pxpos.y, pxpos2.x, pxpos2.y, weap_color[descr->id] );
        }
        for ( unsigned int k(0); k < tourelle.size(); k++ )
        {
            circleColor( screen, tourelle[k].loc.x * MULTI, tourelle[k].loc.y * MULTI, 0.05f * MULTI, weap_color[tourelle[k].missile] );
        }

        // 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 = "Vie : " ;
        msg += std::to_string( vie ) ;
        msg += " | " ;
        for ( unsigned int i(0); i < vie ; i ++ )
            msg += "#" ;
        for ( unsigned int i(vie); i < 30 ; i ++ )
            msg += "-" ;
        stringRGBA( screen, 16, 25, msg.c_str(), 0, 255, 0, 255 );

        msg = "Delai tourelle : " ;
        msg += std::to_string( weap_wait ) ;
        msg += " ms." ;
        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;
}