#include <LiquidCrystal.h>

const int btPin = 2;

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

byte player_rest[][8] = {
  { B01110, B00110, B00100, B01110, B10101, B00100, B01010, B01010 },
  { B00000, B01110, B00110, B00100, B01110, B10101, B01010, B01010 }
};

byte player_run[][8] = {
  { B01110, B00110, B00101, B11111, B10100, B00100, B01010, B10001 },
  { B01110, B00110, B10100, B11111, B00101, B00100, B01010, B01010 }
};

byte player_jump[][8] = {
  { B01110, B00110, B10101, B11111, B00100, B01110, B01010, B01000 },
  { B01110, B00110, B10101, B11111, B00100, B01110, B01010, B01010 }
};

byte objectChar[] = {
  B00100,
  B01110,
  B11111,
  B11111,
  B11111,
  B01110,
  B00100,
  B00000
};

// Variables for animations
unsigned long previousPlayerTime = 0;
unsigned long previousObjectTime = 0;
unsigned long previousJumpTime = 0;

int playerFrame = 0; // To switch between animation frames
int objectColumn = 15; // Starting position of the object
bool isJumping = false; // Track jump state
int playerColumn = 3; // Player column position
int jumpCounter = 0; // Track jump frames

// Game over flag
bool gameOver = false;

void displayPlayer(byte arr[][8], int mem, int row, int col) {
  lcd.clear();
  lcd.createChar(mem, arr[playerFrame]);
  lcd.setCursor(col, row);
  lcd.write(byte(mem));
}

void moveObject() {
  unsigned long currentTime = millis();
  if (currentTime - previousObjectTime >= 200) { // Object speed
    previousObjectTime = currentTime;

    // Clear the current object position
    lcd.setCursor(objectColumn, 1);
    lcd.print(" ");

    // Update object position
    objectColumn--;
    if (objectColumn < 0) {
      objectColumn = 15; // Reset to the rightmost column
    }

    // Display the object in the new position
    lcd.setCursor(objectColumn, 1);
    lcd.write(byte(1));
  }
}

void animatePlayer() {
  unsigned long currentTime = millis();
  if (currentTime - previousPlayerTime >= 300) { // Player animation speed
    previousPlayerTime = currentTime;

    if (isJumping) {
      displayPlayer(player_jump, 0, 0, playerColumn); // Jump animation
    } else {
      displayPlayer(player_run, 0, 1, playerColumn); // Run animation
    }

    playerFrame = 1 - playerFrame; // Toggle between frame 0 and 1
  }
}

void handleJump() {
  if (isJumping) {
    unsigned long currentTime = millis();
    if (currentTime - previousJumpTime >= 150) { // Jump animation speed
      previousJumpTime = currentTime;

      if (jumpCounter < 2) {
        jumpCounter++;
      } else {
        // End jump animation
        isJumping = false;
        jumpCounter = 0;
      }
    }
  }
}

// Function to check for collision between player and object
void checkCollision() {
  if (objectColumn == playerColumn && !isJumping) { // Player and object in the same position, and player is not jumping
    gameOver = true; // Trigger game over
  }
}

void displayGameOver() {
  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("GAME OVER");
}

void setup() {
  lcd.begin(16, 2);

  // Define custom characters
  lcd.createChar(0, player_run[0]);
  lcd.createChar(1, objectChar);

  pinMode(btPin, INPUT_PULLUP);
}

void loop() {
  if (gameOver) {
    displayGameOver(); // Show game over message
    return; // Stop further execution if game is over
  }

  // Check for button press to trigger jump
  if (digitalRead(btPin) == LOW && !isJumping) {
    isJumping = true; // Start jump animation
  }

  moveObject();      // Animate the moving object
  animatePlayer();   // Animate the player
  handleJump();      // Handle jump animation
  checkCollision();  // Check for collision
  delay(10);
}