#include <gint/display.h>
#include <gint/drivers/r61524.h>
#include <gint/rtc.h>

#include <gint/keyboard.h>
#include <gint/gint.h>
#include <gint/defs/types.h>
#include <fxlibc/printf.h>

#include <libprof.h>
#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>

#include <math.h>
#include <vector>

#include "parameters.h"
#include "colors.h"

#include "include/camera.h"
#include "include/segment.h"
#include "include/circuit.h"
#include "include/drawstuff.h"
#include "include/clouds.h"
#include "include/cars.h"
#include "include/menus.h"

#include <gint/kmalloc.h>
#include <stdio.h>

extern bopti_image_t player;
extern font_t speedfont, startseq, plate;
extern bopti_image_t speedhud;
extern bopti_image_t flag, bigflag;

int CurrentCircuitBiome = PLAINS;

std::vector<Segment*> circuit;
std::vector<Clouds*> nuages;
std::vector<Cars*> traffic;
int MAX_SEGMENT=0;
camera *cam;


uint8_t DiffLevel = 1;
uint8_t CarsNumb = 1;
char PlayerName[3] = "SLY";

struct DataPerf
{
    uint8_t update=0;
    uint8_t project=0;
    uint8_t render=0;
};

#define MAXDATA 120
DataPerf GraphPerf[ MAXDATA ];
unsigned int DataIndex=0;

uint8_t NB_CARS_TRAFFIC=200;

bool stop = false;
bool exitToOS = false;
bool record = false;
bool screenshot = false;

bool ShowDebug1 = false;
bool ShowDebug2 = false;
bool ShowDebug3 = false;

bool BDrawDeco = true;
bool BDrawClds = true;
bool BDrawCars = true;
bool BDrawBack = true;
bool BDrawFPS = false;

bool OptionMode = false;
bool PauseMode = false;
bool SkipTime = false;

// for multilap
bool multilap = false;
uint8_t numlap = 1;
uint8_t currentLap = 1;


uint16_t currentcurve=0;
uint8_t shiftcolor=0;


float speed = 0.0;
float maxspeedforward = 5.0;
float maxspeedbackward = 2.0;
int direction = 1;
bool speedcontrol = false;


uint8_t minYRoad = 224;     // We will track the upper Y (in fact the minimum Y during the RoadDrawing to optimize the rendering of the Sky
int8_t viewside = 0;

int deltaFarbackground = 0;
int deltaNearbackground = 0;
int lastindex=0;

int CC=0; // current curve
int CS=0;



void initEverything( void )
{
    CurrentCircuitBiome = PLAINS;

    circuit.clear();
    nuages.clear();
    traffic.clear();
    MAX_SEGMENT=0;

    DataIndex=0;

    stop = false;
    record = false;
    screenshot = false;

    ShowDebug1 = false;
    ShowDebug2 = false;
    ShowDebug3 = false;

    BDrawDeco = true;
    BDrawClds = true;
    BDrawCars = true;
    BDrawFPS = false;


    currentcurve=0;
    shiftcolor=0;


    speed = 0.0;
    maxspeedforward = 5.0;
    maxspeedbackward = 2.0;
    direction = 1;
    speedcontrol = false;


    minYRoad = 224;     // We will track the upper Y (in fact the minimum Y during the RoadDrawing to optimize the rendering of the Sky
    viewside = 0;

    deltaFarbackground = 0;
    deltaNearbackground = 0;
    lastindex=0;

    CC=0; // current curve
    CS=0;

    multilap = false;
    numlap = 1;
    currentLap = 1;
}


