#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <Keyboard.h>
#include <Mouse.h>
#include <avr/pgmspace.h>

// 1. ОБЪЯВЛЯЕМ ПРОТОТИПЫ ФУНКЦИЙ ДЛЯ КОМПИЛЯТОРА
void updateDisplay();
void fillEEPROMWithData();
void handleGamepadMode();
void handlePasswordModes();
void checkIncomingSerial();
void checkModeButton();
void sendPassword(int idx);
String readPassword(int idx);

// Инициализация дисплея
LiquidCrystal_I2C lcd(0x27, 16, 2);

const int SLOT_SIZE = 50;

// ПИНЫ
const int JOY_Y_PIN = A1;  
const int JOY_X_PIN = A2;  
const int JOY_SW_PIN = 9;  
const int BTN_A = 4; 
const int BTN_B = 5; 
const int BTN_X = 6; 
const int BTN_Y = 7; 
const int BTN_MODE = 8; 

enum DeviceMode { MODE_INFO, MODE_WRITE, MODE_GAMEPAD };
DeviceMode currentMode = MODE_INFO;
int currentSlot = 0;

// Переменные логики
bool joyYToggled = false;
bool lastModeBtnState = HIGH;
//bool lastA = HIGH, lastB = HIGH, lastX = HIGH, lastY = HIGH;

unsigned long joySwPressedTime = 0;
bool isSwPressed = false;



void setup() {
  Serial.begin(115200);
  Keyboard.begin();
  Mouse.begin();
  lcd.init(); lcd.backlight();
  
  pinMode(JOY_SW_PIN, INPUT_PULLUP);
  pinMode(BTN_A, INPUT_PULLUP);
  pinMode(BTN_B, INPUT_PULLUP);
  pinMode(BTN_X, INPUT_PULLUP);
  pinMode(BTN_Y, INPUT_PULLUP);
  pinMode(BTN_MODE, INPUT_PULLUP);
  
  // Раскомментируй строчку ниже ОДИН РАЗ, если нужно забить память тест-паролями
  // fillEEPROMWithData();
  
  updateDisplay();
}

void loop() {
  checkModeButton();
  
  if (currentMode == MODE_GAMEPAD) {
    handleGamepadMode();
  } else {
    handlePasswordModes();
  }
  
  checkIncomingSerial();
}

// 2. РЕАЛИЗАЦИЯ ФУНКЦИЙ

void checkModeButton() {
  bool state = digitalRead(BTN_MODE);
  if (state == LOW && lastModeBtnState == HIGH) {
    delay(50);
    if (digitalRead(BTN_MODE) == LOW) {
      currentMode = (DeviceMode)((currentMode + 1) % 3);
      updateDisplay();
      while(digitalRead(BTN_MODE) == LOW);
    }
  }
  lastModeBtnState = state;
}

void handlePasswordModes() {
  int yVal = analogRead(JOY_Y_PIN);
  bool swVal = digitalRead(JOY_SW_PIN);
  
  if (!joyYToggled) {
    if (yVal < 200) { currentSlot = (currentSlot == 19) ? 0 : currentSlot + 1; joyYToggled = true; updateDisplay(); }
    else if (yVal > 800) { currentSlot = (currentSlot == 0) ? 19 : currentSlot - 1; joyYToggled = true; updateDisplay(); }
  }
  if (yVal >= 400 && yVal <= 600) joyYToggled = false;

  if (swVal == LOW) {
    if (!isSwPressed) {
      joySwPressedTime = millis();
      isSwPressed = true;
    }
    if (isSwPressed && (millis() - joySwPressedTime > 2000)) {
      lcd.clear(); lcd.print("Dumping script..");
      //Keyboard.print(pythonScript);
      int len = strlen_P(pythonScript);
      for (int i = 0; i < len; i++) {
        char c = pgm_read_byte_near(pythonScript + i);
        Keyboard.print(c);

        delay(1);
      }
      isSwPressed = false;
      updateDisplay();
    }
  } else {
    if (isSwPressed) {
      if (millis() - joySwPressedTime > 50) {
        if (currentMode == MODE_WRITE) {
          sendPassword(currentSlot);
        }
      }
      isSwPressed = false;
    }
  }
 static bool lastA = HIGH, lastB = HIGH, lastX = HIGH, lastY = HIGH;

  if (digitalRead(BTN_A) == LOW && lastA == HIGH) { delay(50); if(digitalRead(BTN_A)==LOW) sendPassword(0); }
  if (digitalRead(BTN_B) == LOW && lastB == HIGH) { delay(50); if(digitalRead(BTN_B)==LOW) sendPassword(1); }
  if (digitalRead(BTN_X) == LOW && lastX == HIGH) { delay(50); if(digitalRead(BTN_X)==LOW) sendPassword(2); }
  if (digitalRead(BTN_Y) == LOW && lastY == HIGH) { delay(50); if(digitalRead(BTN_Y)==LOW) sendPassword(3); }
  
  lastA = digitalRead(BTN_A); lastB = digitalRead(BTN_B); 
  lastX = digitalRead(BTN_X); lastY = digitalRead(BTN_Y);
}

