/*
  a, Some game!
  idk what it caled. but, the game is just make player
  go up when button pressed and go down if button not pressed.

  Author:
  @reazon

  note: now i will call it Obstacle Jumper!
*/

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define level_ROW 2
#define level_COLS 20
int gameLevel[level_ROW][level_COLS] = { // This is a map, '0' is space and '1' is object. 
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
int playerPosition_Y = 1;
int playerPosition_X = 3;
const int buttonPin = 13;
int score = 0;
uint8_t enemy_0[8] = { 
  0b00000,
  0b01110,
  0b10001,
  0b01110,
  0b01110,
  0b10001,
  0b01110,
  0b00000,
};

uint8_t player[8] = {
  0b11000,
  0b00010,
  0b00010,
  0b01111,
  0b00010,
  0b00010,
  0b00000,
  0b11000,
};
uint8_t player2[8] = {
  0b11000,
  0b00000,
  0b00010,
  0b00010,
  0b01111,
  0b00010,
  0b00010,
  0b11000,
};

uint8_t enemy[8] = {
  0b00000,
  0b00000,
  0b11111,
  0b01110,
  0b01110,
  0b11111,
  0b00000,
  0b00000,
};

 /*
  renderGameOnLCD

  render obstacles and player to lcd
*/
void renderGameOnLCD() {
  for (int row = 0; row < level_ROW; row++) {
    for (int i = 1; i < level_COLS - 1; i++) {
      lcd.setCursor(i, row);
      if (row == playerPosition_Y && playerPosition_X == i) lcd.print(row ? ">" : "D");
      else if(gameLevel[row][i]){
        if (gameLevel[row][i] == 1) gameLevel[row][i] = 2;
        else gameLevel[row][i] = 1;
        lcd.print(gameLevel[row][i] == 1 ? "C" : "<");
      }
      else lcd.print(" ");
    }
  }
}


/*
  moveObstacles

  move obstacle to the player (left). and if there have space in the right,
  it will generate more obstacle
*/
void moveObstacles() {
  for (int i = 0; i < level_ROW; i++) {
    memmove(&gameLevel[i][0], &gameLevel[i][1], (level_COLS - 1) * sizeof(int));
    gameLevel[i][level_COLS - 1] = 0; // clear the last element
  }

  // cheking for obstacle 
  boolean generate = true; // generate will be false if no more space to generate obstacle
  for (int i = 0; i < level_ROW; i++) {
    for (int j = 15; j <= level_COLS; j++) {
      if (gameLevel[i][j] == 1 && generate == true) generate = false;
    }
  }

  // add more obtacle 
  if (generate) {
    int ran_lenght = random(2, 5);
    int ran_line = random(0, 2);
    int ran_enmy = random(1, 3);

    for (int i = 20; i >= 15; i--) {
      if (ran_lenght >= 0) {
        gameLevel[ran_line][i] = ran_enmy;
        ran_lenght -= 1;
      }
    }
  }
}

/*
  checkCollision

  theres an obstacles on player position?
  - well game over.
*/
void checkCollision() {
  if (gameLevel[playerPosition_Y][playerPosition_X]) {
    lcd.setCursor(1, 0);
    lcd.print("GAME OVER!");
    lcd.setCursor(1, 1);
    lcd.print("SCORE: ");
    lcd.print(score);
    score = 0;
    delay(700);
    while (digitalRead(buttonPin) == HIGH) {
      delay(1); 
    }
    for (int i = 0; i < level_ROW; i++) {
      for (int j = 0; j < level_COLS - 1; j++) {
        gameLevel[i][j] = 0;
      }
      gameLevel[i][level_COLS - 1] = 0; // clear
    }
  }
  else score++;
}

/*
 updateGameState

 updating game state by change player positon,
 moving obstacle and check for collision
*/
void updateGameState(){
  playerPosition_Y = digitalRead(buttonPin) == LOW ? 0 : 1;
  moveObstacles();
  checkCollision();
}

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.createChar(2, enemy);
  lcd.createChar(3, enemy_0);
  lcd.createChar(4, player);
  lcd.createChar(5, player2);

  pinMode(buttonPin, INPUT_PULLUP);
  renderGameOnLCD();
  randomSeed(millis());
}

void loop() {
  delay(40); 
  updateGameState();
  renderGameOnLCD();
}