#include "MQTT.h"


WebServer* MQTT::server = new WebServer(80);

bool MQTT::saveConfigFlag=false;
WiFiManagerParameter custom_hostname("hostname", "Hostname", "", 40);
WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "", 40);
WiFiManagerParameter custom_mqtt_port("port", "mqtt port", "",6);
WiFiManagerParameter custom_mqtt_topic("topic", "topic", "", 40);
WiFiManagerParameter custom_deftime("deftime", "Default ON Time (min)", "", 6);
WiFiManagerParameter custom_numValves("numValves", "Available Valves", "", 6);
MQTT::MQTT(){
}


void MQTT::serveData(void){
    JsonDocument doc;
     // Create the "analog" array
    JsonArray valveStates = doc["valveStates"].to<JsonArray>();
    for (int i = 0; i < NUM_VALVES; i++) {
        valveStates.add(glblData.valveState[i]);
    }
    valveStates = doc["desiredValveStates"].to<JsonArray>();
    for (int i = 0; i < NUM_VALVES; i++) {
        valveStates.add(valveStateStrings[glblData.setValveState[i]]);
    }
    serializeJson(doc, Serial);
    Serial.println("Sending website...");
    // Write response headers
    // Write JSON document
    String jsonStr;
    serializeJsonPretty(doc, jsonStr);
    server->send(200, FPSTR(HTTP_HEAD_JSON), jsonStr);
    Serial.println("Sent website...");

    //request->send(404, "text/plain", sending);
}

void MQTT::serveSetValve(void){
    JsonDocument doc;
    doc["status"] = "Fail";

    String sending;
    sending = "{";
    if(server->args() == 2){
        if(server->argName(0)=="VALVE" || server->argName(0)=="valve"){
            int valvenum = atoi(server->arg(0).c_str());
            if(valvenum > 0 && valvenum < NUM_VALVES && (server->argName(1)=="SET" || server->argName(1)=="set")){
                doc["status"] = "Success";   
            }
        }
    }
    String jsonStr;
    serializeJsonPretty(doc, jsonStr);
    server->send(200, FPSTR(HTTP_HEAD_JSON), jsonStr);
}



void MQTT::begin(){
    saveConfigFlag = false;
    prefs.begin("settings");
    prefs.getString("HOST",hostname,40);
    prefs.getString("MQTT_SERVER",mqtt_server,40);
    mqtt_port = prefs.getInt("MQTT_PORT",mqtt_port);
    prefs.getString("MQTT_TOPIC",mqtt_topic,40);
    defTime_min = prefs.getInt("DEF_TIME_MIN",defTime_min);
    numValves = prefs.getInt("NUM_VALVES",numValves);
    //glblData.enBuff = enBuff;
    //ui_settemp(defTemp);
    prefs.end();
    Serial.println("Settings loaded:");
    Serial.print("HOST: ");
    Serial.println(hostname);
    Serial.print("MQTT_SERVER: ");
    Serial.println(mqtt_server);
    Serial.print("MQTT_PORT: ");
    Serial.println(mqtt_port);
    Serial.print("MQTT_TOPIC: ");
    Serial.println(mqtt_topic);
    Serial.print("DEF_TIME_MIN: ");
    Serial.println(defTime_min);
    Serial.print("NUM_VALVES: ");
    Serial.println(numValves);
    wm.setSaveConfigCallback(MQTT::saveConfigCallback);
    custom_hostname.setValue(hostname,40);
    custom_mqtt_server.setValue(mqtt_server,40);
    custom_mqtt_port.setValue(String(mqtt_port).c_str(),6);
    custom_mqtt_topic.setValue(mqtt_topic,40);
    custom_deftime.setValue(String(defTime_min).c_str(),6);
    custom_numValves.setValue(String(numValves).c_str(),6);
    wm.addParameter(&custom_hostname);
    wm.addParameter(&custom_mqtt_server);
    wm.addParameter(&custom_mqtt_port);
    wm.addParameter(&custom_mqtt_topic);
    wm.addParameter(&custom_deftime);
    wm.addParameter(&custom_numValves);
    psclient.setClient(client);
    psclient.setServer(mqtt_server,mqtt_port);
    WiFi.setHostname(custom_hostname.getValue()); //define hostname
    wm.setConfigPortalBlocking(false);


    if(wm.autoConnect("AutoConnectAP")){
        //if you get here you have connected to the WiFi
        Serial.println("connected...yeey :)");
        strcpy(myIP,WiFi.localIP().toString().c_str());
        wm.setHttpPort(8080);
        wm.startWebPortal();
        wifiConnected = true;
    }
    else 
    {
        wm.startWebPortal();
        Serial.println("non blocking config portal running on Port 80");
    }
    if(wifiConnected){
        ArduinoOTA.setHostname(hostname);
        ArduinoOTA
        .onStart([]() {
        String type;
        if (ArduinoOTA.getCommand() == U_FLASH)
            type = "sketch";
        else // U_SPIFFS
            type = "filesystem";

        // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
        Serial.println("Start updating " + type);
        })
        .onEnd([]() {
            Serial.println("\nEnd");
        })
        .onProgress([](unsigned int progress, unsigned int total) {
            Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
        })
        .onError([](ota_error_t error) {
            Serial.printf("Error[%u]: ", error);
            if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
            else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
            else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
            else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
            else if (error == OTA_END_ERROR) Serial.println("End Failed");
        });
        ArduinoOTA.begin();
        
        // This is not the safest way to reset the webserver, it can cause crashes on callbacks initilized before this and since its a shared pointer...
        // @todo add a new callback maybe, after webserver started, callback cannot override handlers, but can grab them first
        
        /* Setup httpd callbacks, web pages: root, wifi config pages, SO captive portal detectors and not found. */

        // G macro workaround for Uri() bug https://github.com/esp8266/Arduino/issues/7102
        server->on("/", MQTT::serveData);
        server->on("/setValve", MQTT::serveSetValve);
        server->begin();
        reconnect();
    }
}
void MQTT::saveConfigCallback(void){
    Serial.println("SAVE");
    saveConfigFlag = true;
}

