#include <Arduino.h>
#include "variant.h"
#include "TDeckBoard.h"
#include <Mesh.h>  // For MESH_DEBUG_PRINTLN

uint32_t deviceOnline = 0x00;

void TDeckBoard::begin() {
  
  MESH_DEBUG_PRINTLN("TDeckBoard::begin() - starting");
  
  // Enable peripheral power (keyboard, sensors, etc.) FIRST
  // This powers the BQ27220 fuel gauge and other I2C devices
  pinMode(PIN_PERF_POWERON, OUTPUT);
  digitalWrite(PIN_PERF_POWERON, HIGH);
  delay(50);  // Allow peripherals to power up before I2C init
  MESH_DEBUG_PRINTLN("TDeckBoard::begin() - peripheral power enabled");

  // Initialize I2C with correct pins for T-Deck Pro
  Wire.begin(I2C_SDA, I2C_SCL);
  Wire.setClock(100000);  // 100kHz for reliable fuel gauge communication
  MESH_DEBUG_PRINTLN("TDeckBoard::begin() - I2C initialized");
  
  // Now call parent class begin (after power and I2C are ready)
  ESP32Board::begin();

  // Enable LoRa module power
  #ifdef P_LORA_EN
    pinMode(P_LORA_EN, OUTPUT);
    digitalWrite(P_LORA_EN, HIGH);
    delay(10);  // Allow module to power up
    MESH_DEBUG_PRINTLN("TDeckBoard::begin() - LoRa power enabled");
  #endif

  // Enable GPS module power and initialize Serial2
  #if HAS_GPS
    #ifdef PIN_GPS_EN
      pinMode(PIN_GPS_EN, OUTPUT);
      digitalWrite(PIN_GPS_EN, GPS_EN_ACTIVE);  // GPS_EN_ACTIVE is 1 (HIGH)
      delay(100);  // Allow GPS to power up
      MESH_DEBUG_PRINTLN("TDeckBoard::begin() - GPS power enabled");
    #endif
    
    // Initialize Serial2 for GPS with correct pins
    Serial2.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
    MESH_DEBUG_PRINTLN("TDeckBoard::begin() - GPS Serial2 initialized at %d baud", GPS_BAUDRATE);
  #endif

  // Disable 4G modem power (only present on 4G version, not audio version)
  // This turns off the red status LED on the modem module
  #ifdef MODEM_POWER_EN
    pinMode(MODEM_POWER_EN, OUTPUT);
    digitalWrite(MODEM_POWER_EN, LOW);  // Cut power to modem
    MESH_DEBUG_PRINTLN("TDeckBoard::begin() - 4G modem power disabled");
  #endif

  // Configure user button
  pinMode(PIN_USER_BTN, INPUT);

  // Configure LoRa SPI pins
  pinMode(P_LORA_MISO, INPUT_PULLUP);

  // Handle wake from deep sleep
  esp_reset_reason_t reason = esp_reset_reason();
  if (reason == ESP_RST_DEEPSLEEP) {
    uint64_t wakeup_source = esp_sleep_get_ext1_wakeup_status();
    if (wakeup_source & (1ULL << P_LORA_DIO_1)) {
      startup_reason = BD_STARTUP_RX_PACKET;  // Received a LoRa packet while in deep sleep
    }

    rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
    rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
  }
  
  // Test BQ27220 communication and configure design capacity
  #if HAS_BQ27220
    uint16_t voltage = getBattMilliVolts();
    MESH_DEBUG_PRINTLN("TDeckBoard::begin() - Battery voltage: %d mV", voltage);
    configureFuelGauge();
  #endif

  // --- Early low-voltage protection ---
  // If we boot below the shutdown threshold, go straight to deep sleep
  // WITHOUT touching the filesystem. This breaks the brown-out reboot
  // loop that corrupts contacts when battery is deeply depleted (~2.5V).
  #if HAS_BQ27220 && defined(AUTO_SHUTDOWN_MILLIVOLTS)
  {
    uint16_t bootMv = getBattMilliVolts();
    if (bootMv > 0 && bootMv < AUTO_SHUTDOWN_MILLIVOLTS) {
      Serial.printf("CRITICAL: Boot voltage %dmV < %dmV — sleeping immediately\n",
                    bootMv, AUTO_SHUTDOWN_MILLIVOLTS);
      // Don't mount SD, don't load contacts, don't pass Go.
      // Only wake on user button press (presumably after plugging in charger).
      esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
      esp_sleep_enable_ext1_wakeup(1ULL << PIN_USER_BTN, ESP_EXT1_WAKEUP_ANY_HIGH);
      esp_deep_sleep_start();  // CPU halts here
    }
  }
  #endif
  
  MESH_DEBUG_PRINTLN("TDeckBoard::begin() - complete");
}