static void get_inputs( float dt, int index  )
{
    CC = circuit[index]->Curve;
    CS = circuit[index]->Slope;

    key_event_t ev;
    while((ev = pollevent()).type != KEYEV_NONE)
    {

    }
    speedcontrol = false;


    viewside=0;
    if (CC<0)
    {
        viewside=-1;

        if (CurrentCircuitBiome==FINLAND) cam->decX( CC*dt*speed/50 );              // very hard to turn in Finland with snow
        else if (CurrentCircuitBiome==AFRICA) cam->decX( CC*dt*speed/75 );          // hard in Africa with dirt
        else cam->decX( CC*dt*speed/100 );                                          // more easy on normal roads
    }
    else if (CC>0)
    {
        viewside=+1;

        if (CurrentCircuitBiome==FINLAND) cam->decX( CC*dt*speed/50 );              // very hard to turn in Finland with snow
        else if (CurrentCircuitBiome==AFRICA) cam->decX( CC*dt*speed/75 );          // hard in Africa with dirt
        else cam->decX( CC*dt*speed/100 );                                          // more easy on normal roads
    }

    if(keydown(KEY_LEFT))
    {
        cam->decX(5.0*speed*direction);

        viewside=-1;
        if (CC<0) viewside=-2;  // We are in a curve and turning
        if (CC>0) viewside=0;
    }

    if(keydown(KEY_RIGHT))
    {
        cam->incX(5.0*speed*direction);

        viewside=1;
        if (CC>0) viewside=+2;
        if (CC<0) viewside=0;
    }

    if(keydown(KEY_SHIFT))              // Accelerates
    {
        if (direction==-1 && speed > 0)
        {
            direction = -1;
            speed -= 0.5;
            if (speed<0) speed=0;
            cam->decZ(speed*dt);
        }
        else
        {
            direction = 1;
            speed+=0.05;
            if (speed>maxspeedforward) speed=maxspeedforward;
            cam->incZ(speed*dt);
        }

        speedcontrol = true;
    }

    if(keydown(KEY_ALPHA))              // breaks or rear if speed <0
    {
        if (direction==1 && speed > 0)
        {
            direction = 1;
            speed -= 0.5;
            if (speed<0) speed=0;
            cam->incZ(speed*dt);
        }
        else
        {
            direction = -1;
            speed+=0.025;
            if (speed>maxspeedbackward) speed=maxspeedbackward;
            cam->decZ(speed*dt);
        }

        speedcontrol = true;
    }

    if(keydown(KEY_EXIT)) drawPauseQuit();

    if(keydown(KEY_OPTN)) drawOptions();

#if IS_FXLIB==1
    if(keydown(KEY_F1))    BDrawDeco = !BDrawDeco;
    if(keydown(KEY_F2))    BDrawClds = !BDrawClds;
    if(keydown(KEY_F3))    BDrawCars = !BDrawCars;
    if(keydown(KEY_F4))    BDrawBack = !BDrawBack;
    if(keydown(KEY_F5))    BDrawFPS = !BDrawFPS;

    if(keydown(KEY_7))    screenshot = true;
    if(keydown(KEY_8))    record = !record;
#endif // IS_FXLIB

    if (speedcontrol==false)
    {
        speed-=0.1;
        if (speed<0) speed=0;

        if (direction==1)
            cam->incZ(speed*dt);
        else
            cam->decZ(speed*dt);
    }


    // Adjust position of the background
    if (lastindex!=index)
    {
        deltaFarbackground -= CC*speed*dt/250;
        deltaNearbackground -= CC*speed*dt/100;
    }
    lastindex = index;

    // adjust speed if we drive on the side of the road
    if (fround(cam->cX)<-1*ROAD_WIDTH && speed>2.0) speed=2.0;
    if (fround(cam->cX)>ROAD_WIDTH && speed>2.0) speed=2.0;

    if (fround(cam->cX)<-1.35*ROAD_WIDTH && speed>0.0)
    {
        speed=0.0;
        cam->cX=fix(-0.0*ROAD_WIDTH);        //set the car in the center of the road (was formerly -0.75)
    }
    if (fround(cam->cX)>1.35*ROAD_WIDTH && speed>0.0)
    {
        speed=0.0;
        cam->cX=fix(0.0*ROAD_WIDTH);        //set the car in the center of the road (was formerly 0.75)
    }
}


static void get_minimum_inputs( void  )
{
    key_event_t ev;
    while((ev = pollevent()).type != KEYEV_NONE)
    {

    }

    if(keydown(KEY_EXIT)) drawPauseQuit();

    if(keydown(KEY_OPTN)) drawOptions();

#if IS_FXLIB==1
    if(keydown(KEY_F1))    BDrawDeco = !BDrawDeco;
    if(keydown(KEY_F2))    BDrawClds = !BDrawClds;
    if(keydown(KEY_F3))    BDrawCars = !BDrawCars;
    if(keydown(KEY_F4))    BDrawBack = !BDrawBack;
    if(keydown(KEY_F5))    BDrawFPS = !BDrawFPS;

    if(keydown(KEY_7))    screenshot = true;
    if(keydown(KEY_8))    record = !record;
#endif // IS_FXLIB
}



