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

#include "canvas.h"

/*
 * State of cells: Dead or Alive
 */
typedef enum {Dead, Alive} State;

#include "program.h"

#define M 80
#define N 24

#define SPEED_UP 10


/*
 * Create MxN matrix of cells, all initialized to 0 (Dead)
 * Also create a result matrix, where the result of the applied rules
 * is stored before it is copied back to the original.
 */
State cells[M][N] = {0};
State cells_result[M][N] = {0};


int main(int argc, char** argv) {
    // Create a canvas of size 80x24
    // Access these numbers by c->width and c->height
    Canvas* c = canvas_create(M, N);

    // Create the initial configuration of the cells.
    setup_board();

    // Main loop
    while (true) {
        canvas_clear(c);

        // --- Update ---
        
        // Apply rules and store in result matrix
        apply_rules();

        // Copy result over in original matrix
        copy_result_back();


        // --- Draw ---
        draw_board(c);


        // Draw the canvas to the terminal and wait for next frame
        canvas_present(c);
        usleep(ANIMATION_TIMEOUT*SPEED_UP);
    }
    return 0;
}


/*
 * Here one can define the starting configuration of the cells.
 */
void setup_board() {
    cells[4][3]  = Alive;
    cells[4][4]  = Alive;
    cells[5][4]  = Alive;
    cells[5][8]  = Alive;
    cells[5][9]  = Alive;
    cells[5][10] = Alive;
    cells[3][9]  = Alive;

    place_blinker(15,7);
}


/*
 * Apply the rules of Game of Life on each cell.
 * Store the result in cells_result as to not mess up the result for neighboring cells.
 *
 * The rules are:
 *   For a cell that is Alive:
 *     Each cell with one or no neighbors dies, as if by solitude. 
 *     Each cell with two or three neighbors survives. 
 *     Each cell with four or more neighbors dies, as if by overpopulation. 
 *   For a cell that is Dead:
 *     Each cell with three neighbors becomes populated. 
 */
void apply_rules() {
    // TODO: Edit here
}


/*
 * Get the state of a cell at (row, col).
 * This is simply an abstraction of cells[row][col].
 * This functions should additionally check if the requested row and column
 * are inside the bounds of the matrix and if not return Dead.
 * This is useful when checking for neighbors at the border of board.
 */
State get_cell(int row, int col) {
    // TODO: Edit here
    return Dead;
}


/*
 * Store the state in cells_result.
 * Again, this is just an abstraction for cells_result[row][col] = state.
 */
void set_result_cell(int row, int col, State state) {
    // TODO: Edit here
}

/*
 * Count the number of neighbors around a cell.
 */
int count_neighbors(int row, int col) {
    // TODO: Edit here
    return 0;
}



/*
 * For each cell in cells_result, 
 * copy this value over in the corresponding cell of cells.
 */
void copy_result_back() {
    // TODO: Edit here
}


/*
 * A little helper function used to convert a state to an int.
 * This is useful for counting neighbors.
 */
int state_to_int(State state) {
    if (state == Dead) {
        return 0;
    } else {
        return 1;
    }
}



/* ===== Drawing ===== */

/*
 * Draw each alive cell
 */
void draw_board(Canvas* c) {
    for (int row = 0; row < M; row++) {
        for (int col = 0; col < N; col++) {
            State cell = cells[row][col];
            if (cell == Alive) {
                draw_cell(c, row, col);
            }
        }
    }
}

void draw_cell(Canvas* c, int row, int col) {
    // Note: canvas_cell expects (x,y) instead fo (row, col),
    //       thus we just transpose it.
    canvas_cell(c, col, row, '#');
}


/* ===== Templates ===== */

/*
 * Set the state of cell.
 * An abstraction on cells[row][col] = state with bounds check
 */
void set_cell(int row, int col, State state) {
    if (0 <= row && row < M) {
        if (0 <= col && col < N) {
            cells[row][col] = state;
        }
    }
}

void set_alive(int row, int col) {
    set_cell(row, col, Alive);
}

/*
 * Blinker
 *
 *   #
 *   #
 *   #
 *
 * (row, col) is center #
 */
void place_blinker(int row, int col) {
    set_alive(row-1, col);
    set_alive(row  , col);
    set_alive(row+1, col);
}

/*
 * Glider
 *
 *    #
 *     #
 *   ###
 *
 * (row, col) is center empty cell
 */
void place_glider(int row, int col) {
    set_alive(row-1, col  );
    set_alive(row  , col+1);
    set_alive(row+1, col-1);
    set_alive(row+1, col);
    set_alive(row+1, col+1);
}
