#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <LiquidCrystal_I2C.h>
#include <settings.h>


#define BUTTON_PIN D3 // GPIO-Pin verbunden mit Taster (D3 entspricht GPIO0)

const uint32_t connectTimeoutMs = 10000; // WLAN Timeout

// Globale Variablen
unsigned long lastUpdateTime = 0; // Zeitstempel der letzten Aktion
unsigned long refreshRate = refreshrate_power; // Aktualisierungsrate in Millisekunden
bool isModeChanged = false; // Zustand, ob eine Nachricht angezeigt wird

int lastButtonState = HIGH; // Letzter Zustand des Buttons
int currentButtonState;

// Array für die Werte
String tasmota_smr_ip_values[6];
String tasmota_plug_ip_values[6];

String tasmota_smr_ip_values_old[6];
String tasmota_plug_ip_values_old[6];

// Modus-Definitionen
int currentMode = 0;
const char* modeNames[] = {"Leistung", "1.8.0", "1.8.1", "1.8.2", "2.8.0"};  // Nur für Seriellen Monitor


// LCD initialisieren (16x2 Display)
LiquidCrystal_I2C lcd(0x27, 16, 2);

int numNetworks = sizeof(ssidList) / sizeof(ssidList[0]);
int current_ap = 0;  // Start mit der ersten SSID
bool reconnecting = false;  // Flag für den Reconnect-Status

// WLAN Symbol
byte customChar_wlan[] = {
    B00000,
    B01110,
    B10001,
    B00100,
    B01010,
    B00000,
    B00100,
    B00000
};

void _lcd_display_text(int row = 0, const char* text = "                ", const char* alignment = "left", bool returnToStart = false) {

    // Funktion die das Anzeigen von Text auf dem 1602 Display etwas erleichtert.
    // Mit alignment kann die Ausrichtung des Texts angegeben werden. (alignment = "left", "center", "right")
    // Mit returnToStart wird das Verhalten von zu langem Text festgelegt. Dieser Scrollt einmal durch und springt danch
    // wieder an Position 1 (returnToStart = true) oder verbleibt nach dem durchscrollen am Ende (returnToStart = false)

    int textLength = strlen(text);

    if (row < 0 || row > 1) {
        return;
    }

    lcd.setCursor(0, row);

    if (textLength <= 16) {
        int padding = 0;

        if (strcmp(alignment, "center") == 0) {
            padding = (16 - textLength) / 2;
        } else if (strcmp(alignment, "right") == 0) {
            padding = 16 - textLength;
        }

        lcd.setCursor(0, row);

        for (int i = 0; i < padding; i++) {
            lcd.print(" ");
        }

        lcd.print(text);
    } else {
        lcd.setCursor(0, row);
        lcd.print(text);
        delay(2000);

        for (int i = 1; i <= textLength - 16; i++) {
            lcd.setCursor(0, row);
            lcd.print(text + i);
            delay(300);
        }

        if (returnToStart) {
            lcd.setCursor(0, row);
            lcd.print(text);
        } else {
            lcd.setCursor(0, row);
            lcd.print(text + (textLength - 16));
        }
    }
}

