#include <gint/gint.h>
#include <gint/hardware.h>
#include <gint/kmalloc.h>
#include <gint/display.h>

#include <libprof.h>
#include <stdlib.h>

#include <SDL/SDL.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <SDL/SDL_image.h>

#include "clock.h"


#define GRID_H 10
#define GRID_W 16
#define CELL_SIZE   15

#define SCREEN_W 396
#define SCREEN_H 224

#define GRID_CENTER_X (SCREEN_W/2-40)
#define GRID_Y (SCREEN_H-14)

cSDL_Font *font;

bool dropped = false;

unsigned char curBlockX=6;
unsigned char curBlockY=10;

unsigned char curBlock[2][2];

unsigned char next1block[2][2];
unsigned char next2block[2][2];
unsigned char next3block[2][2];


SDL_Surface* screen = NULL;
SDL_Surface* background = NULL;
SDL_Surface* blocks = NULL;

typedef struct
{
    int8_t color;
    bool marked;
} block;

block GridBoard[GRID_W][GRID_H];

typedef struct
{
    float x;
    float speed;
    uint8_t column;
    uint8_t positionincolumn;
} sweepline;

sweepline Sweeper;

uint32_t timerstart=0;
uint32_t currenttimer=0;
float currentX=0;

uint32_t time_frame=0;


void drawGrid( SDL_Surface *surf )
{
    for(int x=0; x<GRID_W/2; x++)
    {
        for(int y=0; y<GRID_H; y++)
        {
            rectangleRGBA( surf, GRID_CENTER_X-x*CELL_SIZE, GRID_Y-y*CELL_SIZE, GRID_CENTER_X-(x+1)*CELL_SIZE, GRID_Y-(y+1)*CELL_SIZE, 0, 0, 0, 255 );
            rectangleRGBA( surf, GRID_CENTER_X+x*CELL_SIZE, GRID_Y-y*CELL_SIZE, GRID_CENTER_X+(x+1)*CELL_SIZE, GRID_Y-(y+1)*CELL_SIZE, 0, 0, 0, 255 );
        }
    }
    for(int x=0; x<=GRID_W/2; x++)
    {
        for(int y=0; y<=GRID_H; y++)
        {
            rectangleRGBA( surf, GRID_CENTER_X-x*CELL_SIZE-1, GRID_Y-y*CELL_SIZE-1, GRID_CENTER_X-x*CELL_SIZE+1, GRID_Y-y*CELL_SIZE+1, 0, 0, 0, 255 );
            rectangleRGBA( surf, GRID_CENTER_X+x*CELL_SIZE-1, GRID_Y-y*CELL_SIZE-1, GRID_CENTER_X+x*CELL_SIZE+1, GRID_Y-y*CELL_SIZE+1, 0, 0, 0, 255 );
        }
    }
    rectangleRGBA(surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE-1, GRID_Y-GRID_H*CELL_SIZE-1, GRID_CENTER_X+GRID_W/2*CELL_SIZE+1, GRID_Y+1, 0, 0, 0, 255 );
}

void drawBlock( SDL_Surface *surf, unsigned int cellX, unsigned int cellY, int8_t blockColor )
{
    if (blockColor==-1) return; // this is an empty cell;


    SDL_Rect srcrect, dstrect;

    int theme = blockColor / 2;

    if (blockColor%2==0)
    {
        srcrect.x = theme*15;
        srcrect.y = 0;
        srcrect.h = 14;
        srcrect.w = 14;
    }
    else
    {
        srcrect.x = theme*15;
        srcrect.y = 15;
        srcrect.h = 14;
        srcrect.w = 14;
    }

    dstrect.x = GRID_CENTER_X-GRID_W/2*CELL_SIZE+cellX*CELL_SIZE+1;
    dstrect.y = GRID_Y-(cellY+1)*CELL_SIZE+1;
    dstrect.h = 14;
    dstrect.w = 14;

    SDL_BlitSurface(blocks, &srcrect, surf, &dstrect);
}

void drawBackground( SDL_Surface *surf )
{
    SDL_BlitSurface(background, NULL, surf, NULL);
}

void drawSweepLine( SDL_Surface *surf, uint32_t deltat )
{
    float x = Sweeper.x;
    x+=(float) (deltat*Sweeper.speed)/100.0f;

    if (x>GRID_W*CELL_SIZE) x=0;

    Sweeper.x=x;
    Sweeper.column = ((int) Sweeper.x) / CELL_SIZE;
    Sweeper.positionincolumn = ((int) Sweeper.x) % CELL_SIZE;

    for(int y=1; y<20; y++)
        if (x-y>=0) lineRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x-y, GRID_Y-GRID_H*CELL_SIZE-1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x-y, GRID_Y+1, 255, 255, 0, 255-y*10 );
        else lineRGBA( surf, GRID_CENTER_X+GRID_W/2*CELL_SIZE+x-y, GRID_Y-GRID_H*CELL_SIZE-1, GRID_CENTER_X+GRID_W/2*CELL_SIZE+x-y, GRID_Y+1, 255, 255, 0, 255-y*10 );

    lineRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x, GRID_Y-GRID_H*CELL_SIZE-1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x, GRID_Y+1, 255, 255, 0, 255 );
    lineRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x+1, GRID_Y-GRID_H*CELL_SIZE-1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x+1, GRID_Y+1, 255, 255, 0, 255 );
}

