#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// LCD setup
#define LCD_ADDR 0x27       // I2C address for the LCD
#define LCD_COLS 20         // Number of columns on the LCD
#define LCD_ROWS 4          // Number of rows on the LCD
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);

// Buzzer setup
#define BUZZER_PIN 23       // GPIO pin for the buzzer

#define UP_BUTTON_PIN 18     // GPIO pin for the UP button
#define DOWN_BUTTON_PIN 19   // GPIO pin for the DOWN button
#define SELECT_BUTTON_PIN 33 // GPIO pin for the SELECT button (dedicated)
// Keyboard setup (4x8 matrix)
#define ROWS 4
#define COLS 8
const int rowPins[ROWS] = {4, 16, 17, 27};     // Rows connected to GPIO
const int colPins[COLS] = {32, 33, 26, 13, 14, 12, 15, 19}; // Columns connected to GPIO
char keys[ROWS][COLS] = {
  {'1', '2', '3', '4', '5', '6', '7', '8'},
  {'9', '0', 'Q', 'W', 'E', 'R', 'T', 'Y'},
  {'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K'},
  {'Z', 'X', 'C', 'V', 'B', 'N', 'M', ' '}
};

// Menu variables
int currentMenuIndex = 0;
const char* menuItems[] = {
  "1. Message App",
  "2. Connection",
  "3. Settings"
};
const int menuLength = sizeof(menuItems) / sizeof(menuItems[0]);

// Messaging app variables
bool messagingAppActive = false;
char messageBuffer[6][20]; // 6 lines, each with 20 characters
int cursorPos = 0;         // Current position in the message buffer
int linePos = 0;           // Current line (0 to 5, since there are 6 lines)
bool shiftActive = false;

// Function prototypes
void buzz(int duration);
void displayMenu();
void handleMenuNavigation();
void handleKeyboardInput();
char readKey();
void processSelectedMenu(int index);
void displayMessagingApp();
void handleTyping(char key);
void scrollMessage();

void setup() {
  Serial.begin(115200);

  // Initialize LCD
  lcd.init();
  lcd.backlight();

  // Initialize buzzer
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);

  // Initialize navigation buttons
  pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
  pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
  pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);

  // Initialize keyboard pins
  for (int i = 0; i < ROWS; i++) pinMode(rowPins[i], OUTPUT);
  for (int i = 0; i < COLS; i++) pinMode(colPins[i], INPUT_PULLUP);

  // Clear the message buffer
  for (int i = 0; i < 6; i++) {
    for (int j = 0; j < 20; j++) {
      messageBuffer[i][j] = ' ';
    }
  }

  // Display the initial menu
  displayMenu();
}

void loop() {
  if (!messagingAppActive) {
    handleMenuNavigation();
    handleKeyboardInput();
  } else {
    handleKeyboardInput();
    displayMessagingApp();
  }
}

// Buzzer function
void buzz(int duration) {
  digitalWrite(BUZZER_PIN, HIGH);
  delay(duration);
  digitalWrite(BUZZER_PIN, LOW);
}

// Display the current menu with highlighting
void displayMenu() {
  lcd.clear();
  for (int i = 0; i < LCD_ROWS; i++) {
    int menuItemIndex = currentMenuIndex + i;
    if (menuItemIndex < menuLength) {
      lcd.setCursor(0, i);
      if (menuItemIndex == currentMenuIndex) {
        lcd.print("--> "); // Highlight the selected menu item
      } else {
        lcd.print("    "); // No highlight for other items
      }
      lcd.print(menuItems[menuItemIndex]);
    }
  }
}

// Handle menu navigation
void handleMenuNavigation() {
  if (digitalRead(UP_BUTTON_PIN) == LOW) { // UP button pressed
    if (currentMenuIndex > 0) {
      currentMenuIndex--;
      displayMenu();
      buzz(100);
      delay(200); // Debounce delay
    }
  }

  if (digitalRead(DOWN_BUTTON_PIN) == LOW) { // DOWN button pressed
    if (currentMenuIndex < menuLength - 1) {
      currentMenuIndex++;
      displayMenu();
      buzz(100);
      delay(200); // Debounce delay
    }
  }

  if (digitalRead(SELECT_BUTTON_PIN) == LOW) { // SELECT button pressed
    processSelectedMenu(currentMenuIndex);
    buzz(200);
    delay(200); // Debounce delay
  }
}

// Handle keyboard input
void handleKeyboardInput() {
  char key = readKey();
  if (messagingAppActive && key != '\0') {
    handleTyping(key);
  }
}

// Read key from the keyboard
char readKey() {
  for (int row = 0; row < ROWS; row++) {
    digitalWrite(rowPins[row], LOW); // Activate the current row
    for (int col = 0; col < COLS; col++) {
      if (digitalRead(colPins[col]) == LOW) { // Check if the key is pressed
        delay(50); // Debounce
        while (digitalRead(colPins[col]) == LOW); // Wait until the key is released
        digitalWrite(rowPins[row], HIGH); // Deactivate the row
        return keys[row][col];
      }
    }
    digitalWrite(rowPins[row], HIGH); // Deactivate the row
  }
  return '\0'; // Return null if no key is pressed
}

// Process the selected menu item
void processSelectedMenu(int index) {
  if (index == 0) {
    messagingAppActive = true;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Type your message:");
    lcd.setCursor(0, 1);
    cursorPos = 0;
    linePos = 0;
  }
}

// Handle typing in the messaging app
void handleTyping(char key) {
  if (key == ' ') { // Space key
    key = ' ';
  } else if (key == 'X') { // Backspace key
    if (cursorPos > 0) {
      cursorPos--;
      messageBuffer[linePos][cursorPos] = ' ';
    }
  } else {
    if (shiftActive) key = toupper(key); // Apply shift for uppercase
    if (cursorPos < 20) {
      messageBuffer[linePos][cursorPos++] = key;
    }
  }

  // Check if cursor has moved to the next line
  if (cursorPos >= LCD_COLS) {
    cursorPos = 0;
    if (linePos < 5) {
      linePos++;
    } else {
      scrollMessage();
    }
  }

  // Update the display
  displayMessagingApp();
}

// Scroll the message up to make space for new lines
void scrollMessage() {
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 20; j++) {
      messageBuffer[i][j] = messageBuffer[i + 1][j];
    }
  }
  // Clear the last line
  for (int j = 0; j < 20; j++) {
    messageBuffer[5][j] = ' ';
  }
}

// Display the message on the LCD
void displayMessagingApp() {
  lcd.clear();
  for (int i = 0; i < 6; i++) {
    lcd.setCursor(0, i);
    lcd.print(messageBuffer[i]);
  }
}