#include <LedControl.h>

#define BUTTON_UP VP
#define BUTTON_DOWN VN
#define BUTTON_LEFT D34
#define BUTTON_RIGHT D35

LedControl lc = LedControl(D21, D18, D19, 1); // Pins: DIN, CLK, LOAD, Number of MAX7219 modules

#define SNAKE_SIZE 10
#define GRID_SIZE 8
#define FOOD_SIZE 1

int snakeX[SNAKE_SIZE];
int snakeY[SNAKE_SIZE];
int foodX, foodY;
int direction = 0; // 0: up, 1: right, 2: down, 3: left
int score = 0;
bool game_over = false;

void setup() {
  pinMode(BUTTON_UP, INPUT_PULLUP);
  pinMode(BUTTON_DOWN, INPUT_PULLUP);
  pinMode(BUTTON_LEFT, INPUT_PULLUP);
  pinMode(BUTTON_RIGHT, INPUT_PULLUP);

  lc.shutdown(0, false); // Wake up displays
  lc.setIntensity(0, 8); // Set brightness level (0 is min, 15 is max)
  lc.clearDisplay(0); // Clear display register

  randomSeed(analogRead(0));

  // Initialize snake
  for (int i = 0; i < SNAKE_SIZE; i++) {
    snakeX[i] = 3;
    snakeY[i] = 3 + i;
  }

  // Place food
  spawnFood();
}

void loop() {
  if (!game_over) {
    draw();
    checkButtons();
    move();
    checkCollision();
    delay(100);
  } else {
    delay(2000);
    resetGame();
  }
}

void draw() {
  lc.clearDisplay(0);
  lc.setLed(foodX, foodY, 0, true);

  for (int i = 0; i < SNAKE_SIZE; i++) {
    lc.setLed(snakeX[i], snakeY[i], 0, true);
  }
}

void move() {
  for (int i = SNAKE_SIZE - 1; i > 0; i--) {
    snakeX[i] = snakeX[i - 1];
    snakeY[i] = snakeY[i - 1];
  }
  switch (direction) {
    case 0:
      snakeY[0]--;
      break;
    case 1:
      snakeX[0]++;
      break;
    case 2:
      snakeY[0]++;
      break;
    case 3:
      snakeX[0]--;
      break;
  }
}

void checkCollision() {
  // Check if the snake has hit the walls
  if (snakeX[0] < 0 || snakeX[0] >= GRID_SIZE || snakeY[0] < 0 || snakeY[0] >= GRID_SIZE) {
    game_over = true;
  }

  // Check if the snake has hit itself
  for (int i = 1; i < SNAKE_SIZE; i++) {
    if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
      game_over = true;
    }
  }

  // Check if the snake has eaten the food
  if (snakeX[0] == foodX && snakeY[0] == foodY) {
    score++;
    spawnFood();
    if (SNAKE_SIZE < GRID_SIZE * GRID_SIZE) {
      SNAKE_SIZE++;
    }
  }
}

void spawnFood() {
  foodX = random(0, GRID_SIZE);
  foodY = random(0, GRID_SIZE);
}

void resetGame() {
  score = 0;
  game_over = false;
  for (int i = 0; i < SNAKE_SIZE; i++) {
    snakeX[i] = 3;
    snakeY[i] = 3 + i;
  }
  spawnFood();
}

void checkButtons() {
  if (digitalRead(BUTTON_UP) == LOW && direction != 2) {
    direction = 0;
  } else if (digitalRead(BUTTON_DOWN) == LOW && direction != 0) {
    direction = 2;
  } else if (digitalRead(BUTTON_LEFT) == LOW && direction != 1) {
    direction = 3;
  } else if (digitalRead(BUTTON_RIGHT) == LOW && direction != 3) {
    direction = 1;
  }
}