void MQTT::saveConfigToFlash(void){
    strcpy(mqtt_server,custom_mqtt_server.getValue());
    mqtt_port = atoi(custom_mqtt_port.getValue());
    strcpy(hostname,custom_hostname.getValue());
    strcpy(mqtt_topic,custom_mqtt_topic.getValue());
    defTime_min = atoi(custom_deftime.getValue());
    numValves = atoi(custom_numValves.getValue());
    //glblData.enBuff = enBuff;
    prefs.begin("settings");
    prefs.putString("HOST",hostname);
    prefs.putString("MQTT_SERVER",mqtt_server);
    prefs.putInt("MQTT_PORT",mqtt_port);
    prefs.putString("MQTT_TOPIC",mqtt_topic);
    prefs.putInt("DEF_TIME_MIN",defTime_min);
    prefs.putInt("NUM_VALVES",numValves);
    prefs.end();
    Serial.println("Settings SAVED:");
    Serial.print("HOST: ");
    Serial.println(hostname);
    Serial.print("MQTT_SERVER: ");
    Serial.println(mqtt_server);
    Serial.print("MQTT_PORT: ");
    Serial.println(mqtt_port);
    Serial.print("MQTT_TOPIC: ");
    Serial.println(mqtt_topic);
    Serial.print("DEF_TIME_MIN: ");
    Serial.println(defTime_min);
    Serial.print("NUM_VALVES: ");
    Serial.println(numValves);

}

void MQTT::reconnect(void) {
    // Loop until we're reconnected
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (psclient.connect(hostname)) {
        Serial.println("connected");
        // Subscribe
        if(strlen(mqtt_topic)>1)
            psclient.subscribe(mqtt_topic);
    } else {
        Serial.print("failed, rc=");
        Serial.print(psclient.state());
    }
}

void MQTT::loop(void){
    unsigned long now = millis();
    if(now-lastStatusUpdate > 15000){   //update wifi status every 15secs.
        lastStatusUpdate = now;
        if(WiFi.isConnected()){
            int32_t rssi = WiFi.RSSI();
            if(rssi > -55){
               // update_wifi_strength(WIFI_STRENGTH_HIGH);
            }else if(rssi > -75){
                //update_wifi_strength(WIFI_STRENGTH_MED);
            }else if(rssi > -90){
                //update_wifi_strength(WIFI_STRENGTH_LOW);
            }else{
                //update_wifi_strength(WIFI_STRENGTH_OFF);
            }
            if(!psclient.connected()){
                //update_wifi_strength(WIFI_STRENGTH_ERROR);
                if(now-lastReconnectTry > 60000){
                    lastReconnectTry = now;
                    reconnect();
                }
            }
        }else{
            //update_wifi_strength(WIFI_STRENGTH_OFF);
        }
    }
    server->handleClient();
    psclient.loop();
    ArduinoOTA.handle();
    wm.process();
    if(saveConfigFlag){
        Serial.println("Save Settings");
        saveConfigFlag = false;
        saveConfigToFlash();
        Serial.println("disconnect mqtt to activate new settings");
        psclient.disconnect();
    }
}

char* MQTT::getMqttServer(void){
    return mqtt_server;
}
uint16_t MQTT::getMqttPort(void){
    return mqtt_port;
}
char* MQTT::getHostname(void){
    return hostname;
}
char* MQTT::getMqttTopic(void){
    return mqtt_topic;
}
char* MQTT::getMyIP(void){
    return myIP;
}

void MQTT::publish(const char* msg){
    psclient.publish(mqtt_topic,msg);
}
void MQTT::publish(const char* msg, unsigned int len){
    psclient.publish(mqtt_topic,msg,len);
}
void MQTT::publish(const char* msg, bool retained){
    psclient.publish(mqtt_topic,msg,retained);
}
void MQTT::publish_sub(const char* subtopic, const char* msg){
    char newtopic[60];
    strcpy(newtopic,mqtt_topic);
    strcat(newtopic,"/");
    strcat(newtopic,subtopic);
    psclient.publish(newtopic,msg);
}