int main(void)
{
    __printf_enable_fp();
    __printf_enable_fixed();

#if IS_FXLIB==1
    usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
    usb_open(interfaces, GINT_CALL_NULL);
#endif


    kmalloc_arena_t *_uram = kmalloc_get_arena("_uram");
    kmalloc_gint_stats_t *_uram_stats;


    srand( rtc_ticks() );

    prof_init();



    drawStartTitle();


    //char texttosend[1024];



    exitToOS = false;
    do
    {

        /*
                _uram_stats = kmalloc_get_gint_stats(_uram);
                sprintf( texttosend, "[Begining of loop] Memory Status - Used: %d - Free: %d - Peak Used: %d", _uram_stats->used_memory, _uram_stats->free_memory, _uram_stats->peak_used_memory);
                usb_fxlink_text(texttosend, 0);
        */

        // Try to restart from  very begining
        initEverything();

        prof_t perf_update, perf_create, perf_project, perf_render;
        int32_t start_time = 100000000;
        uint32_t score=0;
        uint8_t stage=0;
        int8_t selectedCircuit=0, secondvalidation=0;
        uint32_t time_update=0, time_create=0, time_project=0, time_render=0;


        exitToOS = false;

        stop = false;
        bool exitflag = false;


        do
        {
            exitflag = false;
            stage = drawMainMenu();

            if (stage==0)
            {
                selectedCircuit = drawMenuCircuitSelect();

                if (selectedCircuit != -1) secondvalidation = drawMenuCircuitDetails( selectedCircuit, 0 );
                if (selectedCircuit != -1 && secondvalidation != -1) exitflag=true;
            }
            else if (stage==1)
            {
                selectedCircuit = drawMenuCircuitSelect();

                if (selectedCircuit != -1) secondvalidation = drawMenuCircuitDetails( selectedCircuit, 1 );
                if (selectedCircuit != -1 && secondvalidation != -1) exitflag=true;
            }
            else if (stage==2)
            {
                drawGameOptions();
            }
            else if (stage==3)
            {
                drawOptions();
            }
            else if (stage==4)
            {
                drawCredit();
            }
            else if (stage==5)
            {
                exitToOS = true;
                exitflag=true;
            }
        }
        while (!exitflag);

        if (!exitToOS)
        {
            stage = selectedCircuit;

            if (selectedCircuit==0) CurrentCircuitBiome = PLAINS, multilap = false, currentLap = 1, numlap = 1;
            else if (selectedCircuit==1) CurrentCircuitBiome = DESERT, multilap = false, currentLap = 1, numlap = 1;
            else if (selectedCircuit==2) CurrentCircuitBiome = USA, multilap = false, currentLap = 1, numlap = 1;
            else if (selectedCircuit==3) CurrentCircuitBiome = FINLAND, multilap = false, currentLap = 1, numlap = 1;
            else if (selectedCircuit==4) CurrentCircuitBiome = AFRICA, multilap = false, currentLap = 1, numlap = 1;
            else if (selectedCircuit==5) CurrentCircuitBiome = PLAINS, multilap = true, currentLap = 1, numlap = 3;
            else if (selectedCircuit==6) CurrentCircuitBiome = DESERT, multilap = true, currentLap = 1, numlap = 3;
            else if (selectedCircuit==7) CurrentCircuitBiome = USA, multilap = true, currentLap = 1, numlap = 3;
            else if (selectedCircuit==8) CurrentCircuitBiome = FINLAND, multilap = true, currentLap = 1, numlap = 3;
            else if (selectedCircuit==9) CurrentCircuitBiome = AFRICA, multilap = true, currentLap = 1, numlap = 3;
            else CurrentCircuitBiome = PLAINS, multilap = false, currentLap = 1, numlap = 1;


            int nbInterestingSegments = (MAX_RENDER_DISTANCE / SEGMENT_LENGTH); // the number of segments to be projected considering the rendering distance


            perf_create = prof_make();
            prof_enter(perf_create);



            initData( );            // Positioning of the Camera

            createCircuit( selectedCircuit );        // Creates the circuit

            MAX_SEGMENT = circuit.size();
            uint32_t maxDistance = (MAX_SEGMENT-nbInterestingSegments-5)*SEGMENT_LENGTH;

            putBillBoards();

            createClouds();         // Creates the Sky and Clouds

            createTraffic( MAX_SEGMENT );        // Creates the cars

            prepareDecoration( CurrentCircuitBiome );    // Prepares the multiple variations of Decoration (image scaling)

            prepareTraffic();       // Prepares the multiple variations of Cars (image scaling)



            prof_leave(perf_create);
            time_create = prof_time(perf_create);

            //--------------


            int indexstart = 0;
            int indexend = 0;



            float dt=0;
            uint16_t l=0;
            uint32_t remaining_time;

            DataIndex = 0;

            bool initiateStart = true;  // are we in the start sequence ?
            int32_t startCount = 6000000;
            int32_t startRemaining;


            bool finishSequence = false;  // Did we reached the end of the race ?
            bool failSequence = false;    // Did we run out of time ?


            while (!stop)
            {


                //new position
                if (!SkipTime)
                {
                    dt = ((float) (time_update+time_render+time_project) / 1000.0);
                }
                else
                {
                    dt = 0;
                    SkipTime=false;
                }


                perf_update = prof_make();
                prof_enter(perf_update);

                if (!initiateStart && !finishSequence && !failSequence) get_inputs( dt, indexstart );       // The racer is not started : no need to get inputs and we are not in fail or finish sequence
                else get_minimum_inputs();      // the we can just have minimum inputs

//former position


                if (initiateStart)
                {
                    startCount -= dt*1000;
                    startRemaining = ((float) (startCount) / 100000.0);
                }

                start_time -= dt*1000;
                if (start_time<0) start_time=0;
                remaining_time = ((float) (start_time) / 1000000.0);

                //--------------
                if (fround(cam->cZ)<=0) cam->cZ=fixdouble(0.0);
                //if (fround(cam->cZ)>=maxDistance) cam->cZ=fixdouble(maxDistance);

                indexstart = fround(cam->cZ) / SEGMENT_LENGTH;
                if (indexstart<0) indexstart=0;

                indexend = indexstart+nbInterestingSegments+1;
                //if (indexstart>MAX_SEGMENT-nbInterestingSegments-2) indexstart=MAX_SEGMENT-nbInterestingSegments-2;


                prof_leave(perf_update);
                time_update = prof_time(perf_update);

                //--------------

                perf_project = prof_make();
                prof_enter(perf_project);

                minYRoad = SCREEN_HEIGHT;

                if (BDrawCars)
                {
                    updateTraffic( dt, maxDistance );

                    for (unsigned int k=0; k<traffic.size(); k++)       // Need to project 1 more segment than actual drawing
                    {
                        uint16_t CarSegment = traffic[k]->wZ / SEGMENT_LENGTH;
                        if (CarSegment>=indexstart && CarSegment<indexend)
                        {
                            //traffic[k]->visible = true;
                            circuit[CarSegment]->CarList.push_back(k);
                        }
                        //else
                        //traffic[k]->visible = false;
                    }

                    float roadpart = f2float(cam->cX)/(float) ROAD_WIDTH;

                    // Update car positions : car with higher speed change line not to collide with player
                    for(unsigned int l=0; l<circuit[indexstart]->CarList.size(); l++ )  // For all cars inside that road segment
                    {
                        uint8_t indexCar = circuit[indexstart]->CarList[l];

                        if (traffic[indexCar]->wX>(roadpart-0.25f) && traffic[indexCar]->wX<(roadpart+0.25f) && traffic[indexCar]->Speed>speed)
                        {
                            if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->wX-=0.5;            // if we are on one of the most right lanes, we overtake by the left
                            else traffic[indexCar]->wX<-0.25;               // else we change to the lane on the right
                        }
                    }

                    for( unsigned int l=0; l<circuit[indexstart+1]->CarList.size(); l++ )  // For all cars inside that road segment
                    {
                        uint8_t indexCar = circuit[indexstart+1]->CarList[l];

                        if (traffic[indexCar]->wX>(roadpart-0.22f) && traffic[indexCar]->wX<(roadpart+0.22f) && traffic[indexCar]->Speed<speed)
                        {
                            speed = traffic[indexCar]->Speed - 0.5;
                        }
                    }

                    if (DiffLevel>=1)       // Normal or Hard : cars can overtake the other opponents
                    {

                        for( unsigned int k=indexstart; k<indexend; k++)
                        {
                            for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ )  // For all cars inside that road segment
                            {
                                uint8_t indexCar = circuit[k]->CarList[l];

                                for( unsigned int m=0; m<circuit[k+1]->CarList.size(); m++ )
                                {
                                    uint8_t indexCar2 = circuit[k+1]->CarList[m];

                                    if (traffic[indexCar]->wX==traffic[indexCar2]->wX && traffic[indexCar]->Speed>=traffic[indexCar2]->Speed)   // a car is following and other one just in front of ehr, at the same speed or quicker
                                    {
                                        //if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->wX-=0.5;
                                        //else traffic[indexCar]->wX=-0.25;

                                        if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->Turn = -1;
                                        else traffic[indexCar]->Turn = +1;
                                    }
                                }
                            }
                        }

                        if (DiffLevel==2)   // if difficulty set to hard, we allo changing lane (random based if not currently overtaking an other car)
                        {

                            for (unsigned int u=indexstart; u<indexend; u++)
                            {
                                for (unsigned int v=0; v<circuit[u]->CarList.size(); v++)
                                {
                                    unsigned int k = circuit[u]->CarList[v];

                                    if (traffic[k]->Turn == 0)
                                    {
                                        int mov = rand() % 100;

                                        if (mov>=95) // we let the car change its lane in 5% of cases
                                        {
                                            if (traffic[k]->wX==-0.75) traffic[k]->Turn = +1;
                                            else if (traffic[k]->wX==0.75) traffic[k]->Turn = -1;
                                            else
                                            {
                                                int side = -1 + (rand()%3);
                                                traffic[k]->Turn = side;
                                            }
                                        }
                                    }
                                }
                            }
                        }


                        for (unsigned int u=indexstart; u<indexend; u++)
                        {
                            for (unsigned int v=0; v<circuit[u]->CarList.size(); v++)
                            {

                                unsigned int k = circuit[u]->CarList[v];



                                /*
                                if (traffic[k]->Turn == -1)
                                {
                                    traffic[k]->wX -= 0.10;
                                    if (traffic[k]->wX==-0.75 || traffic[k]->wX==-0.25 || traffic[k]->wX==0.25) traffic[k]->Turn = 0;
                                }
                                else if (traffic[k]->Turn == 1)
                                {
                                    traffic[k]->wX += 0.10;
                                    if (traffic[k]->wX==-0.25 || traffic[k]->wX==0.25 || traffic[k]->wX==0.75) traffic[k]->Turn = 0;
                                }
                                */

                                traffic[k]->wX += (float) traffic[k]->Turn * 0.10f;
                                if (traffic[k]->wX>=-0.80 && traffic[k]->wX<=-0.70) traffic[k]->wX = -0.75, traffic[k]->Turn = 0;
                                else if (traffic[k]->wX>=-0.30 && traffic[k]->wX<=-0.20) traffic[k]->wX = -0.25, traffic[k]->Turn = 0;
                                else if (traffic[k]->wX>=0.20 && traffic[k]->wX<=0.30) traffic[k]->wX = 0.25, traffic[k]->Turn = 0;
                                else if (traffic[k]->wX>=0.70 && traffic[k]->wX<=0.80) traffic[k]->wX = 0.75, traffic[k]->Turn = 0;
                            }
                        }

                    }
                }

                uint16_t cumulCurve=0;

                for (int k=indexstart; k<=indexend; k++)        // Need to project 1 more segment than actual drawing
                {
                    projectCircuitFP( k );                      // We project the current segment

                    //if (k!= indexend && circuit[k]->Y < minYRoad)                // This is a trick to save precious time while drawing the Sky
                    if (circuit[k]->Y <= minYRoad)    minYRoad = circuit[k]->Y;

                    circuit[k]->CumulatedCurve = cumulCurve;    // This is the curve accumulated when we are drawing curves
                    cumulCurve += circuit[k]->Curve;

                    if (BDrawCars)
                        for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ )  // For all cars inside that road segment
                        {
                            uint8_t indexCar = circuit[k]->CarList[l];
                            traffic[indexCar]->Project3DFP( cam, k );
                        }
                }


                prof_leave(perf_project);
                time_project = prof_time(perf_project);

                //--------------

                perf_render = prof_make();
                prof_enter(perf_render);


                //if (BDrawBack) drawSkyOptimised( DAY_BLUE_SKY );
                //else drawSkyFull( DAY_BLUE_SKY );

                //drawSkyFull( DAY_BLUE_SKY );
                drawSkyQuick( DAY_BLUE_SKY );


                if (BDrawClds)
                {
                    drawClouds( );
                }

                if (BDrawBack)
                {
                    drawFarBackground( deltaFarbackground, CurrentCircuitBiome );
                    drawNearBackground( deltaNearbackground, CurrentCircuitBiome );
                }


                cam->cY = fix( 350+2*CS ) + interpolatePositionY(fround(cam->cZ) );

                for( int k=indexend-1; k>=indexstart; k--)
                {
                    currentcurve = circuit[k]->CumulatedCurve;

                    drawCircuitSegment( k );

                    if (BDrawDeco)
                        drawDecoration( k );

                    if (BDrawCars)
                        for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ )  // For all cars inside that road segment
                        {
                            uint8_t indexCar = circuit[k]->CarList[l];
                            drawTraffic( indexCar );
                        }

                    if (BDrawCars) circuit[k]->CarList.clear();
                }


                if (circuit[indexstart]->Special == CHECKPOINT && circuit[indexstart]->CheckValidated == false)
                {
                    start_time+=60000000;
                    circuit[indexstart]->CheckValidated = true;
                }
                else if (circuit[indexstart]->Special == FINISH && circuit[indexstart]->CheckValidated == false)
                {
                    if (currentLap<numlap)
                    {
                        currentLap++;
                        cam->decZ( indexstart*SEGMENT_LENGTH );
                        for( unsigned int k=0; k<traffic.size(); k++ )  traffic[k]->wZ-=indexstart*SEGMENT_LENGTH;
                    }
                    else
                    {
                        circuit[indexstart]->CheckValidated = true;
                        finishSequence=true;
                    }
                }
                else if (circuit[indexstart]->Special == START && circuit[indexstart]->CheckValidated == false)
                {
                    start_time=99000000;
                    circuit[indexstart]->CheckValidated = true;
                }


                // Draw a message when time is over
                if (start_time<=0)
                {
                    failSequence=true;
                }


                int mod_base=20;
                int mod_comp=10;


                if (abs(speed)<1.0)  mod_base = 30, mod_comp = 15;
                else if (abs(speed)<2.0)  mod_base = 20, mod_comp = 10;
                else if (abs(speed)<3.0)  mod_base = 16, mod_comp = 8;
                else if (abs(speed)<4.0)  mod_base = 12, mod_comp = 6;
                else if (abs(speed)<5.0)  mod_base = 8, mod_comp = 4;
                else mod_base = 4, mod_comp = 2;


                if ((speed==0) || (l%mod_base<=mod_comp))    // the small rick to have the speed impression on wheels and to have the correct view of the car during turns
                {
                    if (viewside==-2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 99,1,80,46,  DIMAGE_NONE);
                    else if (viewside==-1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 181,1,74,46,  DIMAGE_NONE);
                    else if (viewside==0) dsubimage( SCREEN_CX-36, SCREEN_HEIGHT-46, &player, 257,1,72,46,  DIMAGE_NONE);
                    else if (viewside==1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 331,1,74,46,  DIMAGE_NONE);
                    else if (viewside==2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 407,1,80,46,  DIMAGE_NONE);
                }
                else
                {
                    if (viewside==-2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 99,49,80,46,  DIMAGE_NONE);
                    else if (viewside==-1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 181,49,74,46,  DIMAGE_NONE);
                    else if (viewside==0) dsubimage( SCREEN_CX-36, SCREEN_HEIGHT-46, &player, 257,49,72,46,  DIMAGE_NONE);
                    else if (viewside==1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 331,49,74,46,  DIMAGE_NONE);
                    else if (viewside==2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 407,49,80,46,  DIMAGE_NONE);
                }

                dfont(&plate);
                if (viewside==-2) dprint_opt( 205, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2]  );
                else if (viewside==-1) dprint_opt( 202, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2]  );
                else if (viewside==0) dprint_opt( 197, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2]  );
                else if (viewside==1) dprint_opt( 193, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2]  );
                else if (viewside==2) dprint_opt( 189, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2]  );

                score = fround(cam->cZ)/100;


                float drawspeed = (float) (speed*5.0f);


                dsubimage( 5, 5, &speedhud, 0, 117, 37, 13,  DIMAGE_NONE);          // speed logo
                dsubimage( 135, 5, &speedhud, 0, 130, 37, 13,  DIMAGE_NONE);          // timer logo

                dsubimage( 245, 5, &speedhud, 37, 117, 38, 13,  DIMAGE_NONE);          // speed logo
                dsubimage( 245, 25, &speedhud, 37, 130, 38, 13,  DIMAGE_NONE);          // timer logo

                dfont(&speedfont);
                if (drawspeed==0.0)
                {
                    dprint_opt(122,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "0 :", drawspeed );
                    dprint_opt(120,1, C_RGB(255,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "0 :", drawspeed );
                }
                else
                {
                    dprint_opt(122,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.1f :", drawspeed );
                    dprint_opt(120,1, C_RGB(255,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.1f :", drawspeed );      //the ':' char corresponds to "Km/h"
                }

                dprint_opt(233,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", remaining_time );
                dprint_opt(231,1, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", remaining_time );    // the ';' char corresponds to "s"

                dprint_opt(390,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", score );
                dprint_opt(388,1, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", score );    // the ';' char corresponds to "s"

                dprint_opt(390,23, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", stage );
                dprint_opt(388,21, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", stage );    // the ';' char corresponds to "s"


                if (multilap)
                {
                    dsubimage( 135, 25, &speedhud, 28, 143, 27, 13, DIMAGE_NONE);  // Lap logo

                    dprint_opt(233,23, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%d/%d", currentLap, numlap );
                    dprint_opt(231,21, C_RGB(255,0,255), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%d/%d", currentLap, numlap );    // the ';' char corresponds to "s"
                }


                if (drawspeed==0.0) dsubimage( 48, 25, &speedhud, 0, 0, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<3.30) dsubimage( 48, 25, &speedhud, 0, 13, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<6.70) dsubimage( 48, 25, &speedhud, 0, 26, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<10.0) dsubimage( 48, 25, &speedhud, 0, 39, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<13.3) dsubimage( 48, 25, &speedhud, 0, 52, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<16.7) dsubimage( 48, 25, &speedhud, 0, 65, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<20.0) dsubimage( 48, 25, &speedhud, 0, 78, 76, 13,  DIMAGE_NONE);
                else if (drawspeed<23.3) dsubimage( 48, 25, &speedhud, 0, 91, 76, 13,  DIMAGE_NONE);
                else dsubimage( 48, 25, &speedhud, 0, 104, 76, 13,  DIMAGE_NONE);



                if (finishSequence)
                {
                    speed = 0;

                    dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );

                    dfont(&startseq);

                    dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
                    dprint_opt(198,112, C_RGB(0,255,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );

                    dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "WIN" );
                    dprint_opt(198,112, C_RGB(0,255,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "WIN" );
                }


                if (failSequence)
                {
                    speed = 0;

                    dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );

                    dfont(&startseq);

                    dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
                    dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );

                    dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOOSE" );
                    dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOOSE" );
                }


                if (initiateStart)          // We are in the start sequence with the countdown
                {
                    dfont(&startseq);
                    if (startRemaining<54 && startRemaining>46)
                    {
                        dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">5<" );
                        dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">5<" );
                    }
                    else if (startRemaining<44 && startRemaining>36)
                    {
                        dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">4<" );
                        dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">4<" );
                    }
                    else if (startRemaining<34 && startRemaining>26)
                    {
                        dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">3<" );
                        dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">3<" );
                    }
                    else if (startRemaining<24 && startRemaining>16)
                    {
                        dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">2<" );
                        dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">2<" );
                    }
                    else if (startRemaining<14 && startRemaining>6)
                    {
                        dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">1<" );
                        dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">1<" );
                    }
                    else if (startRemaining<4 && startRemaining>-4)
                    {
                        dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );
                    }
                    else if (startRemaining<-4)
                    {
                        start_time = 99000000;
                        initiateStart=false;
                    }

                }

                //dupdate();

                //dprint( 3, 202, C_BLACK, "%.0f", dt );
                //dprint( 1, 200, C_WHITE, "%.0f", dt );

                if (BDrawFPS)
                {
                    dfont(&speedfont);
                    dsubimage( 5, 205, &speedhud, 0, 143, 27, 13,  DIMAGE_NONE);          // fps logo

                    dprint_opt(42,202, C_RGB(0,0,0), C_NONE, DTEXT_LEFT, DTEXT_TOP, "%.0f <", (float) (1000.0f/dt) );       // the '>' symbol corresponds to "fps"
                    dprint_opt(40,200, C_RGB(255,255,0), C_NONE, DTEXT_LEFT, DTEXT_TOP, "%.0f <", (float) (1000.0f/dt) );


                    GraphPerf[DataIndex].update = (uint8_t) (time_update/1000);
                    GraphPerf[DataIndex].project = (uint8_t) (time_project/1000);
                    GraphPerf[DataIndex].render = (uint8_t) (time_render/1000);

                    DataIndex++;
                    DataIndex = DataIndex % MAXDATA;

                    dline( 249, 201, 375, 201, C_BLACK );
                    dline( 249, 202, 375, 202, C_BLACK );

                    dline( 249, 200-33, 375, 200-33, C_BLACK );
                    dline( 249, 200-40, 375, 200-40, C_BLACK );
                    dline( 249, 200-50, 375, 200-50, C_BLACK );

                    dline( 249, 201, 249, 100, C_BLACK );
                    dline( 248, 201, 248, 100, C_BLACK );

                    for( unsigned int k=0; k<MAXDATA-1; k++)
                    {
                        dpixel( 250+k, 200-GraphPerf[k].update, C_RGB(255,0,0) );
                        dpixel( 250+k, 201-GraphPerf[k].update, C_RGB(255,0,0) );

                        dpixel( 250+k, 200-GraphPerf[k].update-GraphPerf[k].project, C_RGB(0,255,0) );
                        dpixel( 250+k, 201-GraphPerf[k].update-GraphPerf[k].project, C_RGB(0,255,0) );

                        dpixel( 250+k, 200-GraphPerf[k].update-GraphPerf[k].project-GraphPerf[k].render, C_RGB(0,0,255) );
                        dpixel( 250+k, 201-GraphPerf[k].update-GraphPerf[k].project-GraphPerf[k].render, C_RGB(0,0,255) );
                    }
                }

                r61524_display(gint_vram, 0, DHEIGHT, R61524_DMA_WAIT);


                prof_leave(perf_render);
                time_render = prof_time(perf_render);

#if IS_FXLIB==1
                if (screenshot && usb_is_open())
                {
                    usb_fxlink_screenshot(false);
                    screenshot = false;
                }

                if(record && usb_is_open())
                {
                    usb_fxlink_videocapture(false);
                }
#endif

                l++;
            }

            /*
                    _uram_stats = kmalloc_get_gint_stats(_uram);
                    sprintf( texttosend, "Memory Status - Used: %d - Free: %d - Peak Used: %d", _uram_stats->used_memory, _uram_stats->free_memory, _uram_stats->peak_used_memory);
                    usb_fxlink_text(texttosend, 0);
            */

            // Free all memory

            freeTraffic();

            freeDecoration();

            for(unsigned int i=0; i<circuit.size(); i++ )    delete circuit[i];  // Not to forget to delete each elements
            circuit.clear();

            for(unsigned int i=0; i<nuages.size(); i++ )    delete nuages[i];    // Not to forget to delete each elements
            nuages.clear();

            for(unsigned int i=0; i<traffic.size(); i++ )    delete traffic[i];  // Not to forget to delete each elements
            traffic.clear();

        }
    }
    while (exitToOS==false);

    prof_quit();

#if IS_FXLIB==1
    usb_close();
#endif

    delete cam;


    return 1;
}
