// Implementación del tablero. Ver board.h.
#include "board.h"

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

typedef enum {
    CELL_HIDDEN, // vale 0: es lo que queda en un bloque de calloc
    CELL_OPEN,
    CELL_FLAGGED
} CellView;

typedef struct {
    int is_mine;
    int around; // minas en las ocho vecinas
    CellView view;
} Cell;

// Las celdas van fila por fila, igual que los píxeles de una imagen.
struct board {
    int rows;
    int cols;
    int mines;
    int opened;   // celdas sin mina ya abiertas
    int exploded; // 1 si ya se abrió una mina
    Cell *cells;
};

static int in_bounds(const Board *board, int row, int col) {
    return row >= 0 && row < board->rows && col >= 0 && col < board->cols;
}

static Cell *cell_at(const Board *board, int row, int col) {
    return &board->cells[row * board->cols + col];
}

// ---------- Sesión 1: escriban estas tres funciones ----------

Board *board_new(int rows, int cols) {
    // Escriban aquí su código. Son dos bloques: el struct y las celdas.
    (void) rows;
    (void) cols;
    return NULL;
}

void board_free(Board *board) {
    // Escriban aquí su código.
    (void) board;
}

// Llena el campo around de todas las celdas. board_from_rows y board_random la llaman cuando
// terminan de poner las minas.
static void count_neighbors(Board *board) {
    // Escriban aquí su código.
    (void) board;
}

// ---------- Sesión 2: escriban estas dos funciones ----------

void board_reveal(Board *board, int row, int col) {
    // Escriban aquí su código. Necesitarán una función auxiliar recursiva, declarada static.
    (void) board;
    (void) row;
    (void) col;
}

GameState board_state(const Board *board) {
    // Escriban aquí su código.
    (void) board;
    return GAME_PLAYING;
}

// ---------- De aquí para abajo, el código ya está hecho ----------

Board *board_from_rows(const char *rows[], int n) {
    if (n < 1) {
        return NULL;
    }
    int cols = (int) strlen(rows[0]);
    Board *board = board_new(n, cols);
    if (board == NULL) {
        return NULL;
    }

    for (int row = 0; row < n; row++) {
        if ((int) strlen(rows[row]) != cols) {
            board_free(board);
            return NULL;
        }
        for (int col = 0; col < cols; col++) {
            if (rows[row][col] == '*') {
                cell_at(board, row, col)->is_mine = 1;
                board->mines++;
            } else if (rows[row][col] != '.') {
                board_free(board);
                return NULL;
            }
        }
    }
    count_neighbors(board);
    return board;
}

// Generador de números seudoaleatorios: cada número sale del anterior. Lleva su estado afuera, en
// *state, para que la misma semilla dé lo mismo en todas las computadoras.
static unsigned int next_random(unsigned int *state) {
    *state = *state * 1664525u + 1013904223u;
    return *state >> 8; // los bits de abajo de este generador se repiten demasiado
}

Board *board_random(int rows, int cols, int mines, unsigned int seed) {
    Board *board = board_new(rows, cols);
    if (board == NULL) {
        return NULL;
    }
    int total = rows * cols;
    if (mines < 0 || mines > total) {
        board_free(board);
        return NULL;
    }

    unsigned int state = seed;
    while (board->mines < mines) {
        Cell *cell = &board->cells[next_random(&state) % (unsigned int) total];
        if (!cell->is_mine) {
            cell->is_mine = 1;
            board->mines++;
        }
    }
    count_neighbors(board);
    return board;
}

void board_toggle_flag(Board *board, int row, int col) {
    if (board_state(board) != GAME_PLAYING || !in_bounds(board, row, col)) {
        return;
    }
    Cell *cell = cell_at(board, row, col);
    if (cell->view == CELL_HIDDEN) {
        cell->view = CELL_FLAGGED;
    } else if (cell->view == CELL_FLAGGED) {
        cell->view = CELL_HIDDEN;
    }
}

int board_rows(const Board *board) {
    return board->rows;
}

int board_cols(const Board *board) {
    return board->cols;
}

int board_mines(const Board *board) {
    return board->mines;
}

int board_opened(const Board *board) {
    return board->opened;
}

int board_mines_around(const Board *board, int row, int col) {
    return cell_at(board, row, col)->around;
}

char board_cell_char(const Board *board, int row, int col) {
    const Cell *cell = cell_at(board, row, col);
    if (cell->is_mine && (board->exploded || cell->view == CELL_OPEN)) {
        return '*';
    }
    if (cell->view == CELL_HIDDEN) {
        return '#';
    }
    if (cell->view == CELL_FLAGGED) {
        return 'F';
    }
    return cell->around == 0 ? '.' : (char) ('0' + cell->around);
}

void board_print(const Board *board) {
    printf("   ");
    for (int col = 0; col < board->cols; col++) {
        printf("%2d", col % 100);
    }
    printf("\n");
    for (int row = 0; row < board->rows; row++) {
        printf("%2d ", row % 100);
        for (int col = 0; col < board->cols; col++) {
            printf(" %c", board_cell_char(board, row, col));
        }
        printf("\n");
    }
}