void extractTasmotaValues(const String& url, String values[6], int decimalPlaces = -1) {
    WiFiClient client;
    HTTPClient http;

    for (int i = 0; i < 6; i++) {
        values[i] = "?"; // Standardwert, falls Tasmota Gerät nicht antwortet
    }

    if (http.begin(client, url)) {
        int httpResponseCode = http.GET();

        if (httpResponseCode == 200) {
            String response = http.getString();
            http.end();

            // Array mit den Schlüsseln der gewünschten Werte
            const String keys[6] = {
                tasmota_plug_json_prefix_power, 
                tasmota_smr_json_prefix_power, 
                tasmota_smr_json_prefix_1_8_0, 
                tasmota_smr_json_prefix_1_8_1, 
                tasmota_smr_json_prefix_1_8_2, 
                tasmota_smr_json_prefix_2_8_0
            };

            // Werte extrahieren
            for (int i = 0; i < 6; i++) {
                values[i] = "?"; // Standardwert, falls Key nicht gefunden wird
                int keyIndex = response.indexOf("\"" + keys[i] + "\":");
                if (keyIndex != -1) {
                    int valueStart = keyIndex + keys[i].length() + 3; // Position nach dem ":"
                    int valueEnd = response.indexOf(",", valueStart); // Ende der Zahl oder des Wertes
                    if (valueEnd == -1) {
                        valueEnd = response.indexOf("}", valueStart); // Falls am Ende der JSON-Objekte
                    }
                    if (valueEnd != -1) {
                        values[i] = response.substring(valueStart, valueEnd);
                        values[i].trim(); // Entfernt überflüssige Leerzeichen

                        // Anzahl der Nachkommastellen begrenzen
                        if (decimalPlaces >= 0) {
                            int dotIndex = values[i].indexOf('.');
                            if (dotIndex != -1) {
                                int truncateIndex = dotIndex + 1 + decimalPlaces;
                                if (truncateIndex < values[i].length()) {
                                    values[i] = values[i].substring(0, truncateIndex);
                                }
                            }
                        }
                    }
                }
            }
        } else {
            Serial.println("HTTP Fehler: " + String(httpResponseCode));
        }
        http.end();
    } else {
        Serial.println("Verbindung zur URL fehlgeschlagen: " + url);
    }
}





void connectToWiFi() {
    Serial.println("WLAN Verbindungsaufbau zu: " + String(ssidList[current_ap]));
    Serial.println("Passwort: " + String(passwordList[current_ap]));

    _lcd_display_text(0, "Bitte warten", "center");
    _lcd_display_text(1, "WLAN verbinden  ", "left");

    lcd.createChar(0, customChar_wlan);
    lcd.setCursor(15, 1);
    lcd.write(byte(0));

    WiFi.begin(ssidList[current_ap], passwordList[current_ap]);

    int attempt = 0;
    while (WiFi.status() != WL_CONNECTED && attempt < 10) {
        delay(500);
        Serial.print(".");
        lcd.setCursor(15, 1);
        lcd.print(" ");
        delay(500);
        lcd.setCursor(15, 1);
        lcd.write(byte(0));
        attempt++;
    }

    if (WiFi.status() != WL_CONNECTED) {
        WiFi.disconnect();
        delay(500);
        Serial.println("\nVerbindung fehlgeschlagen.");
        current_ap = (current_ap + 1) % numNetworks;
        reconnecting = false;
    } else {
        Serial.println("\nWLAN verbunden: " + WiFi.SSID());

        _lcd_display_text(0, "WLAN verbunden", "center");
        _lcd_display_text(1);
        _lcd_display_text(1, WiFi.SSID().c_str());

        reconnecting = false;
        delay(2500);

        _lcd_display_text(0, "Netzbezug:    ?W");
        _lcd_display_text(1, "Erzeugung:    ?W");
    }
}

void update_power_values() {

    lcd.setCursor(10, 0);
    lcd.print("     ");
    lcd.setCursor(16 - (tasmota_smr_ip_values[1].length() + 1), 0);
    lcd.print(tasmota_smr_ip_values[1]);
    lcd.print("W");

    lcd.setCursor(10, 1);
    lcd.print("     ");
    lcd.setCursor(16 - (tasmota_plug_ip_values[0].length() + 1), 1);
    lcd.print(tasmota_plug_ip_values[0]);
    lcd.print("W");

    tasmota_smr_ip_values_old[1] = tasmota_smr_ip_values[1];
    tasmota_plug_ip_values_old[0] = tasmota_plug_ip_values[0];
}

void update_180_values() {
    _lcd_display_text(1, (tasmota_smr_ip_values[2] + "kWh").c_str(), "center"); 
    tasmota_smr_ip_values_old[2] = tasmota_smr_ip_values[2];
}

void update_181_values() {
    _lcd_display_text(1, (tasmota_smr_ip_values[3] + "kWh").c_str(), "center"); 
    tasmota_smr_ip_values_old[3] = tasmota_smr_ip_values[3];
}

