#include <LiquidCrystal_I2C.h>
#include <Keypad.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

const int BUTTON_ROWS = 3;
const int BUTTON_COLS = 3;

const byte ROWS = 4;
const byte COLS = 4;

char hexaKeys[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 customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

const int PLAYER_NONE = 0;
const int PLAYER_1 = 1;
const int PLAYER_2 = 2;

int board[BUTTON_ROWS][BUTTON_COLS];
int currentPlayer;
bool gameOver;

int P1_points = 0;
int P2_points = 0;

void setup() {
  lcd.begin(20, 4);
  lcd.backlight();
  lcd.print("Welcome To Tic-Tac-Toe");
  delay(2000);
  lcd.clear();

  currentPlayer = PLAYER_1;
  gameOver = false;
  resetBoard();
  updateDisplay();
}

void loop() {
  char keypress = customKeypad.getKey();

  if (keypress == 'D') { // Reset scores with 'D'
    P1_points = 0;
    P2_points = 0;
    updateDisplay();
  }

  if (keypress == '0') {
    setup();
  }

  if (!gameOver) {
    if (keypress >= '1' && keypress <= '9') {
      int number = keypress - '1';

      int i = number / 3;
      int j = number % 3;

      if (board[i][j] == PLAYER_NONE) {
        board[i][j] = currentPlayer;
        updateDisplay();
        int result = checkWinCondition();
        if (result == PLAYER_1) {
          P1_points++;
          gameOver = true;
          lcd.setCursor(0, 4);
          lcd.print("Player 1 wins!");
        } else if (result == PLAYER_2) {
          P2_points++;
          gameOver = true;
          lcd.setCursor(0, 4);
          lcd.print("Player 2 wins!");
        } else if (result == -1) {
          gameOver = true;
          lcd.setCursor(0, 4);
          lcd.print("It's a draw!");
        }
        switchPlayers();
      }
      delay(200); // Button Rebound
    }
  }
}

void resetBoard() {
  for (int i = 0; i < BUTTON_ROWS; i++) {
    for (int j = 0; j < BUTTON_COLS; j++) {
      board[i][j] = PLAYER_NONE;
    }
  }
}

void updateDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Player ");
  lcd.print(currentPlayer == PLAYER_1 ? "1" : "2");
  lcd.setCursor(0, 1);

  for (int i = 0; i < BUTTON_ROWS; i++) {
    lcd.setCursor(0, i + 1);
    for (int j = 0; j < BUTTON_COLS; j++) {
      lcd.print("|");
      lcd.print(symbolForPlayer(board[i][j]));
    }
    lcd.print("|");
  }

  lcd.setCursor(15, 1);
  lcd.print("Pts");
  lcd.setCursor(15, 2);
  lcd.print(P1_points);
  lcd.setCursor(15, 3);
  lcd.print(P2_points);
}

char symbolForPlayer(int player) {
  if (player == PLAYER_1) {
    return 'X';
  } else if (player == PLAYER_2) {
    return 'O';
  }
  return ' ';
}

void switchPlayers() {
  currentPlayer = (currentPlayer == PLAYER_1) ? PLAYER_2 : PLAYER_1;
}

int checkWinCondition() {
  for (int i = 0; i < BUTTON_ROWS; i++) {
    if (board[i][0] != PLAYER_NONE && board[i][0] == board[i][1] && board[i][1] == board[i][2]) {
      return board[i][0];
    }
  }

  for (int j = 0; j < BUTTON_COLS; j++) {
    if (board[0][j] != PLAYER_NONE && board[0][j] == board[1][j] && board[1][j] == board[2][j]) {
      return board[0][j];
    }
  }

  if (board[0][0] != PLAYER_NONE && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
    return board[0][0];
  }

  if (board[0][2] != PLAYER_NONE && board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
    return board[0][2];
  }

  for (int i = 0; i < BUTTON_ROWS; i++) {
    for (int j = 0; j < BUTTON_COLS; j++) {
      if (board[i][j] == PLAYER_NONE) {
        return 0; // Game is still ongoing
      }
    }
  }

  return -1; // It's a draw
}