// ============================================================================
// LCD1602 (I2C) + интерфейсный энкодер (CLK/DT/SW) + 2 датчика отражения (A0/A1)
// Состояния:
//  MENU         — переход в MODE1_SELECT по короткому нажатию
//  MODE1_SELECT — выбор датчика S1/S2 поворотом, вход в измерение по короткому
//  MODE1_MEASURE— показ CUR/MIN/MAX, сброс MIN/MAX по короткому
// Управление:
//  SW short: подтверждение / сброс MIN/MAX
//  SW long : назад (в меню или на этап выбора датчика)
// ============================================================================

enum UiState { MENU = 0, MODE1_SELECT = 1, MODE1_MEASURE = 2 };

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

#define SERIAL_BAUD 115200

const unsigned long I2C_CLOCK_HZ = 100000UL;

const uint16_t BTN_DEBOUNCE_MS  = 35;
const uint16_t BTN_LONG_MS      = 700;

const uint16_t LCD_UPDATE_MS    = 150;
const uint16_t SERIAL_UPDATE_MS = 300;

// Интерфейсный энкодер
const uint8_t CLK = 11;  // также встречается: A / S1 / OUT1
const uint8_t DT  = 12;  // также встречается: B / S2 / OUT2
const uint8_t SW  = 13;  // также встречается: SW / KEY / BTN

// Датчики отражения (аналоговые входы)
const uint8_t REFLECT_SENS_1 = A0;
const uint8_t REFLECT_SENS_2 = A1;

// ============================================================================
// LCD I2C
// ============================================================================
LiquidCrystal_I2C* lcdPtr = nullptr;
uint8_t lcdAddr = 0;
bool lcdReady = false;

bool i2cPing(uint8_t addr) {
  Wire.beginTransmission(addr);
  return (Wire.endTransmission() == 0);
}

uint8_t findLcdAddress() {
  if (i2cPing(0x27)) return 0x27;
  if (i2cPing(0x3F)) return 0x3F;
  for (uint8_t a = 1; a < 127; a++) {
    if (a == 0x27 || a == 0x3F) continue;
    if (i2cPing(a)) return a;
  }
  return 0;
}

void lcdFree() {
  if (lcdPtr) {
    delete lcdPtr;
    lcdPtr = nullptr;
  }
  lcdReady = false;
}

void lcdInitOnce() {
  lcdFree();

  Wire.begin();
  Wire.setClock(I2C_CLOCK_HZ);

#if defined(WIRE_HAS_TIMEOUT)
  Wire.setWireTimeout(2000, true);
#endif

  lcdAddr = findLcdAddress();

  Serial.print("LCD address: ");
  if (lcdAddr == 0) {
    Serial.println("not found");
    return;
  }
  Serial.print("0x");
  if (lcdAddr < 16) Serial.print("0");
  Serial.println(lcdAddr, HEX);

  lcdPtr = new LiquidCrystal_I2C(lcdAddr, 16, 2);
  lcdPtr->init();
  lcdPtr->backlight();
  lcdPtr->clear();
  lcdReady = true;
}

void lcdPrintPadded(uint8_t row, const char* text) {
  if (!lcdReady) return;

  char buf[17];
  for (uint8_t i = 0; i < 16; i++) buf[i] = ' ';
  buf[16] = '\0';

  for (uint8_t i = 0; i < 16 && text[i] != '\0'; i++) buf[i] = text[i];

  lcdPtr->setCursor(0, row);
  lcdPtr->print(buf);
}

// ============================================================================
// Интерфейсный энкодер (CLK/DT)
// ============================================================================
int16_t uiDelta = 0;
uint8_t uiLast = 0;

void uiEncoderInit() {
  pinMode(CLK, INPUT_PULLUP);
  pinMode(DT,  INPUT_PULLUP);
  uiLast = (digitalRead(CLK) << 1) | digitalRead(DT);
}

void uiEncoderPoll() {
  uint8_t cur = (digitalRead(CLK) << 1) | digitalRead(DT);
  if (cur == uiLast) return;

  int8_t step = 0;
  if ((uiLast == 0b00 && cur == 0b10) ||
      (uiLast == 0b10 && cur == 0b11) ||
      (uiLast == 0b11 && cur == 0b01) ||
      (uiLast == 0b01 && cur == 0b00)) step = +1;
  else if ((uiLast == 0b00 && cur == 0b01) ||
           (uiLast == 0b01 && cur == 0b11) ||
           (uiLast == 0b11 && cur == 0b10) ||
           (uiLast == 0b10 && cur == 0b00)) step = -1;

  uiLast = cur;
  if (step != 0) uiDelta += step;
}

// ============================================================================
// Кнопка SW (короткое/долгое нажатие)
// ============================================================================
struct BtnState {
  bool lastStable = true;
  bool lastRead   = true;
  unsigned long lastChangeMs = 0;
  unsigned long pressStartMs = 0;
  bool longFired = false;
};

BtnState btn;
bool evShort = false;
bool evLong  = false;

void btnInit() {
  pinMode(SW, INPUT_PULLUP);
  btn.lastStable = digitalRead(SW);
  btn.lastRead = btn.lastStable;
  btn.lastChangeMs = millis();
}