void update_182_values() {
    _lcd_display_text(1, (tasmota_smr_ip_values[4] + "kWh").c_str(), "center"); 
    tasmota_smr_ip_values_old[4] = tasmota_smr_ip_values[4];
}

void update_280_values() {
    _lcd_display_text(1, (tasmota_smr_ip_values[5] + "kWh").c_str(), "center"); 
    tasmota_smr_ip_values_old[5] = tasmota_smr_ip_values[5];
}

void handleButtonPress() {

    currentButtonState = digitalRead(BUTTON_PIN);

    if (currentButtonState == LOW && lastButtonState == HIGH) {

        lcd.clear();

        currentMode = (currentMode + 1) % 5;

        Serial.print("Neuer Modus: ");
        Serial.println(modeNames[currentMode]);

        if (currentMode == 0) {
            refreshRate = refreshrate_power;
        } else {
            refreshRate = refreshrate_consumption;
        }

        Serial.print("Aktualisierungsrate: ");
        Serial.println(refreshRate);

        isModeChanged = true;

        delay(200);
    }

    lastButtonState = currentButtonState;
}

void setup() {
    pinMode(BUTTON_PIN, INPUT_PULLUP);

    Serial.begin(115200);
    delay(1000);

    lcd.init();
    lcd.backlight();
    lcd.createChar(0, customChar_wlan);

    WiFi.setHostname(hostname);
    WiFi.mode(WIFI_STA);

    lcd.setCursor(0, 0);
    _lcd_display_text(0, "Bereit", "center");




}

void loop() {

    handleButtonPress();

    if (WiFi.status() != WL_CONNECTED && !reconnecting) {
        reconnecting = true;
        connectToWiFi();
    }


    if (WiFi.status() == WL_CONNECTED && !reconnecting) {
        if (millis() - lastUpdateTime >= refreshRate) {
            lastUpdateTime = millis();

            extractTasmotaValues("http://" + tasmota_smr_ip + "/cm?cmnd=Status%208", tasmota_smr_ip_values, tasmota_decimalplaces);
            
            if (currentMode == 0) {
                if (isModeChanged == true)  {
                    _lcd_display_text(0, "Netzbezug:", "left");
                    _lcd_display_text(1, "Erzeugung:", "left");
                    update_power_values();
                }
                extractTasmotaValues("http://" + tasmota_plug_ip + "/cm?cmnd=Status%208", tasmota_plug_ip_values, tasmota_decimalplaces);
            }
        }
    }


    switch (currentMode) {
        case 0:
            if (tasmota_smr_ip_values[1] != tasmota_smr_ip_values_old[1] || isModeChanged == true) {
                isModeChanged = false;
                _lcd_display_text(0, "Netzbezug:", "left");
                _lcd_display_text(1, "Erzeugung:", "left");
                update_power_values();
                
            }
            break;
        case 1:
            if (tasmota_smr_ip_values[2] != tasmota_smr_ip_values_old[2] || isModeChanged == true) {
                isModeChanged = false;
                _lcd_display_text(0, "1.8.0 (HT+NT):", "center");
                update_180_values();
            }
            break;
        case 2:
            if (tasmota_smr_ip_values[3] != tasmota_smr_ip_values_old[3] || isModeChanged == true) {
                isModeChanged = false;
                _lcd_display_text(0, "1.8.1 (HT):", "center");
                update_181_values();
            }
            break;
        case 3:
            if (tasmota_smr_ip_values[4] != tasmota_smr_ip_values_old[4] || isModeChanged == true) {
                isModeChanged = false;
                _lcd_display_text(0, "1.8.2 (NT):", "center");
                update_182_values();
            }
            break;
        case 4:
            if (tasmota_smr_ip_values[5] != tasmota_smr_ip_values_old[5] || isModeChanged == true) {
                isModeChanged = false;
                _lcd_display_text(0, "2.8.0 (Einsp.):", "center");
                update_280_values();
            }
            break;
    }

}