uint16_t TDeckBoard::getBattMilliVolts() {
  #if HAS_BQ27220
    Wire.beginTransmission(BQ27220_I2C_ADDR);
    Wire.write(BQ27220_REG_VOLTAGE);
    if (Wire.endTransmission(false) != 0) {
      MESH_DEBUG_PRINTLN("BQ27220: I2C error reading voltage");
      return 0;
    }
    
    uint8_t count = Wire.requestFrom((uint8_t)BQ27220_I2C_ADDR, (uint8_t)2);
    if (count != 2) {
      MESH_DEBUG_PRINTLN("BQ27220: Read error - wrong byte count");
      return 0;
    }
    
    uint16_t voltage = Wire.read();
    voltage |= (Wire.read() << 8);
    return voltage;
  #else
    return 0;
  #endif
}

uint8_t TDeckBoard::getBatteryPercent() {
  #if HAS_BQ27220
    Wire.beginTransmission(BQ27220_I2C_ADDR);
    Wire.write(BQ27220_REG_SOC);
    if (Wire.endTransmission(false) != 0) {
      return 0;
    }
    
    uint8_t count = Wire.requestFrom((uint8_t)BQ27220_I2C_ADDR, (uint8_t)2);
    if (count != 2) {
      return 0;
    }
    
    uint16_t soc = Wire.read();
    soc |= (Wire.read() << 8);
    return (uint8_t)min(soc, (uint16_t)100);
  #else
    return 0;
  #endif
}

// ---- BQ27220 extended register helpers ----

#if HAS_BQ27220
// Read a 16-bit register from BQ27220. Returns 0 on I2C error.
static uint16_t bq27220_read16(uint8_t reg) {
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return 0;
  if (Wire.requestFrom((uint8_t)BQ27220_I2C_ADDR, (uint8_t)2) != 2) return 0;
  uint16_t val = Wire.read();
  val |= (Wire.read() << 8);
  return val;
}

// Read a single byte from BQ27220 register.
static uint8_t bq27220_read8(uint8_t reg) {
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return 0;
  if (Wire.requestFrom((uint8_t)BQ27220_I2C_ADDR, (uint8_t)1) != 1) return 0;
  return Wire.read();
}

// Write a 16-bit subcommand to BQ27220 Control register (0x00).
// Subcommands control unsealing, config mode, sealing, etc.
static bool bq27220_writeControl(uint16_t subcmd) {
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(0x00);                    // Control register
  Wire.write(subcmd & 0xFF);           // LSB first
  Wire.write((subcmd >> 8) & 0xFF);    // MSB
  return Wire.endTransmission() == 0;
}
#endif

// ---- BQ27220 Design Capacity configuration ----
// The BQ27220 ships with a 3000 mAh default. The T-Deck Pro uses a 2000 mAh
// cell. This function checks on boot and writes the correct value via the
// MAC Data Memory interface if needed. The value persists in battery-backed
// RAM, so this typically only writes once (or after a full battery disconnect).
//
// Procedure follows TI TRM SLUUBD4A Section 6.1:
//   1. Unseal  â†’  2. Full Access  â†’  3. Enter CFG_UPDATE
//   4. Write Design Capacity via MAC  â†’  5. Exit CFG_UPDATE  â†’  6. Seal

