/*
  SmartCharge - Меню (ESP32 + OLED + Encoder) [U8g2 + стабильный энкодер]
  - Драйвер OLED: USE_SH1106=1 для SH1106, 0 для SSD1306
  - I2C: SDA=21, SCL=22, адрес 0x3C (проверен сканером)
  - Энкодер: A=GPIO32, B=GPIO33, BTN=GPIO26 (все с INPUT_PULLUP)
  - Подключение энкодера: общий (COM) -> GND; A -> 32; B -> 33.
    Кнопка: один контакт -> GND, второй -> 26.
  - Рекомендация: к A и B поставить по 10нФ на землю для тихого вращения.
*/

#define USE_SH1106 1   // 1 = SH1106, 0 = SSD1306

#include <Wire.h>
#include <Preferences.h>
#include <U8g2lib.h>

// ------------ OLED ------------
#if USE_SH1106
  U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
#else
  U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
#endif
#define MENU_FONT u8g2_font_6x12_t_cyrillic

// ------------ ПИНЫ ЭНКОДЕРА ------------
const int ENC_A_PIN   = 32;
const int ENC_B_PIN   = 33;
const int ENC_BTN_PIN = 26;

// ------------ УСТОЙЧИВАЯ ОБРАБОТКА ЭНКОДЕРА ------------
// Алгоритм: принимаем только валидные переходы и считаем «полный шаг» = 4 тика.
// Плюс фильтр по времени, чтобы отсечь дребезг (микросекунды).
volatile int16_t encSteps = 0;     // целые клики (детенты)
static uint8_t lastAB = 0;
static int8_t accum = 0;
static uint32_t lastEdgeUs = 0;

const uint32_t ENC_DEBOUNCE_US = 500; // игнорируем переходы быстрее 0.5 мкс (защита от дребезга)
const int8_t trans[4][4] = {
/*from\to: 00  01  10  11 */
  /*00*/   {  0, +1, -1,  0 },
  /*01*/   { -1,  0,  0, +1 },
  /*10*/   { +1,  0,  0, -1 },
  /*11*/   {  0, -1, +1,  0 }
};

inline void encoderUpdate() {
  // читаем сразу и A, и B (быстро)
  uint8_t a = (uint8_t)digitalRead(ENC_A_PIN);
  uint8_t b = (uint8_t)digitalRead(ENC_B_PIN);
  uint8_t ab = (a << 1) | b; // 0..3

  if (ab == lastAB) return; // нет изменений

  uint32_t now = micros();
  if (now - lastEdgeUs < ENC_DEBOUNCE_US) { // слишком быстро -> дребезг
    lastAB = ab;
    return;
  }
  lastEdgeUs = now;

  int8_t d = trans[lastAB & 0x03][ab & 0x03];
  lastAB = ab;
  if (d == 0) return; // невалидный прыжок (дребезг)

  accum += d;
  // Полный шаг у обычного энкодера — 4 валидных перехода
  if (accum >= +4) { encSteps++; accum = 0; }
  else if (accum <= -4) { encSteps--; accum = 0; }
}

int16_t encoderReadAndClear() {
  noInterrupts();
  int16_t v = encSteps;
  encSteps = 0;
  interrupts();
  return v;
}

// ------------ КНОПКА (простой дебаунс) ------------
bool btnPressed = false;
bool btnShortClick = false;
bool btnLongReleased = false;
uint32_t btnDownAt = 0;
const uint32_t BTN_DEBOUNCE_MS = 15;
const uint32_t BTN_LONG_MS = 800;

void buttonUpdate() {
  static bool lastRaw = true; // INPUT_PULLUP -> покой = HIGH (true)
  static uint32_t lastChange = 0;

  bool raw = digitalRead(ENC_BTN_PIN); // HIGH=не нажата, LOW=нажата
  uint32_t now = millis();

  if (raw != lastRaw) { // изменение
    lastRaw = raw;
    lastChange = now;
  }

  // дебаунс: ждём стабильности
  if (now - lastChange < BTN_DEBOUNCE_MS) return;

  if (!raw && !btnPressed) { // нажали
    btnPressed = true;
    btnShortClick = false;
    btnLongReleased = false;
    btnDownAt = now;
  } else if (raw && btnPressed) { // отпустили
    btnPressed = false;
    if (now - btnDownAt >= BTN_LONG_MS) btnLongReleased = true;
    else btnShortClick = true;
  }
}

