#include <LedControl.h>

#define LED_CLK  13
#define LED_CS   12
#define LED_DATA 11

#define STATE_START    0
#define STATE_GLOOP    1
#define STATE_GOVER    2

#define JOY_X A0
#define JOY_Y A1
#define JOY_BTN 10

#define MAX_LENGTH 64

int gameState = STATE_START;
int snakeX[MAX_LENGTH];
int snakeY[MAX_LENGTH];
int snakeLength = 3;  // starting length
int score = 0;
char score_txt[17] = {0};

int dx = 1;  // start moving right
int dy = 0;
int dxr = 0;
int dyr = 1;
int x = 0;
int y = 0;

unsigned long lastMove = 0;
const int speed = 200;  // move every 200 ms

int foodX = random(16);
int foodY = random(16);

const int thresholdLow = 400;
const int thresholdHigh = 600;

LedControl lc = LedControl(LED_DATA, LED_CLK, LED_CS, 4);

void setPixel(int x, int y, boolean state) {
  int device, localX, localY;

  if (x < 8 && y < 8) {
    device = 1;  // top-left physical
    localX = x;
    localY = y;
  } else if (x >= 8 && y < 8) {
    device = 0;  // top-right physical
    localX = x - 8;
    localY = y;
  } else if (x < 8 && y >= 8) {
    device = 3;  // bottom-left physical
    localX = x;
    localY = y - 8;
  } else {
    device = 2;  // bottom-right physical
    localX = x - 8;
    localY = y - 8;
  }

  // Apply mirroring if needed (you might need to test these!)
  localX = 7 - localX;  // horizontal flip
  // localY = 7 - localY;  // uncomment if vertical flip needed

  lc.setLed(device, localY, localX, state);
}

void handleInput(int xVal, int yVal) {
  if (xVal < thresholdLow && dx != -1) {
    dxr = 1; dyr = 0;
  }
  if (xVal > thresholdHigh && dx != 1) {
    dxr = -1; dyr = 0;
  }
  if (yVal < thresholdLow && dy != -1) {
    dxr = 0; dyr = 1;
  }
  if (yVal > thresholdHigh && dy != 1) {
    dxr = 0; dyr = -1;
  }
}

void showStartScreen() {
  static bool toggle = false;
  static unsigned long lastBlink = 0;

  if (millis() - lastBlink > 500) {  // blink every 500 ms
    lastBlink = millis();
    toggle = !toggle;

    for (int y = 0; y < 16; y++) {
      for (int x = 0; x < 16; x++) {
        if ((x + y) % 2 == (toggle ? 0 : 1)) {
          setPixel(x, y, true);
        } else {
          setPixel(x, y, false);
        }
      }
    }
  }
}

void startGame() {
  snakeLength = 3;
  snakeX[0] = 8; snakeY[0] = 8;
  snakeX[1] = 7; snakeY[1] = 8;
  snakeX[2] = 6; snakeY[2] = 8;

  dx = 1; dy = 0;

  foodX = random(16);
  foodY = random(16);

  lc.clearDisplay(0);
  lc.clearDisplay(1);
  lc.clearDisplay(2);
  lc.clearDisplay(3);

  gameState = STATE_GLOOP;

  setPixel(foodX, foodY, true);
}

void updateGame() {
  if (millis() - lastMove < speed) {
    return;  // too soon, wait
  }
  dx = dxr;
  dy = dyr;
  lastMove = millis();

  bool ateFood = (snakeX[0] + dx == foodX && snakeY[0] + dy == foodY);

  // Shift body forward
  if (!ateFood) {
    // Erase tail on display
    setPixel(snakeX[snakeLength - 1], snakeY[snakeLength - 1], false);
    for (int i = snakeLength - 1; i > 0; i--) {
      snakeX[i] = snakeX[i - 1];
      snakeY[i] = snakeY[i - 1];
    }
  } else {
    // Growing: shift and increase length
    for (int i = snakeLength; i > 0; i--) {
      snakeX[i] = snakeX[i - 1];
      snakeY[i] = snakeY[i - 1];
    }
    ++snakeLength;
    ++score;
    if (snakeLength > MAX_LENGTH) snakeLength = MAX_LENGTH;
  }

  // Add new head position
  snakeX[0] += dx;
  snakeY[0] += dy;

  // Wrap edges
  if (snakeX[0] < 0) snakeX[0] = 15;
  if (snakeX[0] > 15) snakeX[0] = 0;
  if (snakeY[0] < 0) snakeY[0] = 15;
  if (snakeY[0] > 15) snakeY[0] = 0;

  // Check for self-collision
  for (int i = 1; i < snakeLength; i++) {
    if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
      gameState = STATE_GOVER;
      return;
    }
  }

  // Draw new head
  setPixel(snakeX[0], snakeY[0], true);

  // Check if ate food and place new food
  if (ateFood) {
    bool valid;
    do {
      foodX = random(16);
      foodY = random(16);
      valid = true;
      for (int i = 0; i < snakeLength; i++) {
        if (snakeX[i] == foodX && snakeY[i] == foodY) {
          valid = false;
          break;
        }
      }
    } while (!valid);
    // Draw food
    setPixel(foodX, foodY, true);
  }
}

