#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <time.h>

#ifdef WIN32
#include <windows.h>
#define usleep(t) Sleep((t)/1000)
#define kbhit _kbhit
#define getch _getch
#endif

static int g_all_bricks[7][4][18] = {
    {
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1 },
        { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 4 },
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1 },
        { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 4 },
    },
    {
        { 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
        { 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
    },
    {
        { 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
        { 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3 },
    },
    {
        { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
        { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
        { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
        { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
    },
    {
        { 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
        { 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3 },
    },
    {
        { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3 },
        { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3 },
    },
    {
        { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
        { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 },
        { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 3 },
    },
};

static void draw_game(int board[20][10], int curbricks[16], int curx, int cury,
                      int nextbricks[16], int level, int score, int lines, int history, int gameover)
{
    int  i, j, c;
    system("cls");
    for (i=0; i<20; i++) {
        for (j=0; j<10; j++) {
            c = board[i][j] == 1 ? 'B' : board[i][j] == 2 ? 'x' : '.';
            if (j>=curx && j<curx+4 && i>=cury && i<cury+4) {
                int tx = j - curx, ty = i - cury;
                if (curbricks[ty * 4 + tx]) {
                    c = 'B';
                }
            }
            printf("%c ", c);
        }
        switch (i) {
        case 0 : printf(" next:"); break;
        case 7 : printf(" level  : %d", level  ); break;
        case 9 : printf(" score  : %d", score  ); break;
        case 11: printf(" lines  : %d", lines  ); break;
        case 13: printf(" history: %d", history); break;
        case 15: printf("%s", gameover ? " game over !" : ""); break;
        case 16: printf("%s", gameover ? " press R to again." : ""); break;
        case 18: printf(" ffbricks v1.0.0"); break;
        case 19: printf(" writen by chenk"); break;
        }
        if (i >= 2 && i <= 5) {
            printf(" ");
            for (j=0; j<4; j++) {
                int tx = j, ty = i - 2;
                printf("%c ", nextbricks[ty * 4 + tx] ? 'B' : ' ');
            }
        }
        printf("\n");
    }
    printf("-------------------");
}

static int check_collision(int board[20][10], int bricks[16], int curx, int cury)
{
    int i, j;
    for (i=0; i<20; i++) {
        for (j=0; j<10; j++) {
            if (j>=curx && j<curx+4 && i>=cury && i<cury+4) {
                int tx = j - curx, ty = i - cury;
                if (board[i][j] && bricks[ty * 4 + tx]) return 1;
            }
        }
    }
    return 0;
}

static void copy_bricks_to_board(int board[20][10], int bricks[16], int curx, int cury)
{
    int i, j;
    for (i=0; i<20; i++) {
        for (j=0; j<10; j++) {
            if (j>=curx && j<curx+4 && i>=cury && i<cury+4) {
                int tx = j - curx, ty = i - cury;
                if (bricks[ty * 4 + tx]) {
                    board[i][j] = 1;
                }
            }
        }
    }
}

static int mark_full_rows(int board[20][10])
{
    int i, j, l = 0;
    for (i=0; i<20; i++) {
        for (j=0; j<10; j++) {
            if (!board[i][j]) {
                break;
            }
        }
        if (j == 10) { l++; for (j=0; j<10; j++) board[i][j] = 2; }
    }
    return l;
}

static void remove_full_rows(int board[20][10])
{
    int  i, j, f, m;
    for (i=19,m=19; i>=0 && m>=0; m--) {
        for (f=0,j=0; j<10; j++) {
            if ( board[m][j] == 1) { f = 1; break; }
        }
        if (f) {
            for (j=0; j<10; j++) board[i][j] = board[m][j];
            i--;
        };
    }
    for (; i>=0; i--) for (j=0; j<10; j++) board[i][j] = 0;
}

int main(void)
{
    int board[20][10] = {0};
    int type, rot, next, nrot, posx, posy, newx, newy, newr, collision, speed;
    int remove = 0, score = 0, lines = 0, level = 0, history = 0, gameover = 0;
    int quit = 0, key = 0, counter = 0, dirty = 1;

    srand((unsigned int)time(NULL));
    type = rand()%7, rot = rand()%4, posx = rand()%6, posy = 0, next = rand()%7, nrot = rand()%4;

    while (!quit) {
        if (dirty) {
            draw_game(board, g_all_bricks[type][rot], posx, posy, g_all_bricks[next][nrot], level, score, lines, history, gameover);
            dirty = 0;
        }
        if (remove) { remove_full_rows(board); remove = 0; usleep(10*1000); }

        if (!kbhit()) {
            usleep(16*1000);
            if (gameover) continue;
            speed = 60 - 58 * level / 10;
            if (speed < 2) speed = 2;
            if (++counter % speed == 0) {
                key = 'd';
            } else continue;
        } else key = getch();

        if (gameover) {
            if (key == 'R' || key == 'r') {
                memset(board, 0, sizeof(board));
                score= 0, lines = 0, level = 0, gameover = 0;
            } else continue;
        }
        newx = posx; newy = posy; newr = rot; dirty = 1;
        switch (key) {
        case 'Q': case 'q': quit = 1; break;
        case 'S': case 's': if (newx > 0) newx--; break;
        case 'F': case 'f': newx++; break;
//      case 'E': case 'e': if (newy > 0) newy--; break;
        case 'D': case 'd': newy++; break;
        case 'J': case 'j': newr++; newr %= 4; break;
        case 'K': case 'k':
            while (++newy + g_all_bricks[type][newr][17] <= 20 && !check_collision(board, g_all_bricks[type][newr], newx, newy));
            posy = newy - 1;
            break;
        }

        collision = check_collision(board, g_all_bricks[type][newr], newx, newy);
        if (  newx + g_all_bricks[type][newr][16] <= 10
           && newy + g_all_bricks[type][newr][17] <= 20 && !collision) {
            posx = newx; posy = newy; rot = newr;
        } else if (key == 'D' || key == 'd' || key == 'K' || key == 'k') {
            copy_bricks_to_board(board, g_all_bricks[type][rot], posx, posy);
            remove = mark_full_rows(board);
            lines += remove; score += remove * remove; level = score / 100; history = history > score ? history : score;
            type = next, rot = nrot, posx = rand()%6, posy = 0; next = rand()%7; nrot = rand()%4;
            gameover = check_collision(board, g_all_bricks[type][rot], posx, posy);
        }
    }
    return 0;
}