#include <stdio.h>
#include <time.h>
#include "graphics.h"

int board[16][16];
int flipped[16][16];
int mark[16][16];
int xp = 0, yp = 0;

int size = 8;

void main_loop();
void uncover(int xp, int yp);
void paint_flag(int xp, int yp);
void paint_mine(int xp, int yp);
void paint_x(int xp, int yp);

int main() {
    graphics_screen_mode();
    init_engine(0, 0);
    
    main_loop();
    
    text_screen_mode();
    printf("Thanks for playing mines! :)\n");
    return 0;
}

void main_loop() {
    int x, y, c = 0, z, a;
    int marked, won = 0;
    int lxp = 0, lyp = 0;
    int newgame = 1, first;
    time_t stime, ttime, ptime;

    while(c!=27) {
        c = 0;

        if(newgame) {
            won = xp = yp = 0;

            srand(rawclock());

            for(x = 0; x < size; x ++) {
                for(y = 0; y < size; y ++) {
                    board[x][y] = 0;
                    flipped[x][y] = 0;
                    mark[x][y] = 0;
                }
            }

            for(z = 0; z < (size==8?10:40); z ++) {
                x = rand()%size, y = rand()%size;
        
                if(board[x][y]) {
                    z--; continue;
                }
                else board[x][y] = 1;
            }

            first = 1;
        }

        newgame = marked = 0;

        if(!won) won = 1;
        else if(won>0) won = 2;

        box(0, 0, 320, 200, 0, set);
        
        for(x = 0; x < size; x ++) {
            for(y = 0; y < size; y ++) {
                if(flipped[x][y]) mark[x][y] = 0;
                if(mark[x][y] == 1) marked ++;
                if(won == 2 || won == -1) {
                    //if(board[x][y]) mark[x][y] = 2;  //////////////////////////////////////////////////////////////////////
                }
                else if(!flipped[x][y] && !board[x][y]) won = 0;
            
                if(!flipped[x][y] || mark[x][y]) {
                    box((x*10)+1, (y*10)+1, (x*10)+9, (y*10)+9, x==lxp&&y==lyp&&won==-1?40:22, set);
                
                    line(x*10, y*10, (x*10)+9, y*10, xp==x&&yp==y?20:25, set);   /* top */
                    line(x*10, y*10, x*10, (y*10)+9, xp==x&&yp==y?20:25, set);   /* left */
                    line((x*10)+9, (y*10)+9, x*10, (y*10+9), xp==x&&yp==y?25:20, set);   /* bottom */
                    line((x*10)+9, y*10, (x*10)+9, (y*10+9), xp==x&&yp==y?25:20, set);   /* right */

                    if(won == -1 && board[x][y]) {
                        if(mark[x][y] != 1) paint_mine((x*10), (y*10));
                        else paint_flag((x*10)+3, (y*10)+2);
                    }
                    else if(won == 2 && board[x][y]) paint_flag((x*10)+3, (y*10)+2);
                    else if(mark[x][y] == 2) alpha_letter('?', (x*10)+3, (y*10)+2, 44, set);
                    else if(won != -1 && mark[x][y] == 1) paint_flag((x*10)+3, (y*10)+2);
                    else if(won == -1 && mark[x][y] == 1) paint_x((x*10)+3, (y*10)+2);
                }
                else {  /* flipped */
                    z = board[x][y];
                    z += (x>0 && board[x-1][y-0])?1:0;
                    z += (y>0 && board[x-0][y-1])?1:0;
                    z += (x>0 && y>0 && board[x-1][y-1])?1:0;
                    z += (x<size-1 && board[x+1][y+0])?1:0;
                    z += (y<size-1 && board[x+0][y+1])?1:0;
                    z += (x<size-1 && y<size-1 && board[x+1][y+1])?1:0;
                    z += (x<size-1 && y>0 && board[x+1][y-1])?1:0;
                    z += (x>0 && y<size-1 && board[x-1][y+1])?1:0;

                    box((x*10)+1, (y*10)+1, (x*10)+9, (y*10)+9, 22, set);

                    line(x*10, y*10, (x*10)+9, y*10, xp==x&&yp==y?27:23, set);   /* top */
                    line(x*10, y*10, x*10, (y*10)+9, xp==x&&yp==y?27:23, set);   /* left */
                    line((x*10)+9, (y*10)+9, x*10, (y*10+9), xp==x&&yp==y?27:23, set);   /* bottom */
                    line((x*10)+9, y*10, (x*10)+9, (y*10+9), xp==x&&yp==y?27:23, set);   /* right */

                    if(z==1) a = 32;
                    else if(z==2) a = 47;
                    else if(z==3) a = 42;
                    else if(z==4) a = 104;
                    else if(z==5) a = 107;
                    else if(z==6) a = 50;
                    else if(z==7) a = 35;
                    else a = 0;
                    
                    if(z) alpha_letter(z+'0', (x*10)+3, (y*10)+2, a, set);
                }

                //if(board[x][y]) set((x*10)+2, (y*10)+2, 0);  /* uncomment for cheat */
            }
        }

        if(!won) time(&ttime);

        {
            char s[100];
            sprintf(s, "Mines: %02i", (size==8?10:40)-marked);
            alpha_word(s, 210, 10, 47, set);
            if(!first) sprintf(s, "%03i", (int) difftime(ttime, stime));
            else strcpy(s, "000");
            alpha_word(s, 220, 20, 45, set);
        }

        fill_circle(225, 50, 7, 44, set);

        if(won == 2) {
            line(223, 54, 227, 54, 0, set);
            set(222, 53, 0); set(228, 53, 0);
            set(221, 52, 0); set(229, 52, 0);
            box(221, 47, 225, 50, 0, set);
            box(226, 47, 230, 50, 0, set);
            //line(221, 47, 229, 47, 1, set);
            set(225, 47, 0);
            line(220, 48, 218, 50, 0, set);
            line(230, 48, 232, 50, 0, set);
        }
        else if(won == -1) {
            line(223, 52, 227, 52, 0, set);
            set(222, 53, 0); set(228, 53, 0);
            set(221, 54, 0); set(229, 54, 0);

            set(222, 48, 0); set(228, 48, 0);
            set(221, 47, 0); set(229, 47, 0);
            set(221, 49, 0); set(229, 49, 0);
            set(223, 47, 0); set(227, 47, 0);
            set(223, 49, 0); set(227, 49, 0);
        }
        else {
            line(223, 54, 227, 54, 0, set);
            set(222, 53, 0); set(228, 53, 0);
            set(221, 52, 0); set(229, 52, 0);
            box(222, 47, 224, 49, 0, set);
            box(227, 47, 229, 49, 0, set);
        }

        //set((xp*10)+4, (yp*10)+4, 40);
        
        refresh_screen();
    
        if(kbhit()) c = getxkey();

        if(c==584) {    /* UP */
            if(yp > 0) yp --;
        }
        else if(c==592) {    /* DOWN */
            if(yp < size-1) yp ++;
        }
        else if(c==587) {    /* LEFT */
            if(xp > 0) xp --;
        }
        else if(c==589) {    /* RIGHT */
            if(xp < size-1) xp ++;
        }
        /* Control-x */
        else if(c==653) {    /* CTRL-UP */
            yp = 0;
        }
        else if(c==657) {    /* CTRL-DOWN */
            yp = size-1;
        }
        else if(c==628) {    /* CTRL-RIGHT */
            xp = size-1;
        }
        else if(c==627) {    /* CTRL-LEFT */
            xp = 0;
        }
        else if(c=='\t') newgame = 1, size = 8;
        else if(c==271) newgame = 1, size = 16;
        else if((c=='?' || c=='/') && !flipped[xp][yp] && !won) mark[xp][yp] = 2;
        else if(c=='\r' && !flipped[xp][yp] && !won) mark[xp][yp] = 1;
        else if(c=='\b' && !won) mark[xp][yp] = 0;
        else if(c==' ' && !flipped[xp][yp] && mark[xp][yp]!=1 && !won) {
            if(board[xp][yp]) {
                won = -1;
                lxp = xp, lyp = yp;
            }
            else if((xp>0 && board[xp-1][yp-0])
                || (yp>0 && board[xp-0][yp-1])
                || (xp>0 && yp>0 && board[xp-1][yp-1])
                || (xp<size-1 && board[xp+1][yp+0])
                || (yp<size-1 && board[xp+0][yp+1])
                || (xp<size-1 && yp<size-1 && board[xp+1][yp+1])
                || (xp<size-1 && yp>0 && board[xp+1][yp-1])
                || (xp>0 && yp<size-1 && board[xp-1][yp+1])) {

                flipped[xp][yp] = 1;

                if(first) first = 0, time(&stime);
            }
            else {
                uncover(xp, yp);

                if(first) first = 0, time(&stime);
            }
        }
        else if(c=='\\' && !won) {
            box(0, 0, 320, 200, 0, set);
            alpha_word("mines is paused", 80, 50, 47, set);
            refresh_screen();
            getch();
            time(&ptime);
            stime += (ptime - ttime);
        }
    }
}