bool isShortClick(){ if (btnShortClick){ btnShortClick=false; return true;} return false; }
bool isLongPressReleased(){ if (btnLongReleased){ btnLongReleased=false; return true;} return false; }

// ------------ ПРОФИЛИ/МЕНЮ (как раньше) ------------
enum : uint8_t { CHEM_LIION=0, CHEM_LFP=1, CHEM_LTO=2 };
static inline const char* chemToStr(uint8_t c){
  switch(c){ case CHEM_LIION: return "Li-ion"; case CHEM_LFP: return "LiFePO4"; case CHEM_LTO: return "LTO"; }
  return "Li-ion";
}
struct Profile {
  uint8_t  chem;          // CHEM_*
  uint8_t  S;             // 1..4
  uint16_t capacity_mAh;  // 100..10000
  float    rateC;         // 0.1..1.0
  float    ItermA;        // A
};

Preferences prefs;
const int NUM_PROFILES = 5;
Profile profiles[NUM_PROFILES];
int lastProfileIndex = 0;

void loadProfiles() {
  prefs.begin("sc", true);
  for (int i=0;i<NUM_PROFILES;i++){
    String k = String("p")+i+"_";
    profiles[i].chem = prefs.getUChar((k+"chem").c_str(), i==0?CHEM_LIION:CHEM_LFP);
    profiles[i].S    = prefs.getUChar((k+"S").c_str(),    (i==1)?3:1);
    profiles[i].capacity_mAh = prefs.getUShort((k+"cap").c_str(), (i==0)?2500:1000);
    profiles[i].rateC = prefs.getFloat((k+"rate").c_str(), 0.5f);
    float itdef = (profiles[i].chem==CHEM_LIION?0.05f:0.02f) * profiles[i].capacity_mAh/1000.0f;
    profiles[i].ItermA= prefs.getFloat((k+"iterm").c_str(), itdef);
  }
  lastProfileIndex = prefs.getUChar("lastIdx", 0);
  prefs.end();
}
void saveProfile(int i) {
  prefs.begin("sc", false);
  String k = String("p")+i+"_";
  prefs.putUChar((k+"chem").c_str(), profiles[i].chem);
  prefs.putUChar((k+"S").c_str(),    profiles[i].S);
  prefs.putUShort((k+"cap").c_str(), profiles[i].capacity_mAh);
  prefs.putFloat((k+"rate").c_str(), profiles[i].rateC);
  prefs.putFloat((k+"iterm").c_str(),profiles[i].ItermA);
  prefs.end();
}
void saveLastIndex(int idx){ prefs.begin("sc", false); prefs.putUChar("lastIdx", idx); prefs.end(); }

// UI state
enum UiState {
  UI_HOME=0,
  UI_START_MODE, UI_START_PROFILE,
  UI_START_QUICK_CAP, UI_START_QUICK_RATEC, UI_START_QUICK_ITERM, UI_START_CONFIRM,
  UI_PROFILES_LIST, UI_PROFILE_EDIT_FIELD,
  UI_EDIT_CHEM, UI_EDIT_S, UI_EDIT_CAP, UI_EDIT_RATEC, UI_EDIT_ITERM,
  UI_SETTINGS, UI_SERVICE, UI_MESSAGE
};
UiState ui = UI_HOME;

int cursor=0, topIndex=0;

enum StartMode { SM_CHARGE=0, SM_DISCHARGE, SM_RINT };
StartMode startMode = SM_CHARGE;
int startProfileIdx = 0;
uint16_t quickCap_mAh = 2500; float quickRateC=0.5f, quickItermA=0.10f;

