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

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

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

bool runGame = true;
bool gameOver = false;

int gameEra = 0;
int currentTick;
int finalTicks;
int cellsAliveNow;
int cellsAliveBefore;
int noEvolutionTicks;

const uint scoreScreenTimeout = 5000;
unsigned long currentMillis;

void setupGameOfLife()
{
  gameOver = false;
  gameEra++;
  currentTick = 0;
  finalTicks = 0;
  cellsAliveBefore = 0;
  noEvolutionTicks = 0;
  randomSeed(analogRead(34));
  createRandomMatrix(g);
  for (int i = 0; i < 10; i++)
  {
    addGlider(random(SCREEN_HEIGHT), random(SCREEN_WIDTH), g);
  }
  logLine("Starting Game " + String(gameEra));
}

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])
{
  cellsAliveNow = 0;
  currentTick++;
  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];
    }
  }
}

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 == false)
  {
    return;
  }

  gameOfLife(g);

  if (gameOver)
  {

    int brightness = constrain(floor(255 * (1 - ((float)(millis() - currentMillis) / (float)scoreScreenTimeout * 2))), 0, 255);
    gameBrightness(brightness);

    if (millis() - currentMillis > scoreScreenTimeout)
    {
      resetGame();
    }
  }
  else // not gameOver
  {

    if (currentTick % 2)
    {
      // char msg[80];
      // sprintf(msg, "Tick: %4d, Cells now: %4d, before: %4d - No evolution since: %3d", currentTick, cellsAliveNow, cellsAliveBefore, noEvolutionTicks);
      // logLine(msg);
      cellsAliveBefore = cellsAliveNow;
    }

    if (cellsAliveNow >= cellsAliveBefore - 5 &&
        cellsAliveNow <= cellsAliveBefore + 5)
    {
      noEvolutionTicks++;
    }
    else
    {
      noEvolutionTicks = 0;
    }

    if (noEvolutionTicks > noEvolutionTicksLimit)
    {
      finalTicks = currentTick - noEvolutionTicksLimit;
      currentMillis = millis();
      showEndScreen(finalTicks);
      gameOver = true;
    }
  }
}

void resetGame()
{
  setupGameOfLife();
  clearDisplay();
}