#include <LiquidCrystal.h>

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

const int buttons[4] = {5, 4, 3, 2};
const int speaker = 6;

int game_map[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int dificulty = 1;
int cursor = 0;
int level = 0;
int score = 0;
int Hp = 5;

String chara[6] = {"\x06", "\x01", "\x02", "\x03", "\x04", "\x05",};
const int gameTones[4] = { 262, 296, 330, 349 };
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,
};


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, heart);
  lcd.createChar(6, blank);
  delay(200);
  lcd.begin(16, 2);



  for (int button_pin : buttons) {
    pinMode(button_pin, INPUT_PULLUP);
  }

  pinMode(speaker, OUTPUT);
  greeting();
  setLevel();
  // Serial.begin(115200);
}

const void greeting() {
  lcd.clear();

  for (int i; i < 4; i++) {
    lcd.setCursor(6 + i, 0);
    lcd.print(chara[i + 1]);
    tone(speaker, 330, 100);
    delay(600);
  }

  lcd.setCursor(2, 1);
  lcd.print("Arrow Memory");
  tone(speaker, 330, 100);
  delay(1000);

  lcd.setCursor(1, 1);
  lcd.print("Game By Reazon");
  tone(speaker, 330, 100);
  delay(1200);

  lcd.clear();
  delay(800);
}

void generate() {
  for (int i; i < dificulty; i++) {
    game_map[i] = random(1, 5);
  }

}

void renderHUD() {
  // Hp
  for (int i = 0; i < 5; i++) {
    lcd.setCursor(15 - i, 1);
    lcd.print(Hp > i ? chara[5] : " ");
    delay(200);
  }

  // Score
  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) {
      while (!digitalRead(button_pin)) {
        delay(10);
      }
      return i + 1;
    }
  }
  return 0;
}

void clearA(int line) {
  for (int i = 0; i < 30; i++) {
    int remove_at = i - 14;
    if (i < 16) {
      lcd.setCursor(i, line);
      lcd.print(chara[0]);
    }
    if (remove_at >= 0) {
      lcd.setCursor(remove_at, line);
      lcd.print(" ");
    }
    delay(70);
  }
}

void startDisplayArrow() {
  for (int i; i <= dificulty; i++) {
    if (i < dificulty) {
      lcd.setCursor(i, 0);
      lcd.print(chara[game_map[i]]);
      tone(speaker, gameTones[game_map[i]], 100);
      delay(900);
    }
    int remove_at = i;
    if (remove_at >= 0) {
      lcd.setCursor(remove_at, 0);
      lcd.print(" ");
    }
  }
}

void setLevel() {
  // lcd.clear();
  renderHUD();
  if (score > (dificulty * (dificulty * 3)) && dificulty < 16) dificulty++;
  lcd.setCursor(4, 0);
  lcd.print("Level");
  // set to next dificulty
  lcd.setCursor(10, 0);
  lcd.print(level);
  level++;
  delay(500);
  lcd.setCursor(10, 0);
  lcd.print(level);
  delay(500);

  generate();
  clearA(0);
  startDisplayArrow();
}

void reset(){
  dificulty = 1;
  level = 0;
  score = 0;
  Hp = 5;
  cursor = 0;

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

  while (score != 0) {
    if (score > 0 ) score--;
    else score++;
    lcd.setCursor(0, 1);
    lcd.print("     ");
    lcd.setCursor(0, 1);
    lcd.print(score);
    delay(40);
  }
}

void loop() {
  int input = inputChek();
  if (input < 1) return;

  if (input == game_map[cursor]) {
    lcd.setCursor(cursor, 0);
    lcd.print(chara[game_map[cursor]]);
    tone(speaker, gameTones[input - 1], 100);
    score++;
    delay(100);
  }  else {
    lcd.setCursor(cursor, 0);
    lcd.print("X");
    tone(speaker, 462, 300);
    Hp--;
    delay(100);

    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("\x05\x05\x05\x05\x05");
        if (!inputChek()) continue;
        reset();
        return;
      }
    }
  }

  cursor++;
  if (cursor >= dificulty){
    cursor = 0;
    setLevel();
  }
}