/*
 * Stefan Bianca-Eugenia
 * 334CB 2018
 * LCD Minesweeper
 */

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>

#include "u8g.h"

#define F_CPU 16000000

#define LCD_WIDTH 128
#define LCD_HEIGHT 64
#define BOX_HEIGHT 6
#define BOX_WIDTH 8
#define TABLE_WIDTH 82
#define TABLE_HEIGHT (LCD_HEIGHT)
#define UNIT 10
#define DELAY 500
#define BOMBNO 10

#define SIZE 9

#define BOMB 11
#define VISITED 10

typedef struct {
    uint8_t x;
    uint8_t y;
} coord;

uint8_t matrix[9][9] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/* LCD */
static u8g_t u8g;

/* Digit strings - number of bombs in the neibourhood */
static char *digits[10] = {"0", "1", "2", "3", "4", "5", "6", "7", "8"};

static uint8_t seed = 103;

/* Current cursor position */
static coord current_pos;

uint8_t countNonBombs;

/*--------------------------HARDWARE-PART-----------------------------*/

/* Initialize LCD */
void init_lcd(void) {

    MCUCR |= (1 << JTD);
    MCUCR |= (1 << JTD);

    /* u8g_II_api.c */
    u8g_Init8Bit(&u8g, &u8g_dev_st7920_128x64_4x_8bit,
        PN(0, 7), PN(0, 6), PN(0, 5), PN(0, 4), 
        PN(0, 3), PN(0, 2), PN(0, 1), PN(0, 0), // DATA - Port A
        PN(1, 4),                               // ENABLE - PB4
        U8G_PIN_NONE,
        U8G_PIN_NONE,
        PN(1, 5),                               // RS/DI - PB5
        PN(1, 6),                               // RW    - PB6
        PN(1, 7));                              // RESET - PB7
}

/* Initialize buttons */
void init_buttons(void) {

    DDRD = 0xff;
    PORTD = 0x00;
    
    /* UP */
    DDRD &= ~(1 << PD7);
    PORTD |= (1 << PD7);

    /* DOWN */
    DDRD &= ~(1 << PD5);
    PORTD |= (1 << PD5);
	
	/* LEFT */
    DDRD &= ~(1 << PD6);
    PORTD |= (1 << PD6);
    
    /* RIGHT */
    DDRD &= ~(1 << PD4);
    PORTD |= (1 << PD4);

    /* SELECT */
    DDRD &= ~(1 << PD2);
    PORTD |= (1 << PD2);

	/* FLAG */
	DDRD &= ~(1 << PD3);
    PORTD |= (1 << PD3);

    /* Enable interrupts  */
    PCICR  = (1 << PCIE2);
    PCMSK2 = (1 << PCINT31) | (1 << PCINT29) | (1 << PCINT30) | (1 << PCINT28)
        | (1 << PCINT26) | (1 << PCINT27);
}

/* Initialize the game */
void init_game(void) {
    current_pos.x = 0;
    current_pos.y = 0;
	countNonBombs = 0;
}

/* Draw the grid */
void draw_grid(void) {
    uint8_t i;

    for (i = 0; i <= SIZE; i++) {
        u8g_DrawBox(&u8g, 0, i * (BOX_HEIGHT + 1), TABLE_WIDTH, 1);
        u8g_DrawBox(&u8g, i * (BOX_WIDTH + 1), 0, 1, TABLE_HEIGHT);
    }

    u8g_DrawBox(&u8g, 3 * (BOX_WIDTH + 1) - 1, 3 * (BOX_HEIGHT + 1) - 1, 3, 3);
    u8g_DrawBox(&u8g, 6 * (BOX_WIDTH + 1) - 1, 3 * (BOX_HEIGHT + 1) - 1, 3, 3);
    u8g_DrawBox(&u8g, 3 * (BOX_WIDTH + 1) - 1, 6 * (BOX_HEIGHT + 1) - 1, 3, 3);
    u8g_DrawBox(&u8g, 6 * (BOX_WIDTH + 1) - 1, 6 * (BOX_HEIGHT + 1) - 1, 3, 3);
}

/* Draw the current position of the cursor */
void draw_cursor(void) {

	u8g_DrawBox(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, current_pos.y * (BOX_HEIGHT + 1), 1, BOX_HEIGHT + 1);
}

/* Draw the game */
void draw_game(void) {

    uint8_t REF = 86;

	u8g_SetDefaultForegroundColor(&u8g);
    u8g_SetFont(&u8g, u8g_font_04b_03b);
    draw_grid();
    draw_cursor();
    
    u8g_DrawStr(&u8g, REF, UNIT, "MINES :)");

}