void uncover(int xp, int yp) {
    flipped[xp][yp] = 1;

    if(board[xp][yp]
        || (xp>0 && board[xp-1][yp-0])
        || (yp>0 && board[xp-0][yp-1])
        || (xp>0 && yp>0 && board[xp-1][yp-1])
        || (xp<size-1 && board[xp+1][yp+0])
        || (yp<size-1 && board[xp+0][yp+1])
        || (xp<size-1 && yp<size-1 && board[xp+1][yp+1])
        || (xp<size-1 && yp>0 && board[xp+1][yp-1])
        || (xp>0 && yp<size-1 && board[xp-1][yp+1])) {

        return;
    }
    else {   /* recurse */
        if(xp>0 && !flipped[xp-1][yp-0] && mark[xp-1][yp-0]!=1 && !board[xp-1][yp-0]) uncover(xp-1, yp-0);
        if(yp>0 && !flipped[xp-0][yp-1] && mark[xp-0][yp-1]!=1 && !board[xp-0][yp-1]) uncover(xp-0, yp-1);
        if(xp>0 && yp>0 && !flipped[xp-1][yp-1] && mark[xp-1][yp-1]!=1 && !board[xp-1][yp-1]) uncover(xp-1, yp-1);
        if(xp<size-1 && !flipped[xp+1][yp+0] && mark[xp+1][yp+0]!=1 && !board[xp+1][yp+0]) uncover(xp+1, yp+0);
        if(yp<size-1 && !flipped[xp+0][yp+1] && mark[xp+0][yp+1]!=1 && !board[xp+0][yp+1]) uncover(xp+0, yp+1);
        if(xp<size-1 && yp<size-1 && !flipped[xp+1][yp+1] && mark[xp+1][yp+1]!=1 && !board[xp+1][yp+1]) uncover(xp+1, yp+1);
        if(xp<size-1 && yp>0 && !flipped[xp+1][yp-1] && mark[xp+1][yp-1]!=1 && !board[xp+1][yp-1]) uncover(xp+1, yp-1);
        if(xp>0 && yp<size-1 && !flipped[xp-1][yp+1] && mark[xp-1][yp+1]!=1 && !board[xp-1][yp+1]) uncover(xp-1, yp+1);
    }
}