void initBoard( void )
{
    srand( 0 );

    for(int x=0; x<GRID_W; x++)
    {
        for(int y=0; y<GRID_H; y++)
        {
            GridBoard[x][y].color = (char) rand()%2;
            if (GridBoard[x][y].color<0) GridBoard[x][y].color=0;
            if (GridBoard[x][y].color>9) GridBoard[x][y].color=9;
            GridBoard[x][y].marked = false;
        }
    }

    for(int x=0; x<2; x++)
    {
        for(int y=0; y<2; y++)
        {
            curBlock[x][y] = (char) rand()%2;
            next1block[x][y] = (char) rand()%2;
            next2block[x][y] = (char) rand()%2;
            next3block[x][y] = (char) rand()%2;
        }
    }
}


void drawCurBlocks( SDL_Surface *surf )
{
    for(int x=0; x<2; x++)
    {
        for(int y=0; y<2; y++)
        {

        }
    }
}


void drawBlockGrid( SDL_Surface *surf )
{
    for(int x=0; x<GRID_W; x++)
    {
        for(int y=0; y<GRID_H; y++)
        {
            drawBlock( surf, x, y, (int) GridBoard[x][y].color );
        }
    }
}

void checkSquareGroups( SDL_Surface *surf  )
{
    for(int x=0; x<GRID_W-1; x++)
    {
        if (x>=Sweeper.column)
            for(int y=0; y<GRID_H-1; y++)
            {
                if (GridBoard[x][y].color==-1) break; // the cell is empty so we can jump to next column

                if (GridBoard[x][y].color==GridBoard[x+1][y].color && GridBoard[x][y].color==GridBoard[x][y+1].color && GridBoard[x][y].color==GridBoard[x+1][y+1].color)
                {
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x*CELL_SIZE+1, GRID_Y-(y+1)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE-1, GRID_Y-(y)*CELL_SIZE-1, 255, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE+1, GRID_Y-(y+1)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+2)*CELL_SIZE-1, GRID_Y-(y)*CELL_SIZE-1, 255, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x*CELL_SIZE+1, GRID_Y-(y+2)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE-1, GRID_Y-(y+1)*CELL_SIZE-1, 255, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE+1, GRID_Y-(y+2)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+2)*CELL_SIZE-1, GRID_Y-(y+1)*CELL_SIZE-1, 255, 255, 0, 172 );
                    GridBoard[x][y].marked = true;
                    GridBoard[x+1][y].marked = true;
                    GridBoard[x][y+1].marked = true;
                    GridBoard[x+1][y+1].marked = true;
                }
            }
        else
            for(int y=0; y<GRID_H-1; y++)
            {
                if (GridBoard[x][y].color==-1) break; // the cell is empty so we can jump to next column

                if (GridBoard[x][y].color==GridBoard[x+1][y].color && GridBoard[x][y].color==GridBoard[x][y+1].color && GridBoard[x][y].color==GridBoard[x+1][y+1].color)
                {
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x*CELL_SIZE+1, GRID_Y-(y+1)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE-1, GRID_Y-(y)*CELL_SIZE-1, 0, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE+1, GRID_Y-(y+1)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+2)*CELL_SIZE-1, GRID_Y-(y)*CELL_SIZE-1, 0, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+x*CELL_SIZE+1, GRID_Y-(y+2)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE-1, GRID_Y-(y+1)*CELL_SIZE-1, 0, 255, 0, 172 );
                    boxRGBA( surf, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+1)*CELL_SIZE+1, GRID_Y-(y+2)*CELL_SIZE+1, GRID_CENTER_X-GRID_W/2*CELL_SIZE+(x+2)*CELL_SIZE-1, GRID_Y-(y+1)*CELL_SIZE-1, 0, 255, 0, 172 );
                }
            }

    }
}

void updateColumn( void )
{
    /*int column = 0;
    if (currentSweepX<CELL_SIZE) column = GRID_W-1;
    else column=(int) currentSweepX/CELL_SIZE - 1;
*/

    if (Sweeper.positionincolumn<CELL_SIZE/2) return;

    for(int y=0; y<GRID_H; y++)
    {
        if (GridBoard[Sweeper.column][y].marked==true || GridBoard[Sweeper.column][y].color==-1)
        {
            if (y!=GRID_H-1)
            {
                GridBoard[Sweeper.column][y].color=GridBoard[Sweeper.column][y+1].color;
                GridBoard[Sweeper.column][y].marked=GridBoard[Sweeper.column][y+1].marked;

                GridBoard[Sweeper.column][y+1].color=-1;
                GridBoard[Sweeper.column][y+1].marked=false;
            }
            else
            {
                GridBoard[Sweeper.column][y].color=-1;
                GridBoard[Sweeper.column][y].marked=false;
            }
        }
    }
}