bool TDeckBoard::configureFuelGauge(uint16_t designCapacity_mAh) {
#if HAS_BQ27220
  // Read current design capacity from standard command register
  uint16_t currentDC = bq27220_read16(BQ27220_REG_DESIGN_CAP);
  Serial.printf("BQ27220: Design Capacity = %d mAh (target %d)\n", currentDC, designCapacity_mAh);

  if (currentDC == designCapacity_mAh) {
    // Design Capacity correct, but check if Full Charge Capacity is sane.
    uint16_t fcc = bq27220_read16(BQ27220_REG_FULL_CAP);
    Serial.printf("BQ27220: Design Capacity already correct, FCC=%d mAh\n", fcc);
    if (fcc >= designCapacity_mAh * 3 / 2) {
      // FCC is >=150% of design — stale from factory defaults (typically 3000 mAh).
      uint16_t designEnergy = (uint16_t)((uint32_t)designCapacity_mAh * 37 / 10);
      Serial.printf("BQ27220: FCC %d >> DC %d, checking Design Energy (target %d mWh)\n",
                    fcc, designCapacity_mAh, designEnergy);

      // Unseal to read data memory and issue RESET
      bq27220_writeControl(0x0414); delay(2);
      bq27220_writeControl(0x3672); delay(2);
      // Full Access
      bq27220_writeControl(0xFFFF); delay(2);
      bq27220_writeControl(0xFFFF); delay(2);

      // Read current Design Energy from data memory to check if it needs writing
      // Enter CFG_UPDATE to access data memory
      bq27220_writeControl(0x0090);
      bool ready = false;
      for (int i = 0; i < 50; i++) {
        delay(20);
        uint16_t opSt = bq27220_read16(BQ27220_REG_OP_STATUS);
        if (opSt & 0x0400) { ready = true; break; }
      }
      if (ready) {
        // Read Design Energy at data memory address 0x92A1
        Wire.beginTransmission(BQ27220_I2C_ADDR);
        Wire.write(0x3E); Wire.write(0xA1); Wire.write(0x92);
        Wire.endTransmission();
        delay(10);
        uint8_t oldMSB = bq27220_read8(0x40);
        uint8_t oldLSB = bq27220_read8(0x41);
        uint16_t currentDE = (oldMSB << 8) | oldLSB;

        if (currentDE != designEnergy) {
          // Design Energy actually needs updating — write it
          uint8_t oldChk = bq27220_read8(0x60);
          uint8_t dLen   = bq27220_read8(0x61);
          uint8_t newMSB = (designEnergy >> 8) & 0xFF;
          uint8_t newLSB = designEnergy & 0xFF;
          uint8_t temp = (255 - oldChk - oldMSB - oldLSB);
          uint8_t newChk = 255 - ((temp + newMSB + newLSB) & 0xFF);

          Serial.printf("BQ27220: DE old=%d new=%d mWh, writing\n", currentDE, designEnergy);

          Wire.beginTransmission(BQ27220_I2C_ADDR);
          Wire.write(0x3E); Wire.write(0xA1); Wire.write(0x92);
          Wire.write(newMSB); Wire.write(newLSB);
          Wire.endTransmission();
          delay(5);
          Wire.beginTransmission(BQ27220_I2C_ADDR);
          Wire.write(0x60); Wire.write(newChk); Wire.write(dLen);
          Wire.endTransmission();
          delay(10);

          // Exit with reinit since we actually changed data
          bq27220_writeControl(0x0091);  // EXIT_CFG_UPDATE_REINIT
          delay(200);
          Serial.println("BQ27220: Design Energy written, exited CFG_UPDATE");
        } else {
          // DC=2000, DE=7400, Update Status=0x00, but FCC is stuck at 3000.
          // Diagnostic scan found the culprits:
          //   0x9106 = Qmax Cell 0 (IT Cfg class) — the raw capacity the
          //            gauge uses for FCC calculation. Factory default 3000.
          //   0x929D = Stored FCC reference (Gas Gauging class, 2 bytes
          //            before Design Capacity). Also stuck at 3000.
          //
          // Fix: overwrite both with designCapacity_mAh (2000).
          Serial.printf("BQ27220: DE correct (%d mWh) — fixing Qmax + stored FCC\n", currentDE);

          // --- Helper lambda for MAC data memory 2-byte write ---
          // Reads old value + checksum, computes differential checksum, writes new value.
          auto writeDM16 = [](uint16_t addr, uint16_t newVal) -> bool {
            // Select address
            Wire.beginTransmission(BQ27220_I2C_ADDR);
            Wire.write(0x3E);
            Wire.write(addr & 0xFF);
            Wire.write((addr >> 8) & 0xFF);
            Wire.endTransmission();
            delay(10);

            uint8_t oldMSB = bq27220_read8(0x40);
            uint8_t oldLSB = bq27220_read8(0x41);
            uint8_t oldChk = bq27220_read8(0x60);
            uint8_t dLen   = bq27220_read8(0x61);
            uint16_t oldVal = (oldMSB << 8) | oldLSB;

            if (oldVal == newVal) {
              Serial.printf("BQ27220:   [0x%04X] already %d, skip\n", addr, newVal);
              return true;  // already correct
            }

            uint8_t newMSB = (newVal >> 8) & 0xFF;
            uint8_t newLSB = newVal & 0xFF;
            uint8_t temp = (255 - oldChk - oldMSB - oldLSB);
            uint8_t newChk = 255 - ((temp + newMSB + newLSB) & 0xFF);

            Serial.printf("BQ27220:   [0x%04X] %d -> %d\n", addr, oldVal, newVal);

            // Write new value
            Wire.beginTransmission(BQ27220_I2C_ADDR);
            Wire.write(0x3E);
            Wire.write(addr & 0xFF);
            Wire.write((addr >> 8) & 0xFF);
            Wire.write(newMSB);
            Wire.write(newLSB);
            Wire.endTransmission();
            delay(5);

            // Write checksum
            Wire.beginTransmission(BQ27220_I2C_ADDR);
            Wire.write(0x60);
            Wire.write(newChk);
            Wire.write(dLen);
            Wire.endTransmission();
            delay(10);
            return true;
          };

          // Overwrite Qmax Cell 0 (IT Cfg) — this is what FCC is derived from
          writeDM16(0x9106, designCapacity_mAh);

          // Overwrite stored FCC reference (Gas Gauging, 2 bytes before DC)
          writeDM16(0x929D, designCapacity_mAh);

          // Exit with reinit to apply the new values
          bq27220_writeControl(0x0091);  // EXIT_CFG_UPDATE_REINIT
          delay(200);
          Serial.println("BQ27220: Qmax + stored FCC updated, exited CFG_UPDATE");
        }
      } else {
        Serial.println("BQ27220: Failed to enter CFG_UPDATE for DE check");
      }

      // Seal first, then issue RESET.
      // RESET forces the gauge to fully reinitialize its Impedance Track
      // algorithm and recalculate FCC from the current DC/DE values.
      // This is the actual fix when DC and DE are correct but FCC is stuck.
      bq27220_writeControl(0x0030);  // SEAL
      delay(5);
      Serial.println("BQ27220: Issuing RESET to force FCC recalculation...");
      bq27220_writeControl(0x0041);  // RESET
      delay(2000);  // Full reset needs generous settle time

      fcc = bq27220_read16(BQ27220_REG_FULL_CAP);
      Serial.printf("BQ27220: FCC after RESET: %d mAh (target <= %d)\n", fcc, designCapacity_mAh);

      if (fcc > designCapacity_mAh * 3 / 2) {
        // RESET didn't fix FCC — the gauge IT algorithm is stubbornly
        // retaining its learned value. This typically resolves after one
        // full charge/discharge cycle. Software clamp in
        // getFullChargeCapacity() ensures correct display regardless.
        Serial.printf("BQ27220: FCC still stale at %d — software clamp active\n", fcc);
      }
    }
    return true;
  }

  Serial.printf("BQ27220: Updating Design Capacity from %d to %d mAh\n", currentDC, designCapacity_mAh);

  // Step 1: Unseal (default unseal keys)
  bq27220_writeControl(0x0414);
  delay(2);
  bq27220_writeControl(0x3672);
  delay(2);

  // Step 2: Enter Full Access mode
  bq27220_writeControl(0xFFFF);
  delay(2);
  bq27220_writeControl(0xFFFF);
  delay(2);

  // Step 3: Enter CFG_UPDATE mode
  bq27220_writeControl(0x0090);

  // Wait for CFGUPMODE bit (bit 10) in OperationStatus register
  bool cfgReady = false;
  for (int i = 0; i < 50; i++) {
    delay(20);
    uint16_t opStatus = bq27220_read16(BQ27220_REG_OP_STATUS);
    Serial.printf("BQ27220: OperationStatus = 0x%04X (attempt %d)\n", opStatus, i);
    if (opStatus & 0x0400) {  // CFGUPMODE is bit 10
      cfgReady = true;
      break;
    }
  }
  if (!cfgReady) {
    Serial.println("BQ27220: ERROR - Timeout waiting for CFGUPDATE mode");
    bq27220_writeControl(0x0092);  // Try to exit cleanly
    bq27220_writeControl(0x0030);  // Re-seal
    return false;
  }
  Serial.println("BQ27220: Entered CFGUPDATE mode");

  // Step 4: Write Design Capacity via MAC Data Memory interface
  // Design Capacity mAh lives at data memory address 0x929F

  // 4a. Select the data memory block by writing address to 0x3E-0x3F
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(0x3E);   // MACDataControl register
  Wire.write(0x9F);   // Address low byte
  Wire.write(0x92);   // Address high byte
  Wire.endTransmission();
  delay(10);

  // 4b. Read old data (MSB, LSB) and checksum for differential update
  uint8_t oldMSB     = bq27220_read8(0x40);
  uint8_t oldLSB     = bq27220_read8(0x41);
  uint8_t oldChksum  = bq27220_read8(0x60);
  uint8_t dataLen    = bq27220_read8(0x61);

  Serial.printf("BQ27220: Old DC bytes=0x%02X 0x%02X chk=0x%02X len=%d\n",
                     oldMSB, oldLSB, oldChksum, dataLen);

  // 4c. Compute new values (BQ27220 stores big-endian in data memory)
  uint8_t newMSB = (designCapacity_mAh >> 8) & 0xFF;
  uint8_t newLSB = designCapacity_mAh & 0xFF;

  // Differential checksum: remove old bytes, add new bytes
  uint8_t temp = (255 - oldChksum - oldMSB - oldLSB);
  uint8_t newChksum = 255 - ((temp + newMSB + newLSB) & 0xFF);

  Serial.printf("BQ27220: New DC bytes=0x%02X 0x%02X chk=0x%02X\n",
                     newMSB, newLSB, newChksum);

  // 4d. Write address + new data as a single block transaction
  //     BQ27220 MAC requires: [0x3E] [addr_lo] [addr_hi] [data...]
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(0x3E);    // Start at MACDataControl
  Wire.write(0x9F);    // Address low byte
  Wire.write(0x92);    // Address high byte
  Wire.write(newMSB);  // Data byte 0 (at 0x40)
  Wire.write(newLSB);  // Data byte 1 (at 0x41)
  uint8_t writeResult = Wire.endTransmission();
  Serial.printf("BQ27220: Write block result = %d\n", writeResult);

  // 4e. Write updated checksum and length
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(0x60);
  Wire.write(newChksum);
  Wire.write(dataLen);
  writeResult = Wire.endTransmission();
  Serial.printf("BQ27220: Write checksum result = %d\n", writeResult);
  delay(10);

  // 4f. Verify the write took effect before exiting config mode
  //     Re-read the block to confirm
  Wire.beginTransmission(BQ27220_I2C_ADDR);
  Wire.write(0x3E);
  Wire.write(0x9F);
  Wire.write(0x92);
  Wire.endTransmission();
  delay(10);
  uint8_t verMSB = bq27220_read8(0x40);
  uint8_t verLSB = bq27220_read8(0x41);
  Serial.printf("BQ27220: Verify in CFGUPDATE: DC bytes=0x%02X 0x%02X (%d mAh)\n",
                     verMSB, verLSB, (verMSB << 8) | verLSB);

  // Step 4g: Also update Design Energy (address 0x92A1) while in CFG_UPDATE.
  // Design Energy = capacity × 3.7V (nominal LiPo voltage).
  // The gauge uses both DC and DE to compute Full Charge Capacity.
  {
    uint16_t designEnergy = (uint16_t)((uint32_t)designCapacity_mAh * 37 / 10);
    Wire.beginTransmission(BQ27220_I2C_ADDR);
    Wire.write(0x3E); Wire.write(0xA1); Wire.write(0x92);
    Wire.endTransmission();
    delay(10);
    uint8_t deOldMSB = bq27220_read8(0x40);
    uint8_t deOldLSB = bq27220_read8(0x41);
    uint8_t deOldChk = bq27220_read8(0x60);
    uint8_t deLen    = bq27220_read8(0x61);

    uint8_t deNewMSB = (designEnergy >> 8) & 0xFF;
    uint8_t deNewLSB = designEnergy & 0xFF;
    uint8_t deTemp = (255 - deOldChk - deOldMSB - deOldLSB);
    uint8_t deNewChk = 255 - ((deTemp + deNewMSB + deNewLSB) & 0xFF);

    Serial.printf("BQ27220: Design Energy: old=%d new=%d mWh\n",
                  (deOldMSB << 8) | deOldLSB, designEnergy);

    Wire.beginTransmission(BQ27220_I2C_ADDR);
    Wire.write(0x3E); Wire.write(0xA1); Wire.write(0x92);
    Wire.write(deNewMSB); Wire.write(deNewLSB);
    Wire.endTransmission();
    delay(5);
    Wire.beginTransmission(BQ27220_I2C_ADDR);
    Wire.write(0x60); Wire.write(deNewChk); Wire.write(deLen);
    Wire.endTransmission();
    delay(10);
  }

  // Step 5: Exit CFG_UPDATE (with reinit to apply changes immediately)
  bq27220_writeControl(0x0091);  // EXIT_CFG_UPDATE_REINIT
  Serial.println("BQ27220: Sent EXIT_CFG_UPDATE_REINIT, waiting...");
  delay(200);  // Allow gauge to reinitialize

  // Verify
  uint16_t verifyDC = bq27220_read16(BQ27220_REG_DESIGN_CAP);
  Serial.printf("BQ27220: Design Capacity now reads %d mAh (expected %d)\n",
                     verifyDC, designCapacity_mAh);

  uint16_t newFCC = bq27220_read16(BQ27220_REG_FULL_CAP);
  Serial.printf("BQ27220: Full Charge Capacity: %d mAh\n", newFCC);

  if (verifyDC == designCapacity_mAh) {
    Serial.println("BQ27220: Configuration SUCCESS");
  } else {
    Serial.println("BQ27220: Configuration FAILED");
  }

  // Step 6: Seal the device
  bq27220_writeControl(0x0030);
  delay(5);

  // Step 7: Force full gauge RESET to reinitialize FCC from new DC/DE.
  // Without this, the Impedance Track algorithm retains the old FCC
  // (often 3000 mAh from factory) until a full charge/discharge cycle.
  bq27220_writeControl(0x0041);  // RESET
  delay(1000);  // Gauge needs time to fully reinitialize

  // Re-verify after hard reset
  verifyDC = bq27220_read16(BQ27220_REG_DESIGN_CAP);
  newFCC = bq27220_read16(BQ27220_REG_FULL_CAP);
  Serial.printf("BQ27220: Post-RESET DC=%d FCC=%d mAh\n", verifyDC, newFCC);

  return verifyDC == designCapacity_mAh;
#else
  return false;
#endif
}

