const unitLength = 20;
let boxColor = 150;

const strokeColor = 50;
let columns; /* To be determined by window width */
let rows; /* To be determined by window height */

let currentBoard;
let nextBoard;

let o_currentBoard;
let o_nextBoard;

let color_white = 255;
let paused_game = false;

let color_dice;
let color_dice_darken;

function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

/**
 * Initialize/reset the board state
 */
function init(fr) {
  console.debug({ init: { fr } });

  frameRate(fr || 10); // Attempt to refresh at starting FPS

  for (let i = 0; i < columns; i++) {
    for (let j = 0; j < rows; j++) {
      currentBoard[i][j] = 0;
      nextBoard[i][j] = 0;

      o_currentBoard[i][j] = { alive: 0 };
      o_nextBoard[i][j] = { alive: 0 };
    }
  }
}

function generate() {
  //Loop over every single box on the board
  for (let x = 0; x < columns; x++) {
    for (let y = 0; y < rows; y++) {
      // Count all living members in the Moore neighborhood(8 boxes surrounding)
      let neighbors = 0;
      let o_neighbors = 0;

      for (let i of [-1, 0, 1]) {
        for (let j of [-1, 0, 1]) {
          if (i == 0 && j == 0) {
            // the cell itself is not its own neighbor
            continue;
          }
          // The modulo operator is crucial for wrapping on the edge
          // neighbors += currentBoard[(x + i + columns) % columns][(y + j + rows) % rows];
          o_neighbors += parseInt(o_currentBoard[(x + i + columns) % columns][(y + j + rows) % rows].alive);

          // Rules of Life
          if (o_currentBoard[x][y].alive == 1 && o_neighbors < 2) {
            // Die of Loneliness
            o_nextBoard[x][y] = { alive: 0 };
          } else if (o_currentBoard[x][y].alive == 1 && o_neighbors > 3) {
            // Die of Overpopulation
            o_nextBoard[x][y] = { alive: 0 };
          } else if (o_currentBoard[x][y].alive == 0 && o_neighbors == 3) {
            // New life due to Reproduction
            let color_idx = getRandomInt(color_dice_length);
            o_nextBoard[x][y] = { alive: 1, color: color_dice[color_idx], color_idx };
          } else {
            // Stasis
            o_nextBoard[x][y] = o_currentBoard[x][y];
            o_nextBoard[x][y].color = color(64, 64, 64);
          }
        }
      }
    }
  }

  // Swap the nextBoard to be the current Board
  [currentBoard, nextBoard] = [nextBoard, currentBoard];
  [o_currentBoard, o_nextBoard] = [o_nextBoard, o_currentBoard];
}

function setup() {
  console.log('setup');
  // Setup logic here
  boxColor = color(255, 204, 0);

  color_dice = [
    color(250, 211, 144),
    color(106, 137, 204),
    color(184, 233, 148),
    color(248, 194, 145),
    color(130, 204, 221),
  ];
  color_dice_length = color_dice.length;

  /* Set the canvas to be under the element #canvas*/

  // NOTES:
  // Let's look at some of the magic variables here. The magic variables include windowWidth, windowHeight, width and height. They are all provided by p5.js to make our life easier.
  // windowWidth and windowHeight are the width and height of the viewport.
  // width and height are the width and height of the canvas element.
  const canvas = createCanvas(windowWidth, windowHeight - 100);
  canvas.parent(document.querySelector('#canvas'));

  /*Calculate the number of columns and rows */

  // NOTES:
  // We are calling createCanvas() with windowWidth and windowHeight - 100 to make a canvas that is as wide as the screen but 100 px shorter than the height.
  // We then use .parent() to insert our canvas element to the element with id canvas.
  columns = floor(width / unitLength);
  rows = floor(height / unitLength);

  /*Making both currentBoard and nextBoard 2-dimensional matrix that has (columns * rows) boxes. */
  currentBoard = [];
  nextBoard = [];

  o_currentBoard = [];
  o_nextBoard = [];

  for (let i = 0; i < columns; i++) {
    currentBoard[i] = [];
    nextBoard[i] = [];

    o_currentBoard[i] = [];
    o_nextBoard[i] = [];
  }
  // Now both currentBoard and nextBoard are array of array of undefined values.
  init(); // Set the initial values of the currentBoard and nextBoard
}

function draw() {
  // draw logic here
  background(255);

  if (paused_game) {
    //
  } else {
    generate();
  }
  for (let i = 0; i < columns; i++) {
    for (let j = 0; j < rows; j++) {
      if (o_currentBoard[i][j].alive == 1) {
        // let idx = getRandomInt(color_dice_length);
        // fill(color_dice[idx]);
        fill(o_currentBoard[i][j].color);
      } else {
        fill(color_white);
      }
      stroke(strokeColor);
      rect(i * unitLength, j * unitLength, unitLength, unitLength);
    }
  }
}
/**
 * When mouse is dragged
 */
function mouseDragged() {
  /**
   * If the mouse coordinate is outside the board
   */
  if (mouseX > unitLength * columns || mouseY > unitLength * rows) {
    return;
  }
  const x = Math.floor(mouseX / unitLength);
  const y = Math.floor(mouseY / unitLength);

  currentBoard[x][y] = 1;
  o_currentBoard[x][y] = { alive: 1, color: boxColor };

  fill(boxColor);
  stroke(strokeColor);
  rect(x * unitLength, y * unitLength, unitLength, unitLength);
}

/**
 * When mouse is pressed
 */
function mousePressed() {
  noLoop();
  mouseDragged();
}

/**
 * When mouse is released
 */
function mouseReleased() {
  loop();
}

function windowResized() {
  resizeCanvas(window.innerWidth, window.innerHeight);
  setup();
}

document.addEventListener('DOMContentLoaded', () => {
  document.querySelector('#frame-rate-value').innerHTML = 10;
  document.querySelector('#frame-rate-range').value = 10;

  document.querySelector('#reset-game').addEventListener('click', function () {
    init(10);
    document.querySelector('#frame-rate-value').innerHTML = 10;
  });

  document.querySelector('#pause-game').addEventListener('click', function () {
    paused_game = !paused_game;
    if (paused_game) {
      document.querySelector('#game_status').innerHTML = 'paused';
    } else {
      document.querySelector('#game_status').innerHTML = 'running';
    }
  });

  document.querySelector('#frame-rate-range').addEventListener('change', function () {
    let ele = document.querySelector('#frame-rate-range');
    let framerate_value = ele.value;
    document.querySelector('#frame-rate-value').innerHTML = framerate_value;

    init(parseInt(framerate_value));
  });
});

// Control speed of the Game of Life.
// (Checkout framerate, you can use slider to control the framerate )

// Allow users to change the rules of survival.

// Allow users to change the rules of reproduction.

// Start/Stop the Game of life

// Multiple colors of life on the same board.

// Darken colors for stable life.

// Random initial states

// Well-known patterns of Game of Life to select from (Examples: Gosper Glider Gun, Glider, Lightweight train).

// Use Keyboard to control the cursor to place the life

// Resize board on windows resize (Check out windowsResized)

// Switching between different styles.

// Anything else that you could think of.