// --- helpers (U8g2)
void setFont(){ u8g2.setFont(MENU_FONT); }
void drawHeader(const char* t){
  setFont();
  u8g2.setDrawColor(1); u8g2.drawBox(0,0,128,12);
  u8g2.setDrawColor(0); u8g2.setCursor(2,10); u8g2.print(t);
  u8g2.setDrawColor(1);
}
void drawMenuItem(int y, const String& text, bool sel){
  setFont();
  if (sel){ u8g2.drawBox(0,y-9,128,11); u8g2.setDrawColor(0); u8g2.setCursor(2,y); u8g2.print(text); u8g2.setDrawColor(1); }
  else     { u8g2.setCursor(2,y); u8g2.print(text); }
}
void drawFooter(const char* s){ setFont(); u8g2.setCursor(0,62); u8g2.print(s); }
String fmtFloat(float v,int p){ char b[20]; dtostrf(v,0,p,b); return String(b); }

// --- rotary input wrapper
int readRot(int step=1){
  int16_t d = encoderReadAndClear();
  if (!d) return 0;
  // по одному пункту на «щелчок»
  return (d>0)? +step : -step;
}

// --- screens
void screenHome(){
  u8g2.clearBuffer(); drawHeader("Главное меню");
  const char* items[]={"Старт","Профили","Параметры","Сервис"}; int n=4;
  int r=readRot(); if(r){ cursor += (r>0?1:-1); if(cursor<0) cursor=n-1; if(cursor>=n) cursor=0; }
  for(int i=0;i<n;i++) drawMenuItem(12+(i+1)*11, items[i], i==cursor);
  drawFooter("Нажми: выбор  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){
    if(cursor==0){ ui=UI_START_MODE; cursor=0; }
    else if(cursor==1){ ui=UI_PROFILES_LIST; cursor=0; topIndex=0; }
    else if(cursor==2){ ui=UI_SETTINGS; }
    else { ui=UI_SERVICE; }
  }
}

