#include <LiquidCrystal.h>

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

const int buttons[4] = {5, 4, 3, 2};
int game_map[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const int speaker = 6;
int game_interval = 200;
int current_interval = 0;
int score = 100;
int Hp = 5;

uint8_t arrow_top[8] = {
  0b00000,
  0b00000,
  0b00100,
  0b01110,
  0b10101,
  0b00100,
  0b00000,
  0b00000,
};

uint8_t arrow_down[8] = {
  0b00000,
  0b00000,
  0b00100,
  0b10101,
  0b01110,
  0b00100,
  0b00000,
  0b00000,
};

uint8_t arrow_right[8] = {
  0b00000,
  0b00100,
  0b00010,
  0b01111,
  0b00010,
  0b00100,
  0b00000,
  0b00000,
};
uint8_t arrow_left[8] = {
  0b00000,
  0b00100,
  0b01000,
  0b11110,
  0b01000,
  0b00100,
  0b00000,
  0b00000,
};

uint8_t blank[8] = {
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
};

uint8_t heart[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b11111,
  0b01110,
  0b00100,
  0b00000,
  0b00000,
};

int generate () {
  // give a some space
  bool its_must_be_space = game_map[0] &&
                           game_map[1] &&
                           game_map[2] &&
                           game_map[3] &&
                           game_map[4] &&
                           game_map[5];

  if (its_must_be_space) return 0;
  return random(0, 5);
}

// Moving all variable to the right;
void move() {
  for (int i = 17; i >= 0; i--) {
    game_map[i + 1] = game_map[i];
  }
  game_map[0] = generate();
}

void setup() {
  lcd.createChar(1, arrow_top);
  lcd.createChar(2, arrow_right);
  lcd.createChar(3, arrow_down);
  lcd.createChar(4, arrow_left);
  lcd.createChar(5, blank);
  lcd.createChar(6, heart);
  lcd.begin(16, 2);

  lcd.print("\x01\x02\x03\x04");
  delay(600);

  for (int button_pin : buttons) {
    pinMode(button_pin, INPUT_PULLUP);
  }
  pinMode(speaker, OUTPUT);
  greeting();
  reset();
  // Serial.begin(115200);
}

const greeting() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Game Gwe");
  tone(speaker, 330, 100);
  delay(800);
}

String getChara(int number) {
  switch (number) {
    case 1:
      return "\x01";
    case 2:
      return "\x02";
    case 3:
      return "\x03";
    case 4:
      return "\x04";
    case 5:
      return "-";
    case -1:
      return "x";
    default:
      return " ";
  }
}

void render() {
  lcd.clear();

  for (int i = 0; i < 16; i++) {
    int pointer = i;
    if (pointer < 0) continue;
    lcd.setCursor(pointer, 0);
    String chara = getChara(game_map[i]);
    lcd.print(chara);
  }

  for (int i = 0; i < Hp; i++) {
    lcd.setCursor(15 - i, 1);
    lcd.print("\x06");
  }

  lcd.setCursor(6, 1);
  lcd.print("[ ]");
  // lcd.setCursor(7, 1);
  // lcd.print("^");
  lcd.setCursor(0, 1);
  lcd.print(score);
}

int inputChek() {
  for (int i = 0; i < 4; i++) {
    const int button_pin = buttons[i];
    const bool pressed = !digitalRead(button_pin);
    if (pressed) {
      return i + 1;
    }
  }
  return 0;
}

void reset() {
  // lcd.clear();
  game_interval = 600;
  current_interval = 0;
  Hp = 5;

  lcd.setCursor(11, 1);
  lcd.print("     ");

  for (int i = 0; i < 17; i++) {
    game_map[i] = 0;
  }

  for (int i = 0; i < 16; i++) {
    move();
  }

  for (int i = 0; i < 30; i++) {
    int remove_at = i - 14;
    if (i < 16) {
      lcd.setCursor(i, 0);
      lcd.print("\x05");
    }
    if (remove_at >= 0) {
      lcd.setCursor(remove_at, 0);
      String chara = getChara(game_map[remove_at]);
      lcd.print(chara);
    }
    delay(70);
  }

  while (score != 0) {
    if (score > 0 ) score--;
    else score++;
    lcd.setCursor(0, 1);
    lcd.print("     ");
    lcd.setCursor(0, 1);
    lcd.print(score);
    delay(40);
  }
  
  for (int i = 0; i < Hp; i++) {
    lcd.setCursor(15 - i, 1);
    lcd.print("\x06");
    delay(40);
  }

}


void loop() {
  render();

  bool havePressedButton = false;
  while (current_interval < game_interval) {
    delay(1);
    current_interval++;
    const int input = inputChek();
    if (input == 0 || havePressedButton) continue;
    if (game_map[7] == 5 || game_map[7] == -1) continue;

    // input chek
    if (input == game_map[7]) {
      score++;
      game_map[7] = 5;
      tone(speaker, 450, 10);
      lcd.setCursor(7, 1);

      String chara = getChara(input);
      lcd.print(chara);
    }
    else {
      Hp--; score--;
      game_map[7] = -1;
      tone(speaker, 170, 100);
      lcd.setCursor(7, 1);
      lcd.print("x");
    }
    havePressedButton = true;
  }
  havePressedButton = false;

  // if (game_map[15] > 0 && game_map[15] != 5) {
  //   Hp--;
  //   tone(speaker, 170, 100);
  // }

  // game over
  if (Hp == 0) {
    lcd.setCursor(0, 1);
    lcd.print(score);
    while (true) {
      delay(200);
      lcd.setCursor(11, 1);
      lcd.print("     ");
      tone(speaker, 170, 100);
      delay(300);
      lcd.setCursor(11, 1);
      lcd.print("\x06\x06\x06\x06\x06");
      if (!inputChek()) continue;
      reset();
      return;
    }
  }

  current_interval = 0;
  // render();
  move();
}