/* Update the cursor when a button is pressed */
void update_cursor(void) {

	/* UP */
	if (!(PIND & (1 << PD7))) {
		if(current_pos.y > 0)
			current_pos.y -= 1;
	}

	/* DOWN */
	if (!(PIND & (1 << PD5))) {
		if(current_pos.y < SIZE - 1)
		current_pos.y += 1;
	}
	
	/* LEFT */
	if (!(PIND & (1 << PD6))) {
		if(current_pos.x > 0)
			current_pos.x -= 1;
	}

	/* RIGHT */
	if (!(PIND & (1 << PD4))) {
		if(current_pos.x < SIZE - 1)
			current_pos.x += 1;
	}
	u8g_DrawBox(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, current_pos.y * (BOX_HEIGHT + 1), 1, BOX_HEIGHT + 1);
}

/* Show the value of the selected box */
void show_element(void){
	switch(matrix[current_pos.x][current_pos.y]){
		case 0: {
			
			break;
		}
		case 1: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "1");
			break;
		}
		case 2: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "2");
			break;
		}
		case 3: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "3");
			break;
		}
		case 4: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "4");
			break;
		}
		case 5: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "5");
			break;
		}
		case 6: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "6");
			break;
		}
		case 7: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "7");
			break;
		}
		case 8: {
			u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "8");
			break;
		}
	}
	matrix[current_pos.x][current_pos.y] = VISITED;
	countNonBombs++;
}

/*--------------------------SOFTWARE-PART-----------------------------*/
/* assign values to squares adiacent to bombs */
void count_bombs(void) {
	uint8_t i, j;
	uint8_t mat[11][11];
	
		
	for(i = 0; i < 11; i++) {
			mat[i][0] = 0;
			mat[i][10] = 0;
			mat[0][i] = 0;
			mat[10][i] = 0;
	}
		
	for(i = 1; i < 10; i++) {
		for(j = 1; j < 10; j++) {
			mat[i][j] = matrix[i - 1][j - 1];
		}
	}
	
	for(i = 1; i < 10; i++) {
		for(j = 1; j < 10; j++) {
			if(mat[i][j] >= BOMB) {
				mat[i-1][j-1] += 1;
				mat[i-1][j] += 1;
				mat[i-1][j+1] += 1;
				mat[i][j-1] += 1;
				mat[i][j+1] += 1;
				mat[i+1][j-1] += 1;
				mat[i+1][j] += 1;
				mat[i+1][j+1] += 1;
			}
		}
	}
	
	for(i = 1; i < 10; i++) {
		for(j = 1; j < 10; j++) {
			matrix[i - 1][j - 1] = mat[i][j];
		}
	}
}

/* generates bombs at random positions, then fills the rest of the matrix */
void init_matrix(void) {
	uint8_t i = 0;
	uint8_t x, y;
	
	/* random bombs */
	srand(seed);
	while (1){
		x = rand() % SIZE;
		y = rand() % SIZE;
		if(i == BOMBNO)
			break;
		if(matrix[x][y] == 0){
			matrix[x][y] = BOMB;
			i++;
		}
	}
	
	/* complete the matrix */
	count_bombs();
}

void show_all(void) {
	uint8_t i, j;
	for(i=0; i<SIZE; i++){
		for(j=0; j<SIZE; j++){
			if(matrix[current_pos.x][current_pos.y] >= BOMB){
				u8g_DrawStr(&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "X");
			}
			else if(matrix[current_pos.x][current_pos.y] < 9) 
				show_element();
		}
	}
}

int main(void) {
	uint8_t i, j;
	init_lcd();
    init_buttons();
	init_game();
	draw_game();
	init_matrix();
    //sei();
	
	while (1) {
		if(countNonBombs == SIZE * SIZE - BOMBNO) {
			init_game();
			draw_game();
			for(i=0;i<SIZE; i++){
				for(j=0; j<SIZE; j++){
					matrix[i][j] = 0;
				}
			}
			init_matrix();
		}
		/* UP + DOWN + LEFT + RIGHT */
		update_cursor();

		/* SELECT */
		if (!(PIND & (1 << PD2))) {
			if(matrix[current_pos.x][current_pos.y] == BOMB){
				show_all();
				_delay_ms(2000);
			}
			if(matrix[current_pos.x][current_pos.y] < 9) {
				show_element();
			}
		}
		
		/* FLAG */
		if (!(PIND & (1 << PD3))) {
			u8g_DrawStr&u8g, (current_pos.x + 1) * (BOX_WIDTH + 1) - 1, 
				current_pos.y * (BOX_HEIGHT + 1), "F");
		}
    }
	return 0;
}