bool canWeAllocate3Mb = false;

void increaseRAM( void )
{
    char const *osv = (char*) 0x80020020;
    static kmalloc_arena_t extended_ram = { 0 };

    if((!strncmp(osv, "03.", 3) && osv[3] <= '6') && gint[HWCALC] == HWCALC_FXCG50) // CG-50
    {
        extended_ram.name = "extram";
        extended_ram.is_default = true;
        extended_ram.start =   (void *)0x8c200000;
        extended_ram.end = (void *)0x8c500000 ;

        kmalloc_init_arena(&extended_ram, true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = true;
    }
    else if (gint[HWCALC] == HWCALC_PRIZM)  // CG-10/20
    {

        extended_ram.name = "extram";
        extended_ram.is_default = true;

        uint16_t *vram1, *vram2;
        dgetvram(&vram1, &vram2);
        dsetvram(vram1, vram1);

        extended_ram.start = vram2;
        extended_ram.end = (char *)vram2 + 396*224*2;

        kmalloc_init_arena(&extended_ram, true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = false;

    }
    else if (gint[HWCALC] == HWCALC_FXCG_MANAGER) // CG-50 EMULATOR
    {

        extended_ram.name = "extram";
        extended_ram.is_default = true;
        extended_ram.start =   (void *)0x88200000;
        extended_ram.end = (void *)0x88500000 ;

        kmalloc_init_arena(&extended_ram, true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = true;

    }
    else abort();
}

int main ( int argc, char** argv )
{
    increaseRAM();

    overclock_level levelOCinit = overclock_detect( );
    clock_overclock( OC_PtuneF5 );

    prof_init();
    prof_t perf;

    if (SDL_Init(SDL_INIT_VIDEO)<0) return 1;
    SDL_ShowCursor(SDL_DISABLE);


    screen = SDL_SetVideoMode(396, 224, 16, SDL_SWSURFACE);
    //if (!screen) return 1;

    IMG_Init( IMG_INIT_PNG | IMG_INIT_JPG );
    font = cSDL_LoadFont(cSDL_FONT_TINYTYPE, 255, 255, 255);

    background = SDL_ConvertSurface( (SDL_Surface*) gint_world_switch( GINT_CALL( IMG_Load, "./Lumines/Backgrounds/back002.jpg" ) ),
                                     screen->format, SDL_SWSURFACE );
    //if (!background) return 1;

    blocks = SDL_ConvertSurface( (SDL_Surface*) gint_world_switch( GINT_CALL( IMG_Load, "./Lumines/Blocks/test.png" ) ),
                                 screen->format, SDL_SWSURFACE );
    //if (!blocks) return 1;

    initBoard( );

    Sweeper.x=0.0f;
    Sweeper.speed=1.0f;

    timerstart=0;

    bool done = false;
    while (!done)
    {
        perf = prof_make();
        prof_enter(perf);

        currenttimer=SDL_GetTicks();
        uint32_t deltatimer = currenttimer-timerstart;
        timerstart=currenttimer;

        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            // check for messages
            switch (event.type)
            {
            // exit if the window is closed
            case SDL_QUIT:
                done = true;
                break;

            // check for keypresses
            case SDL_KEYDOWN:
            {
                // exit if ESCAPE is pressed
                if (event.key.keysym.sym == SDLK_PRZ_KEY_EXIT)
                    done = true;
                break;
            }
            } // end switch
        } // end of message processing

        //SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 127, 127, 127));
        drawBackground( screen );

        drawGrid( screen );

        /*        drawBlock( screen, 0, 0, 0 );
                drawBlock( screen, 0, 1, 1 );

                drawBlock( screen, 2, 0, 2 );
                drawBlock( screen, 2, 1, 3 );

                drawBlock( screen, 4, 0, 4 );
                drawBlock( screen, 4, 1, 5 );

                drawBlock( screen, 6, 0, 6 );
                drawBlock( screen, 6, 1, 7 );

                drawBlock( screen, 8, 0, 8 );
                drawBlock( screen, 8, 1, 9 );
        */

        drawBlockGrid( screen );

        drawSweepLine( screen, deltatimer );

        checkSquareGroups( screen );

        updateColumn( );

        cSDL_DrawString( screen, font, 10, 10, "Delta t: %d", time_frame/1000 );

        cSDL_DrawString( screen, font, 10, 20, "Hello !!" );

        SDL_Flip(screen);

        prof_leave(perf);
        time_frame = prof_time(perf);
    } // end main loop


    SDL_FreeSurface( blocks );
    SDL_FreeSurface( background );
    cSDL_FreeFont( font );

    SDL_Quit();

    clock_overclock( levelOCinit );

    return 0;
}