void paint_flag(int xp, int yp) {
    int x, y;

    for(x = 0; x < 3; x ++) {
        for(y = 0; y < 3; y ++) {
            set(xp+x, yp+y, x+y>2?112:40);
        }
    }

    set(xp+2, yp+3, 0), set(xp+2, yp+4, 0), set(xp+2, yp+5, 0);
    set(xp+1, yp+5, 0), set(xp+3, yp+5, 0);
}

void paint_mine(int xp, int yp) {
    int x, y;
    
    for(x = 0; x < 3; x ++) {
        for(y = 0; y < 3; y ++) {
            set(xp+x+3, yp+y+3, 0);
        }
    }

    set(xp+3, yp+4, 30);
    set(xp+2, yp+4, 0), set(xp+4, yp+2, 0);
    set(xp+6, yp+4, 0), set(xp+4, yp+6, 0);
}

void paint_x(int xp, int yp) {
    set(xp+0, yp+0, 40), set(xp+3, yp+0, 40);
    set(xp+0, yp+1, 40), set(xp+3, yp+1, 40);
    set(xp+1, yp+2, 40), set(xp+2, yp+2, 40);
    set(xp+0, yp+3, 40), set(xp+3, yp+3, 40);
    set(xp+0, yp+4, 40), set(xp+3, yp+4, 40);
}