int16_t TDeckBoard::getAvgCurrent() {
  #if HAS_BQ27220
    return (int16_t)bq27220_read16(BQ27220_REG_AVG_CURRENT);
  #else
    return 0;
  #endif
}

int16_t TDeckBoard::getAvgPower() {
  #if HAS_BQ27220
    return (int16_t)bq27220_read16(BQ27220_REG_AVG_POWER);
  #else
    return 0;
  #endif
}

uint16_t TDeckBoard::getTimeToEmpty() {
  #if HAS_BQ27220
    return bq27220_read16(BQ27220_REG_TIME_TO_EMPTY);
  #else
    return 0xFFFF;
  #endif
}

uint16_t TDeckBoard::getRemainingCapacity() {
  #if HAS_BQ27220
    return bq27220_read16(BQ27220_REG_REMAIN_CAP);
  #else
    return 0;
  #endif
}

uint16_t TDeckBoard::getFullChargeCapacity() {
  #if HAS_BQ27220
    uint16_t fcc = bq27220_read16(BQ27220_REG_FULL_CAP);
    // Clamp to design capacity — the gauge may report a stale factory FCC
    // (e.g. 3000 mAh) until it completes a full learning cycle. Never let
    // the reported FCC exceed what the actual cell can hold.
    if (fcc > BQ27220_DESIGN_CAPACITY_MAH) fcc = BQ27220_DESIGN_CAPACITY_MAH;
    return fcc;
  #else
    return 0;
  #endif
}

uint16_t TDeckBoard::getDesignCapacity() {
  #if HAS_BQ27220
    return bq27220_read16(BQ27220_REG_DESIGN_CAP);
  #else
    return 0;
  #endif
}

int16_t TDeckBoard::getBattTemperature() {
  #if HAS_BQ27220
    uint16_t raw = bq27220_read16(BQ27220_REG_TEMPERATURE);
    // BQ27220 returns 0.1°K, convert to 0.1°C (273.1K = 0°C)
    return (int16_t)(raw - 2731);
  #else
    return 0;
  #endif
}