#include <Keypad.h>

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

const int BASE_MIN_HOLD_TIME = 1000;
const int MAX_HOLD_TIME = 5000;
const int PENALTY = 70;
const int ROUNDS = 5;
const int MAX_LEVEL = 3;

int score = 0;
int currentRound = 1;
int level = 1;
char targetKey;
unsigned long pressStartTime = 0;
unsigned long holdDuration = 0;
bool isWaitingForPress = false;
bool isHolding = false;
bool gameOver = false;
bool nextLevelPrompt = false;

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));
  startGame();
}

void loop() {
  char key = keypad.getKey();
  
  if (nextLevelPrompt) {
    if (key == '#') {
      level++;
      Serial.println("\n=== Переход на уровень " + String(level) + " ===");
      currentRound = 1;
      gameOver = false;
      nextLevelPrompt = false;
      startNewRound();
      return;
    }
  }

  if (gameOver) {
    if (key == '*') {
      resetGame();
    }
    return;
  }

  KeyState state = keypad.getState();
  unsigned long currentTime = millis();

  if (isWaitingForPress && key) {
    if (key == targetKey) {
      pressStartTime = currentTime;
      isHolding = true;
      isWaitingForPress = false;
      Serial.println("Верная клавиша! Удерживайте...");
    } else {
      score -= PENALTY;
      Serial.println("Неверная клавиша! Штраф " + String(PENALTY) + " очков.");
      Serial.println("Текущий счёт: " + String(score));
      checkScoreLimit();
    }
  }

  if (isHolding) {
    if (state == PRESSED || state == HOLD) {
      holdDuration = currentTime - pressStartTime;

      if (holdDuration >= MAX_HOLD_TIME) {
        Serial.println("\nВы достигли максимального времени удержания!");
        endRound();
      }
    } else if (state == RELEASED) {
      Serial.println("\nКлавиша отпущена.");
      endRound();
    }
  }

  if (currentRound > ROUNDS && !gameOver && !nextLevelPrompt) {
    endGame();
  }
}

void startGame() {
  Serial.println("\n=== Игра: Реакция и сила нажатия ===");
  Serial.println("Пройдите " + String(ROUNDS) + " раундов на уровне " + String(level) + "!");
  score = 0;
  currentRound = 1;
  gameOver = false;
  nextLevelPrompt = false;
  startNewRound();
}

void startNewRound() {
  Serial.println("\nРаунд " + String(currentRound) + " из " + String(ROUNDS) + " (Уровень " + String(level) + ")");
  
  int row = random(0, ROWS);
  int col = random(0, COLS);
  targetKey = keys[row][col];

  int minHoldTime = BASE_MIN_HOLD_TIME + (currentRound - 1) * 500 - level * 200;

  Serial.println("Нажмите и удерживайте клавишу: " + String(targetKey));
  Serial.println("Минимальное время удержания: " + String(minHoldTime / 1000.0) + " секунд");
  Serial.println("Максимальное время удержания: " + String(MAX_HOLD_TIME / 1000.0) + " секунд");

  isWaitingForPress = true;
  isHolding = false;
  holdDuration = 0;
}

void endRound() {
  isHolding = false;

  int minHoldTime = BASE_MIN_HOLD_TIME + (currentRound - 1) * 500 - level * 200;
  int points = 0;

  if (holdDuration < minHoldTime) {
    points = -30 * level;
    Serial.println("Удержание слишком короткое!");
  } else if (holdDuration >= MAX_HOLD_TIME) {
    points = 100 * level;
    Serial.println("Достигнуто максимальное время удержания!");
  } else {
    points = map(holdDuration, minHoldTime, MAX_HOLD_TIME, 10, 100) * level;
  }

  score += points;

  Serial.println("Итоговое время удержания: " + String(holdDuration / 1000.0, 1) + " секунд");
  Serial.println("Очки за раунд: " + String(points));
  Serial.println("Общий счёт: " + String(score));

  checkScoreLimit();

  currentRound++;
  if (!gameOver && currentRound <= ROUNDS) {
    startNewRound();
  }
}

void endGame() {
  if (level < MAX_LEVEL) {
    Serial.println("\n=== Уровень пройден! ===");
    Serial.println("Поздравляем! Вы набрали " + String(score) + " очков.");
    Serial.println("Нажмите # для перехода на уровень " + String(level + 1));
    nextLevelPrompt = true;
  } else {
    gameOver = true;
    Serial.println("\n=== Игра завершена! ===");
    Serial.println("Вы прошли все уровни! Ваш счёт: " + String(score));
    Serial.println("Хотите сыграть ещё раз? Нажмите *.");
  }
}

void resetGame() {
  Serial.println("\nПерезапуск игры...");
  level = 1;
  startGame();
}

void checkScoreLimit() {
  if (level == 1 && score <= -150) {
    Serial.println("\n=== Игра завершена! ===");
    Serial.println("Ваш счёт упал ниже -150 очков.");
    Serial.println("К сожалению, вы проиграли!");
    Serial.println("Хотите сыграть ещё раз? Нажмите *.");
    gameOver = true;
  } else if (level > 1 && score < 0) {
    Serial.println("\n=== Игра завершена! ===");
    Serial.println("Ваш счёт стал отрицательным.");
    Serial.println("К сожалению, вы проиграли!");
    Serial.println("Хотите сыграть ещё раз? Нажмите *.");
    gameOver = true;
  }
}