void screenStartMode(){
  u8g2.clearBuffer(); drawHeader("Старт: режим");
  const char* items[]={"Заряд","Разряд (1S)","МиллиОм (1S)"}; int n=3;
  int r=readRot(); if(r){ cursor += (r>0?1:-1); if(cursor<0) cursor=n-1; if(cursor>=n) cursor=0; }
  for(int i=0;i<n;i++) drawMenuItem(12+(i+1)*11, items[i], i==cursor);
  drawFooter("Нажми: далее  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){ startMode=(StartMode)cursor; ui=UI_START_PROFILE; startProfileIdx=lastProfileIndex; }
  if(isLongPressReleased()){ ui=UI_HOME; cursor=0; }
}

void screenStartProfile(){
  u8g2.clearBuffer(); drawHeader("Старт: профиль");
  int n=NUM_PROFILES; int r=readRot(); if(r){ startProfileIdx += (r>0?1:-1); if(startProfileIdx<0) startProfileIdx=n-1; if(startProfileIdx>=n) startProfileIdx=0; }
  if(startProfileIdx<topIndex) topIndex=startProfileIdx;
  if(startProfileIdx>=topIndex+4) topIndex=startProfileIdx-3;

  for(int row=0; row<4; row++){
    int i=topIndex+row; if(i>=n) break;
    String line="["+String(i+1)+"] "; line+=chemToStr(profiles[i].chem);
    line+=" S="+String(profiles[i].S)+" "+String(profiles[i].capacity_mAh)+"mAh";
    drawMenuItem(12+(row+1)*11, line, i==startProfileIdx);
  }
  drawFooter("Нажми: правка  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){
    Profile &p=profiles[startProfileIdx];
    quickCap_mAh=p.capacity_mAh; quickRateC=p.rateC;
    quickItermA=(p.ItermA>0.001f)?p.ItermA:((p.chem==CHEM_LIION?0.05f:0.02f)*p.capacity_mAh/1000.0f);
    ui=UI_START_QUICK_CAP; saveLastIndex(startProfileIdx);
  }
  if(isLongPressReleased()){ ui=UI_START_MODE; cursor=0; }
}

void editNumberInt(uint16_t &val,uint16_t minV,uint16_t maxV,uint16_t step,const char* title){
  u8g2.clearBuffer(); drawHeader(title);
  int r=readRot(); if(r){ long nv=(long)val + (r>0?step:-step); if(nv<minV) nv=minV; if(nv>maxV) nv=maxV; val=(uint16_t)nv; }
  u8g2.setFont(MENU_FONT); u8g2.setCursor(2, 22+10); u8g2.print(val); u8g2.print(" ");
  drawFooter("Нажми: ОК  |  Держи: отмена");
  u8g2.sendBuffer();
}
void editNumberFloat(float &val,float minV,float maxV,float step,const char* title){
  u8g2.clearBuffer(); drawHeader(title);
  int r=readRot(); if(r){ float nv=val+(r>0?step:-step); if(nv<minV) nv=minV; if(nv>maxV) nv=maxV; val=nv; }
  u8g2.setFont(MENU_FONT); u8g2.setCursor(2, 22+10); char b[16]; dtostrf(val,0,(step<0.1f?2:1),b); u8g2.print(b);
  drawFooter("Нажми: ОК  |  Держи: отмена");
  u8g2.sendBuffer();
}

void screenStartQuickCap(){ editNumberInt(quickCap_mAh,100,10000,100,"Емкость (mAh)"); if(isShortClick()) ui=UI_START_QUICK_RATEC; if(isLongPressReleased()) ui=UI_START_PROFILE; }
void screenStartQuickRateC(){ editNumberFloat(quickRateC,0.1f,1.0f,0.1f,"C-rate"); if(isShortClick()) ui=UI_START_QUICK_ITERM; if(isLongPressReleased()) ui=UI_START_PROFILE; }
void screenStartQuickIterm(){ editNumberFloat(quickItermA,0.01f,5.0f,0.01f,"Iterm (A)"); if(isShortClick()) ui=UI_START_CONFIRM; if(isLongPressReleased()) ui=UI_START_PROFILE; }

void screenStartConfirm(){
  u8g2.clearBuffer(); drawHeader("Подтверждение");
  Profile &p=profiles[startProfileIdx];
  u8g2.setFont(MENU_FONT);
  u8g2.setCursor(2,22); u8g2.print("Режим: "); u8g2.print(startMode==SM_CHARGE?"Заряд":(startMode==SM_DISCHARGE?"Разряд":"МиллиОм"));
  u8g2.setCursor(2,33); u8g2.print("Профиль: "); u8g2.print(chemToStr(p.chem)); u8g2.print(" S="); u8g2.print(p.S);
  u8g2.setCursor(2,44); u8g2.print("Cap="); u8g2.print(quickCap_mAh); u8g2.print("mAh  C="); u8g2.print(quickRateC,1);
  u8g2.setCursor(2,55); u8g2.print("Iterm="); u8g2.print(quickItermA,2); u8g2.print("A");
  drawFooter("Нажми: ПУСК  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){ ui=UI_MESSAGE; }
  if(isLongPressReleased()){ ui=UI_START_PROFILE; }
}

void screenMessage(){
  u8g2.clearBuffer(); drawHeader("Сообщение");
  u8g2.setFont(MENU_FONT);
  u8g2.setCursor(2,26); u8g2.print("Старт (демо).");
  u8g2.setCursor(2,38); u8g2.print("Дальше: алгоритм/логи");
  drawFooter("Нажми: в меню");
  u8g2.sendBuffer();

  if(isShortClick() || isLongPressReleased()){ ui=UI_HOME; cursor=0; }
}

// Профили
enum EditField { EF_CHEM=0, EF_S, EF_CAP, EF_RATEC, EF_ITERM, EF_SAVE, EF_CANCEL };
int profListCursor=0, editProfileIdx=0; EditField editField=EF_CHEM;

void screenProfilesList(){
  u8g2.clearBuffer(); drawHeader("Профили");
  int n=NUM_PROFILES; int r=readRot(); if(r){ profListCursor += (r>0?1:-1); if(profListCursor<0) profListCursor=n-1; if(profListCursor>=n) profListCursor=0; }
  if(profListCursor<topIndex) topIndex=profListCursor; if(profListCursor>=topIndex+4) topIndex=profListCursor-3;

  for(int row=0; row<4; row++){
    int i=topIndex+row; if(i>=n) break;
    String line="["+String(i+1)+"] "; line+=chemToStr(profiles[i].chem);
    line+=" S="+String(profiles[i].S)+" "+String(profiles[i].capacity_mAh)+"mAh";
    drawMenuItem(12+(row+1)*11, line, i==profListCursor);
  }
  drawFooter("Нажми: редакт.  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){ editProfileIdx=profListCursor; editField=EF_CHEM; ui=UI_PROFILE_EDIT_FIELD; }
  if(isLongPressReleased()){ ui=UI_HOME; cursor=0; }
}

void screenProfileEditField(){
  u8g2.clearBuffer(); drawHeader("Редакт.профиля");
  const char* items[]={"Химия","S (банок)","Емкость (mAh)","C-rate","Iterm (A)","Сохранить","Отмена"}; int n=7;
  int r=readRot(); if(r){ int c=(int)editField + (r>0?1:-1); if(c<0) c=n-1; if(c>=n) c=0; editField=(EditField)c; }
  for(int i=0;i<n;i++) drawMenuItem(12+(i+1)*11, items[i], i==(int)editField);
  drawFooter("Нажми: выбрать  |  Держи: назад");
  u8g2.sendBuffer();

  if(isShortClick()){
    switch(editField){
      case EF_CHEM:  ui=UI_EDIT_CHEM;  break;
      case EF_S:     ui=UI_EDIT_S;     break;
      case EF_CAP:   ui=UI_EDIT_CAP;   break;
      case EF_RATEC: ui=UI_EDIT_RATEC; break;
      case EF_ITERM: ui=UI_EDIT_ITERM; break;
      case EF_SAVE:  saveProfile(editProfileIdx); ui=UI_PROFILES_LIST; break;
      case EF_CANCEL:ui=UI_PROFILES_LIST; break;
    }
  }
  if(isLongPressReleased()) ui=UI_PROFILES_LIST;
}

void screenEditChem(){
  u8g2.clearBuffer(); drawHeader("Химия");
  uint8_t &c=profiles[editProfileIdx].chem; int r=readRot(); if(r){ int v=(int)c+(r>0?1:-1); if(v<0) v=2; if(v>2) v=0; c=(uint8_t)v; }
  u8g2.setFont(MENU_FONT); u8g2.setCursor(2, 22+10); u8g2.print(chemToStr(c));
  drawFooter("Нажми: ОК  |  Держи: отмена");
  u8g2.sendBuffer();
  if(isShortClick()||isLongPressReleased()) ui=UI_PROFILE_EDIT_FIELD;
}

void screenEditS(){
  u8g2.clearBuffer(); drawHeader("S (1..4)");
  uint8_t &S=profiles[editProfileIdx].S; int r=readRot(); if(r){ int v=(int)S+(r>0?1:-1); if(v<1) v=4; if(v>4) v=1; S=(uint8_t)v; }
  u8g2.setFont(MENU_FONT); u8g2.setCursor(2,22+10); u8g2.print("S="); u8g2.print(S);
  drawFooter("Нажми: ОК  |  Держи: отмена");
  u8g2.sendBuffer();
  if(isShortClick()||isLongPressReleased()) ui=UI_PROFILE_EDIT_FIELD;
}

void editNumberInt(uint16_t &val,uint16_t minV,uint16_t maxV,uint16_t step,const char* title);
void editNumberFloat(float &val,float minV,float maxV,float step,const char* title);
void screenEditCap(){ editNumberInt(profiles[editProfileIdx].capacity_mAh,100,10000,100,"Емкость (mAh)"); if(isShortClick()||isLongPressReleased()) ui=UI_PROFILE_EDIT_FIELD; }
void screenEditRateC(){ editNumberFloat(profiles[editProfileIdx].rateC,0.1f,1.0f,0.1f,"C-rate"); if(isShortClick()||isLongPressReleased()) ui=UI_PROFILE_EDIT_FIELD; }
void screenEditIterm(){ editNumberFloat(profiles[editProfileIdx].ItermA,0.01f,5.0f,0.01f,"Iterm (A)"); if(isShortClick()||isLongPressReleased()) ui=UI_PROFILE_EDIT_FIELD; }

// Заглушки
void screenSettings(){
  u8g2.clearBuffer(); drawHeader("Параметры");
  u8g2.setFont(MENU_FONT);
  u8g2.setCursor(2,26); u8g2.print("Позже: лог/экран/сеть");
  drawFooter("Держи: назад");
  u8g2.sendBuffer();
  if(isShortClick()||isLongPressReleased()) ui=UI_HOME;
}
void screenService(){
  u8g2.clearBuffer(); drawHeader("Сервис");
  u8g2.setFont(MENU_FONT);
  u8g2.setCursor(2,26); u8g2.print("Позже: тесты/калибровка");
  drawFooter("Держи: назад");
  u8g2.sendBuffer();
  if(isShortClick()||isLongPressReleased()) ui=UI_HOME;
}

// ------------ SETUP/LOOP ------------
void setup(){
  Serial.begin(115200);
  Wire.begin(21,22);
  Wire.setClock(100000);

  u8g2.begin();
  u8g2.enableUTF8Print();
  u8g2.setI2CAddress(0x3C << 1);
  u8g2.setBusClock(100000);

  pinMode(ENC_A_PIN, INPUT_PULLUP);
  pinMode(ENC_B_PIN, INPUT_PULLUP);
  pinMode(ENC_BTN_PIN, INPUT_PULLUP);

  // Инициализируем lastAB текущим состояние контактов, чтобы избежать «скачка» при старте
  lastAB = ((uint8_t)digitalRead(ENC_A_PIN) << 1) | (uint8_t)digitalRead(ENC_B_PIN);

  loadProfiles();

  // Экран приветствия + подсказка по пинам
  u8g2.clearBuffer(); drawHeader("SmartCharge UI");
  u8g2.setFont(MENU_FONT);
  u8g2.setCursor(2,20); u8g2.print("Меню готово.");
  u8g2.setCursor(2,32); u8g2.print("Encoder A=32 B=33 BTN=26");
  u8g2.setCursor(2,44); u8g2.print("COM/BTN GND, pullups вкл.");
  u8g2.sendBuffer();
  delay(700);
}

void loop(){
  // Регулярно опрашиваем энкодер и кнопку
  encoderUpdate();
  buttonUpdate();

  switch(ui){
    case UI_HOME:               screenHome(); break;
    case UI_START_MODE:         screenStartMode(); break;
    case UI_START_PROFILE:      screenStartProfile(); break;
    case UI_START_QUICK_CAP:    screenStartQuickCap(); break;
    case UI_START_QUICK_RATEC:  screenStartQuickRateC(); break;
    case UI_START_QUICK_ITERM:  screenStartQuickIterm(); break;
    case UI_START_CONFIRM:      screenStartConfirm(); break;
    case UI_PROFILES_LIST:      screenProfilesList(); break;
    case UI_PROFILE_EDIT_FIELD: screenProfileEditField(); break;
    case UI_EDIT_CHEM:          screenEditChem(); break;
    case UI_EDIT_S:             screenEditS(); break;
    case UI_EDIT_CAP:           screenEditCap(); break;
    case UI_EDIT_RATEC:         screenEditRateC(); break;
    case UI_EDIT_ITERM:         screenEditIterm(); break;
    case UI_SETTINGS:           screenSettings(); break;
    case UI_SERVICE:            screenService(); break;
    case UI_MESSAGE:            screenMessage(); break;
  }

  // Без лишних задержек, чтобы не пропускать тики
  delay(1);
}