void handleGamepadMode() {
  int xVal = analogRead(JOY_X_PIN);
  int yVal = analogRead(JOY_Y_PIN);
  bool swVal = digitalRead(JOY_SW_PIN);

  // 1. ДВИЖЕНИЕ МЫШИ
  // Считаем отклонение от центра (0-1023, центр ~512)
  int deltaX = xVal - 512;
  int deltaY = yVal - 512;

  // Если вышли за пределы мертвой зоны в 100 единиц
  if (abs(deltaX) > 100 || abs(deltaY) > 100) {
    // Масштабируем скорость (делим на 50, как у тебя, или подбираем экспериментально)
    int mouseX = deltaX / 50;
    // Инвертируем Y, если джойстик при отклонении "вперед" должен двигать курсор "вверх"
    int mouseY = (deltaY / 50); 

    // Ограничиваем значения в безопасный диапазон [-128, 127]
    mouseX = constrain(mouseX, -127, 127);
    mouseY = constrain(mouseY, -127, 127);

    // Двигаем курсор!
    Mouse.move(mouseX, mouseY, 0);
  }

  // 2. КЛИК КНОПКИ ДЖОЙСТИКА (Левая Кнопка Мыши)
  static bool lastSwVal = HIGH;
  if (swVal == LOW && lastSwVal == HIGH) {
    delay(50); // Дебаунс
    if (digitalRead(JOY_SW_PIN) == LOW) {
      // Имитируем полноценный клик (нажал и тут же отпустил)
      Mouse.click(MOUSE_LEFT); 
      
      // Альтернатива, если нуженドラッグ (зажатие):
      // Mouse.press(MOUSE_LEFT); -> а при отпускании кнопки вызывать Mouse.release(MOUSE_LEFT);
    }
  }
  lastSwVal = swVal;

  // 3. КЛАВИАТУРНЫЙ БЛОК (остается без изменений)
   static bool lastA = HIGH, lastB = HIGH, lastX = HIGH, lastY = HIGH;
   
  if (digitalRead(BTN_A) == LOW && lastA == HIGH) { delay(50); if(digitalRead(BTN_A)==LOW) Serial.println("KEY: TAB"); Keyboard.write(KEY_TAB); }
  if (digitalRead(BTN_B) == LOW && lastB == HIGH) { delay(50); if(digitalRead(BTN_B)==LOW) Serial.println("KEY: SPACE"); Keyboard.write(' ');  }
  if (digitalRead(BTN_X) == LOW && lastX == HIGH) { delay(50); if(digitalRead(BTN_X)==LOW) Serial.println("KEY: ENTER"); Keyboard.write(KEY_RETURN); }
  if (digitalRead(BTN_Y) == LOW && lastY == HIGH) { delay(50); if(digitalRead(BTN_Y)==LOW) Serial.println("KEY: ESC"); Keyboard.write(KEY_ESC); }
  
  lastA = digitalRead(BTN_A); lastB = digitalRead(BTN_B); 
  lastX = digitalRead(BTN_X); lastY = digitalRead(BTN_Y);

  //if (digitalRead(BTN_A) == LOW) { Serial.println("KEY: TAB"); Keyboard.write(KEY_TAB); }
  //if (digitalRead(BTN_B) == LOW) { Serial.println("KEY: SPACE"); Keyboard.write(' '); }
  //if (digitalRead(BTN_X) == LOW) { Serial.println("KEY: ENTER"); Keyboard.write(KEY_RETURN); }
  //if (digitalRead(BTN_Y) == LOW) { Serial.println("KEY: ESC"); Keyboard.write(KEY_ESC); }

  delay(10); // Задержку лучше уменьшить со 100 до 10-20 мс, чтобы мышь двигалась плавно, без рывков
}

void sendPassword(int idx) {
  String pass = readPassword(idx);
  if (pass == "") pass = "EmptySlot" + String(idx + 1);
  
  Keyboard.print(pass); 
  
  if (currentMode == MODE_WRITE) {
    lcd.setCursor(0, 1); 
    lcd.print("Typed Slot: "); lcd.print(idx + 1); lcd.print("  ");
    delay(1200); 
    updateDisplay();
  }
}

String readPassword(int idx) {
  String p = "";
  for (int i = 0; i < SLOT_SIZE; i++) {
    char c = EEPROM.read(idx * SLOT_SIZE + i);
    if (c == '\0' || c == 255) break;
    p += c;
  }
  return p;
}

void checkIncomingSerial() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    if (input.startsWith("SET:")) {
      int firstColon = input.indexOf(':');
      int secondColon = input.indexOf(':', firstColon + 1);
      if (firstColon != -1 && secondColon != -1) {
        int slotIndex = input.substring(firstColon + 1, secondColon).toInt();
        String newPass = input.substring(secondColon + 1);
        if (slotIndex >= 0 && slotIndex <= 19 && newPass.length() < SLOT_SIZE) {
          int startAddress = slotIndex * SLOT_SIZE;
          int len = newPass.length();
          for (int i = 0; i < SLOT_SIZE - 1; i++) {
            if (i < len) EEPROM.update(startAddress + i, newPass[i]);
            else EEPROM.update(startAddress + i, '\0');
          }
          EEPROM.update(startAddress + SLOT_SIZE - 1, '\0');
          lcd.clear(); lcd.print("SAVED TO SLOT "); lcd.print(slotIndex + 1);
          delay(1000); updateDisplay();
        }
      }
    }
  }
}

void updateDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  
  switch(currentMode) {
    case MODE_INFO:
      lcd.print("MODE: [INFO]");
      lcd.setCursor(0, 1);
      {
        String pass = readPassword(currentSlot);
        if (pass == "") pass = "Empty " + String(currentSlot + 1);
        lcd.print(currentSlot + 1); lcd.print(": "); lcd.print(pass.substring(0, 12));
      }
      break;
      
    case MODE_WRITE:
      lcd.print("MODE: [WRITE]");
      lcd.setCursor(0, 1);
      lcd.print("Select Slot: "); lcd.print(currentSlot + 1);
      break;
      
    case MODE_GAMEPAD:
      lcd.print("MODE: [GAMEPAD]");
      lcd.setCursor(0, 1);
      lcd.print("Joy=Mouse 4B=HID");
      break;
  }
}

