#include <iostream>
#include <cstdlib>
#include <ctime>
// Ignore these next two libraries! There is a note about them in the
// assignment handout if you are interested. They are for pausing between frames.
#include <thread>
#include <chrono>
using namespace std;

// Function prototypes. Don't change these!
int rc2index(int r, int c, int board_size);
int numNeighborsAlive(int r, int c, int* board, int board_size);
void initializeBoard(int* board, int board_size);
void printBoard(int* board, int board_size);
void updateBoard(int* board, int board_size);

int main() {

    // First, seed the random number generator
    srand(time(NULL));

    // Ask the user what size board to use
    // All our boards are square (same numbers of rows
    // and columns). The minimum board size is 3x3.
    int board_size = 0;
    while (board_size < 3) {
        cout << "How big should the board be? ";
        cin >> board_size;
        if (board_size < 3)
            cout << "That is too small!" << endl;
    }
    cout << "Okay. Starting a game with a "
         << board_size << " by "
         << board_size << " board." << endl;


    // Create a new board and put random 0's and 1's in every cell
    int* board = new int[board_size * board_size];
    initializeBoard(board, board_size);

    // Main program loop. Do this forever.
    while(true) {
        printBoard(board, board_size);
        updateBoard(board, board_size);

        // Don't worry about how this next line works. It will pause the code for
        // 1/10 of a second. This gives us the impression that the code is drawing
        // a series of frames, rather than just scrolling forever.
        //
        // Feel free to experiment with changing the delay. That will just give you
        // a different frame rate for the game.
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

// Given a game board, randomly assign 0 (dead) or 1 (alive) to each cell.
// See the assignment handout for lots of details.
void initializeBoard(int* board, int board_size) {
    /////////////////////////////
    // TASK 1: YOUR CODE BELOW //
    /////////////////////////////



    /////////////////////////////
    // TASK 1: YOUR CODE ABOVE //
    /////////////////////////////
}

// Each r,c coordinate on our game board corresponds to a particular
// index in the board array. Convert r,c coordinates to array indices.
// Also, this function should understand the wrap-around boundaries.
// See the assignment handout for lots of details
int rc2index(int r, int c, int board_size) {

    /////////////////////////////
    // TASK 2: YOUR CODE BELOW //
    /////////////////////////////


    return -1;

    /////////////////////////////
    // TASK 2: YOUR CODE ABOVE //
    /////////////////////////////
}

// Take one turn of the game.
// The main logic is missing! See the assignment handout for lots of details
// about how to write this.
void updateBoard(int* board, int board_size) {

    // We need a scratch board to work on. We'll copy the results back into
    // the main board when we're done.
    int* new_board = new int[board_size * board_size];

    // For every cell on the board
    for (int r = 0; r < board_size; ++r) {
        for (int c = 0; c < board_size; ++c) {
            int index = rc2index(r, c, board_size);

            // Count how many neighboring cells are alive
            int num_neighbors_on = numNeighborsAlive(r, c, board, board_size);

            /////////////////////////////
            // TASK 3: YOUR CODE BELOW //
            /////////////////////////////


            /////////////////////////////
            // TASK 3: YOUR CODE ABOVE //
            /////////////////////////////
        }
    }

    // Copy the results back onto the board, and free up the temporary space
    // that we were using.
    for (int i = 0; i < board_size * board_size; ++i)
        board[i] = new_board[i];
    // No memory leaks! Be sure to delete the scratch board that we were using.
    delete[] new_board;
}

// Count how many of the neighbors of a certain r,c cell are alive
// This works even at the edges of the board, because the function
// rc2index handles the wrap-around board edges for us.
int numNeighborsAlive(int r, int c, int* board, int board_size) {
    return board[rc2index(r + 1, c - 1, board_size)] +
           board[rc2index(r + 1, c    , board_size)] +
           board[rc2index(r + 1, c + 1, board_size)] +
           board[rc2index(r    , c - 1, board_size)] +
           board[rc2index(r    , c + 1, board_size)] +
           board[rc2index(r - 1, c - 1, board_size)] +
           board[rc2index(r - 1, c    , board_size)] +
           board[rc2index(r - 1, c + 1, board_size)];
}

// Display the current state of the board
void printBoard(int* board, int board_size) {

    // It looks nice to print a few blank lines above each board
    for (int i = 0; i < 6; ++i)
        cout << "\n";

    // Loop through each row/column coordinate and print a 'X' if that cell is
    // on, a '.' if the cell is off, and a '?' if the cell is not 0 or 1. If
    // a '?' ever gets printed, then you've messed up somewhere.
    for (int r = 0; r < board_size; ++r) {
        for (int c = 0; c < board_size; ++c) {
            int index = rc2index(r, c, board_size);

            // Decide what symbol to print.
            // Also, because letters are about twice as high as they are wide,
            // it looks nicer to print an extra space after each cell symbol.
            if (board[index] == 1)
                cout << "X ";
            else if (board[index] == 0)
                cout << ". ";
            else
                cout << "? ";
        }
        // print a newline when I finish with each row.
        cout << "\n";
    }
    // print a final newline when I'm done.
    // There's a subtle reason to print '\n's before, and an endl at the end:
    // cout can choose to buffer things up when there's a lot of output, and
    // endl forces a buffer flush, but '\n' doesn't. This way of doing it helps
    // to get the "one frame at a time" impression in our displaying.
    cout << endl;
}
