#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <ModbusMaster.h>
#include <HardwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "time.h"

// ========== OLED ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ========== WIFI ==========
#define WIFI_SSID     "AGIL"
#define WIFI_PASSWORD "agil2626"

// ========== FIREBASE ==========
#define API_KEY       "AIzaSyCefzDqzoTLblHtOsxMiDw3BB1QAox0VIQ"
#define DATABASE_URL  "https://nutriplant-579c9-default-rtdb.firebaseio.com/"
#define LEGACY_TOKEN  "yEFKFhgPmBoLWrdkRSf2pgiYY3ebS12uWDoikwkl"

// ========== LED & BUTTON ==========
#define LED_PIN       15
#define BUTTON_PIN    14
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
bool manualMode = false;

// ========== MODBUS RS485 ==========
#define RXD2   16
#define TXD2   17
#define RE_PIN 19
#define DE_PIN 18
HardwareSerial MySerial(2);
ModbusMaster node;

// ========== SOIL SENSOR ==========
#define SOIL_PIN 34

// Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

// Variables
uint16_t nitrogen = 0, phosphorus = 0, potassium = 0;
float soilPercent = 0;
int currentPage = 0;
const int totalPages = 4;
unsigned long lastSensorDisplayTime = 0;

// Fungsi Modbus TX control
void preTransmission() {
  digitalWrite(DE_PIN, HIGH);
  digitalWrite(RE_PIN, HIGH);
}
void postTransmission() {
  digitalWrite(DE_PIN, LOW);
  digitalWrite(RE_PIN, LOW);
}

// Fungsi teks rata tengah
void centerText(const String &text, int y, int textSize = 2) {
  display.setTextSize(textSize);
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(text, 0, y, &x1, &y1, &w, &h);
  int x = (SCREEN_WIDTH - w) / 2;
  display.setCursor(x, y);
  display.print(text);
}

// Fungsi menampilkan ikon WiFi
void drawWiFiIcon() {
  display.drawPixel(118, 0, WHITE);
  display.drawLine(117, 1, 119, 1, WHITE);
  display.drawLine(116, 2, 120, 2, WHITE);
  display.drawLine(115, 3, 121, 3, WHITE);
}

// Fungsi waktu update ke string
String getFormattedTime() {
  time_t now;
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return "unknown";
  }
  char buffer[30];
  strftime(buffer, sizeof(buffer), "%d/%m/%Y %H:%M:%S", &timeinfo);
  return String(buffer);
}

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

  // OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("OLED failed"));
    while (true);
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
  centerText("Nutriplant", 20, 2);
  display.display();
  delay(1500);
  display.clearDisplay();

  // Pin setup
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(RE_PIN, OUTPUT);
  pinMode(DE_PIN, OUTPUT);
  digitalWrite(RE_PIN, LOW);
  digitalWrite(DE_PIN, LOW);

  // WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
    Serial.print(".");
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delay(500);
  }
  digitalWrite(LED_PIN, LOW);
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi connected");
    manualMode = false;
    configTime(25200, 0, "pool.ntp.org"); // GMT+7 (WIB)
  } else {
    Serial.println("\nWiFi not connected - Manual Mode");
    manualMode = true;
  }

  // Firebase
  config.api_key = API_KEY;
  config.database_url = DATABASE_URL;
  config.signer.tokens.legacy_token = LEGACY_TOKEN;
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);

  // RS485 setup
  MySerial.begin(4800, SERIAL_8N1, RXD2, TXD2);
  node.begin(1, MySerial);
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

  delay(1000);
}