void showGameOverScreen() {
  for (int y = 0; y < 16; y++) {
    for (int x = 0; x < 16; x++) {
      setPixel(x, y, true);
    }
  }
}

void setup() {
  gameState = STATE_START;
  pinMode( JOY_BTN, INPUT_PULLUP );
  for (int i = 0; i < 4; i++) {
    lc.shutdown(i, false);      // Wake up MAX7221
    lc.setIntensity(i, 8);      // Set brightness (0–15)
    lc.clearDisplay(i);         // Clear display
  }
  // Initialize snake in center
  snakeX[0] = 8; snakeY[0] = 8;
  snakeX[1] = 7; snakeY[1] = 8;
  snakeX[2] = 6; snakeY[2] = 8;
}

void loop() {
  int xVal = analogRead(JOY_X);
  int yVal = analogRead(JOY_Y);
  bool buttonPressed = (digitalRead(JOY_BTN) == LOW);

  switch (gameState) {
    case STATE_START:
      // Show welcome / ready screen
      if (buttonPressed) {
        startGame();
      } else {
        showStartScreen();
      }
      break;

    case STATE_GLOOP:
      handleInput(xVal, yVal);
      updateGame();
      break;

    case STATE_GOVER:
      // Show game over screen
      if (buttonPressed) {
        gameState = STATE_START;
      } else {
        showGameOverScreen();
      }
      break;
  }
}

/*
void loop() {

  int xVal = analogRead(JOY_X);
  int yVal = analogRead(JOY_Y);

  // Horizontal
  if (xVal < thresholdLow && dx != -1) {
    dx = 1; dy = 0;  // Left
  }
  if (xVal > thresholdHigh && dx != 1) {
    dx = -1; dy = 0;   // Right
  }

  // Vertical
  if (yVal < thresholdLow && dy != -1) {
    dx = 0; dy = 1;  // Up
  }
  if (yVal > thresholdHigh && dy != 1) {
    dx = 0; dy = -1;   // Down
  }
  if (millis() - lastMove > speed) {
    lastMove = millis();

    // Check if eating food
    bool ateFood = (snakeX[0] + dx == foodX && snakeY[0] + dy == foodY);

    // Shift body (only if not growing)
    if (!ateFood) {
      setPixel(snakeX[snakeLength - 1], snakeY[snakeLength - 1], false);
      for (int i = snakeLength - 1; i > 0; i--) {
        snakeX[i] = snakeX[i - 1];
        snakeY[i] = snakeY[i - 1];
      }
    } else {
      // Growing → shift all forward and extend length
      for (int i = snakeLength; i > 0; i--) {
        snakeX[i] = snakeX[i - 1];
        snakeY[i] = snakeY[i - 1];
      }
      snakeLength++;
      if (snakeLength >= MAX_LENGTH) snakeLength = MAX_LENGTH;
    }

    // Add new head
    snakeX[0] += dx;
    snakeY[0] += dy;

    // Wrap edges
    if (snakeX[0] < 0) snakeX[0] = 15;
    if (snakeX[0] > 15) snakeX[0] = 0;
    if (snakeY[0] < 0) snakeY[0] = 15;
    if (snakeY[0] > 15) snakeY[0] = 0;

    // Check collision (with self)
    for (int i = 1; i < snakeLength; i++) {
      if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
        // Game over → reset
        snakeLength = 3;
        snakeX[0] = 8; snakeY[0] = 8;
        snakeX[1] = 7; snakeY[1] = 8;
        snakeX[2] = 6; snakeY[2] = 8;
      }
    }

    // Draw snake
    for (int i = 0; i < snakeLength; i++) {
      setPixel(snakeX[i], snakeY[i], true);
    }

    // Draw food
    setPixel(foodX, foodY, true);

    // Spawn new food if eaten
    if (ateFood) {
      bool valid;
      do {
        foodX = random(16);
        foodY = random(16);
        valid = true;
        for (int i = 0; i < snakeLength; i++) {
          if (snakeX[i] == foodX && snakeY[i] == foodY) {
            valid = false;
            break;
          }
        }
      } while (!valid);
    }
  }
}
*/