void btnUpdate() {
  evShort = false;
  evLong  = false;

  bool r = digitalRead(SW);
  unsigned long now = millis();

  if (r != btn.lastRead) {
    btn.lastRead = r;
    btn.lastChangeMs = now;
  }

  if ((now - btn.lastChangeMs) >= BTN_DEBOUNCE_MS && btn.lastStable != btn.lastRead) {
    btn.lastStable = btn.lastRead;

    if (btn.lastStable == false) {
      btn.pressStartMs = now;
      btn.longFired = false;
    } else {
      unsigned long held = now - btn.pressStartMs;
      if (!btn.longFired && held >= 30 && held < BTN_LONG_MS) evShort = true;
    }
  }

  if (btn.lastStable == false && !btn.longFired) {
    if ((now - btn.pressStartMs) >= BTN_LONG_MS) {
      btn.longFired = true;
      evLong = true;
    }
  }
}

// ============================================================================
// Измерение датчика
// ============================================================================
uint8_t selectedSensor = 0;  // 0 = S1(A0), 1 = S2(A1)

int curVal = 0;
int minVal = 1023;
int maxVal = 0;

void measReset() {
  minVal = 1023;
  maxVal = 0;
}

int readSelectedSensor() {
  return (selectedSensor == 0) ? analogRead(REFLECT_SENS_1) : analogRead(REFLECT_SENS_2);
}

void measUpdate() {
  curVal = readSelectedSensor();
  if (curVal < minVal) minVal = curVal;
  if (curVal > maxVal) maxVal = curVal;
}

// ============================================================================
// Экранные формы
// ============================================================================
void drawMenu() {
  lcdPrintPadded(0, "Select mode:");
  lcdPrintPadded(1, ">MODE 1");
}

void drawMode1Select() {
  lcdPrintPadded(0, "MODE 1: sensor");
  if (selectedSensor == 0) lcdPrintPadded(1, ">S1(A0)  S2(A1)");
  else                    lcdPrintPadded(1, " S1(A0) >S2(A1)");
}

void drawMode1Measure() {
  char l0[17], l1[17];
  if (selectedSensor == 0) snprintf(l0, sizeof(l0), "S1 CUR:%4d", curVal);
  else                     snprintf(l0, sizeof(l0), "S2 CUR:%4d", curVal);
  snprintf(l1, sizeof(l1), "MN:%4d MX:%4d", minVal, maxVal);

  lcdPrintPadded(0, l0);
  lcdPrintPadded(1, l1);
}

// ============================================================================
// Serial
// ============================================================================
unsigned long lastSerMs = 0;

void serialTick(UiState st) {
  unsigned long now = millis();
  if (now - lastSerMs < SERIAL_UPDATE_MS) return;
  lastSerMs = now;

  if (st == MENU) {
    Serial.println("MENU mode=MODE1");
    return;
  }

  if (st == MODE1_SELECT) {
    Serial.print("MODE1_SELECT sensor=");
    Serial.println((selectedSensor == 0) ? "S1(A0)" : "S2(A1)");
    return;
  }

  Serial.print((selectedSensor == 0) ? "S1 " : "S2 ");
  Serial.print("cur="); Serial.print(curVal);
  Serial.print(" min="); Serial.print(minVal);
  Serial.print(" max="); Serial.println(maxVal);
}

// ============================================================================
// Главный цикл
// ============================================================================
UiState state = MENU;
unsigned long lastLcdMs = 0;

void setup() {
  Serial.begin(SERIAL_BAUD);
  Serial.println("START");

  pinMode(REFLECT_SENS_1, INPUT);
  pinMode(REFLECT_SENS_2, INPUT);

  uiEncoderInit();
  btnInit();

  lcdInitOnce();
  if (lcdReady) drawMenu();
}

void loop() {
  uiEncoderPoll();
  btnUpdate();

  int16_t d = uiDelta;
  if (d != 0) uiDelta = 0;

  if (state == MENU) {
    if (evShort) {
      state = MODE1_SELECT;
      lastLcdMs = 0;
    }

    unsigned long now = millis();
    if (lcdReady && (now - lastLcdMs >= LCD_UPDATE_MS)) {
      lastLcdMs = now;
      drawMenu();
    }

    serialTick(MENU);
    return;
  }

  if (state == MODE1_SELECT) {
    if (d != 0) {
      if (d > 0) selectedSensor = (selectedSensor + 1) % 2;
      else       selectedSensor = (selectedSensor + 1 + 1) % 2;
      lastLcdMs = 0;
    }

    if (evShort) {
      measReset();
      state = MODE1_MEASURE;
      lastLcdMs = 0;
    }

    if (evLong) {
      state = MENU;
      lastLcdMs = 0;
      return;
    }

    unsigned long now = millis();
    if (lcdReady && (now - lastLcdMs >= LCD_UPDATE_MS)) {
      lastLcdMs = now;
      drawMode1Select();
    }

    serialTick(MODE1_SELECT);
    return;
  }

  if (state == MODE1_MEASURE) {
    if (evShort) measReset();

    if (evLong) {
      state = MODE1_SELECT;
      lastLcdMs = 0;
      return;
    }

    measUpdate();

    unsigned long now = millis();
    if (lcdReady && (now - lastLcdMs >= LCD_UPDATE_MS)) {
      lastLcdMs = now;
      drawMode1Measure();
    }

    serialTick(MODE1_MEASURE);
    return;
  }
}