void loop() {
  unsigned long currentMillis = millis();
  static unsigned long wifiDisconnectedSince = 0;

  // === Read NPK ===
  uint8_t result = node.readHoldingRegisters(0x0000, 3);
  if (result == node.ku8MBSuccess) {
    nitrogen = node.getResponseBuffer(0);
    phosphorus = node.getResponseBuffer(1);
    potassium = node.getResponseBuffer(2);

    if (!manualMode) {
      Firebase.RTDB.setInt(&fbdo, "sensor_data/nitrogen", nitrogen);
      Firebase.RTDB.setInt(&fbdo, "sensor_data/phosphorus", phosphorus);
      Firebase.RTDB.setInt(&fbdo, "sensor_data/potassium", potassium);
      Firebase.RTDB.setString(&fbdo, "sensor_data/error_npk", "");

      // Set waktu update
      String timeNow = getFormattedTime();
      Firebase.RTDB.setString(&fbdo, "sensor_data/update", timeNow);
    }
  } else {
    if (!manualMode) {
      Firebase.RTDB.setString(&fbdo, "sensor_data/error_npk", "Modbus Error: " + String(result));
    }
  }

  // === Read Soil Moisture ===
  int soilAnalog = analogRead(SOIL_PIN);
  soilPercent = map(soilAnalog, 4095, 1500, 0, 100);
  soilPercent = constrain(soilPercent, 0, 100);
  if (!manualMode) {
    Firebase.RTDB.setFloat(&fbdo, "sensor_data/soil_moisture", soilPercent);

    // Update waktu juga
    String timeNow = getFormattedTime();
    Firebase.RTDB.setString(&fbdo, "sensor_data/update", timeNow);
  }

  // === Tombol untuk reconnect WiFi ===
  bool reading = digitalRead(BUTTON_PIN);
  if (reading != lastButtonState) {
    lastDebounceTime = currentMillis;
  }

  if ((currentMillis - lastDebounceTime) > debounceDelay) {
    if (lastButtonState == HIGH && reading == LOW) {
      Serial.println("Reconnect button pressed");

      display.clearDisplay();
      centerText("Connecting WiFi", 10);
      display.display();

      WiFi.disconnect();
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
      unsigned long reconnectStart = millis();
      bool reconnected = false;

      while (millis() - reconnectStart < 10000) {
        if (WiFi.status() == WL_CONNECTED) {
          reconnected = true;
          break;
        }
        delay(500);
      }

      manualMode = !reconnected;

      display.clearDisplay();
      if (reconnected) {
        centerText("WiFi Connected", 10);
        Serial.println("Reconnected to WiFi, online mode.");
      } else {
        centerText("Reconnect Failed", 10);
        Serial.println("Reconnect failed, still in manual mode.");
      }
      display.display();
      delay(1500);
    }
  }
  lastButtonState = reading;

  // === Auto-switch ke Manual jika WiFi putus ===
  if (WiFi.status() != WL_CONNECTED) {
    if (wifiDisconnectedSince == 0) {
      wifiDisconnectedSince = currentMillis;
    } else if (!manualMode && currentMillis - wifiDisconnectedSince >= 10000) {
      manualMode = true;
      display.clearDisplay();
      centerText("No WiFi - Manual", 10);
      display.display();
      Serial.println("Auto-switched to Manual Mode");
    }
  } else {
    wifiDisconnectedSince = 0;
  }

  // === Tampilkan Sensor Setiap 5 Detik ===
  if (currentMillis - lastSensorDisplayTime >= 5000) {
    lastSensorDisplayTime = currentMillis;
    currentPage = (currentPage + 1) % totalPages;

    display.clearDisplay();
    switch (currentPage) {
      case 0:
        centerText("N (mg/kg)", 5, 2);
        centerText(String(nitrogen), 30, 3);
        break;
      case 1:
        centerText("P (mg/kg)", 5, 2);
        centerText(String(phosphorus), 30, 3);
        break;
      case 2:
        centerText("K (mg/kg)", 5, 2);
        centerText(String(potassium), 30, 3);
        break;
      case 3:
        centerText("Soil Moisture", 5, 1);
        centerText(String(soilPercent, 1) + "%", 30, 3);
        break;
    }

    if (!manualMode) {
      drawWiFiIcon();
    }

    display.display();
  }

  digitalWrite(LED_PIN, manualMode ? LOW : HIGH);
  delay(100);
}