#include <Arduino.h>
#include "display.h"
#include "gameoflife.h"

// https://github.com/Stavrosfil/game-of-life-esp32

int g[SCREEN_HEIGHT][SCREEN_WIDTH];
int arrayCopy[SCREEN_HEIGHT][SCREEN_WIDTH];

bool runGame = true;

int currentTick;
int cellsAliveBefore;
int noEvolutionTicks;

void setupGameOfLife()
{
  randomSeed(analogRead(34));
  createRandomMatrix(g);
  for (int i = 0; i < 10; i++)
    addGlider(random(SCREEN_HEIGHT), random(SCREEN_WIDTH), g);
  clearDisplay();
  currentTick = 0;
  cellsAliveBefore = 0;
  noEvolutionTicks = 0;
}

void createRandomMatrix(int (&a)[SCREEN_HEIGHT][SCREEN_WIDTH])
{
  for (int i = 0; i < SCREEN_HEIGHT; i++)
  {
    for (int j = 0; j < SCREEN_WIDTH; j++)
    {
      a[i][j] = random(100) < 25 ? 1 : 0;
    }
  }
}

void gameOfLife(int (&a)[SCREEN_HEIGHT][SCREEN_WIDTH])
{
  currentTick++;
  int cellsAliveNow = 0;
 
  for (int i = 0; i < SCREEN_HEIGHT; i++)
  {
    for (int j = 0; j < SCREEN_WIDTH; j++)
    {
      arrayCopy[i][j] = a[i][j];
    }
  }

  for (int i = 0; i < SCREEN_HEIGHT; i++)
  {
    for (int j = 0; j < SCREEN_WIDTH; j++)
    {
      int total = (a[i][(j - 1) % SCREEN_WIDTH] + a[i][(j + 1) % SCREEN_WIDTH] + a[(i - 1) % SCREEN_HEIGHT][j] +
                   a[(i + 1) % SCREEN_HEIGHT][j] + a[(i - 1) % SCREEN_HEIGHT][(j - 1) % SCREEN_WIDTH] +
                   a[(i - 1) % SCREEN_HEIGHT][(j + 1) % SCREEN_WIDTH] +
                   a[(i + 1) % SCREEN_HEIGHT][(j - 1) % SCREEN_WIDTH] +
                   a[(i + 1) % SCREEN_HEIGHT][(j + 1) % SCREEN_WIDTH]);

      if (a[i][j] == 1)
      {
        if (total < 2 || total > 3)
        {
          arrayCopy[i][j] = 0;
        }
      }
      else if (total == 3)
      {
        arrayCopy[i][j] = 1;
        cellsAliveNow++;
      }
    }
  }

  for (int i = 0; i < SCREEN_HEIGHT; i++)
  {
    for (int j = 0; j < SCREEN_WIDTH; j++)
    {
      a[i][j] = arrayCopy[i][j];
    }
  }
  
  if (currentTick % 2) {
    cellsAliveBefore = cellsAliveNow;
  }
  
  if (cellsAliveNow == cellsAliveBefore)
  {
    noEvolutionTicks++;
  }
  else
  {
    noEvolutionTicks = 0;
  }

  if (noEvolutionTicks > noEvolutionTicksLimit)
  {
    setupGameOfLife();
  }
}

void addGlider(int i1, int j1, int (&a)[SCREEN_HEIGHT][SCREEN_WIDTH])
{
  // 010
  // 001
  // 111
  int glider[3][3] = {{0, 0, 1}, {1, 0, 1}, {0, 1, 1}};
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      a[i1 + i][j1 + j] = glider[i][j];
    }
  }
}

void gameLoop()
{
  if (runGame)
    gameOfLife(g);
}