#include "HardwareSerial.h"
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebSrv.h>

#include "index.h"
#include "style.h"
#include "script.h"
#include "setting.h"

AsyncWebServer server(80);

String getHour(String s) {
  return s.substring(0, 2);
}

String getMinutes(String s) {
  return s.substring(3, 5);
}

void handle_switch(AsyncWebServerRequest *request) {
  if (request->hasParam("relay") && request->hasParam("state")) {
    int s = request->getParam("state")->value().toInt();
    switch (request->getParam("relay")->value().toInt()) {
      case 1:
        switch_relay(RELAY1, s, pref.getBool("r1_neg", false));
        Serial.println("Relay 1 -> " + (String)relay_state(RELAY1, pref.getBool("r1_neg", false)));
        break;
      case 2:
        switch_relay(RELAY2, s, pref.getBool("r2_neg", false));
        Serial.println("Relay 2 -> " + (String)relay_state(RELAY2, pref.getBool("r2_neg", false)));
        break;
      case 3:
        switch_relay(RELAY3, s, pref.getBool("r3_neg", false));
        Serial.println("Relay 3 -> " + (String)relay_state(RELAY3, pref.getBool("r3_neg", false)));
        break;
      case 4:
        switch_relay(RELAY4, s, pref.getBool("r4_neg", false));
        Serial.println("Relay 4 -> " + (String)relay_state(RELAY4, pref.getBool("r4_neg", false)));
        break;
    }
    request->send(200);
  }
}

void handle_switch_state(AsyncWebServerRequest *request) {
  if (request->hasParam("relay")) {
    switch (request->getParam("relay")->value().toInt()) {
      case 1:
        request->send(200, "text/plain", (String)relay_state(RELAY1, pref.getBool("r1_neg", false)));
        break;
      case 2:
        request->send(200, "text/plain", (String)relay_state(RELAY2, pref.getBool("r2_neg", false)));
        break;
      case 3:
        request->send(200, "text/plain", (String)relay_state(RELAY3, pref.getBool("r3_neg", false)));
        break;
      case 4:
        request->send(200, "text/plain", (String)relay_state(RELAY4, pref.getBool("r4_neg", false)));
        break;
    }
  }
}

void handle_setting_save(AsyncWebServerRequest *request) {
  //relay 1
  if (request->hasParam("relay1_on", true) && request->hasParam("relay1_off", true) && request->hasParam("relay1_name", true)) {
    if (request->hasParam("relay1_enable", true)) {
      pref.putBool("r1_enable", true);
    } else {
      pref.putBool("r1_enable", false);
    }
    if (request->hasParam("relay1_negation", true)) {
      pref.putBool("r1_neg", true);
    } else {
      pref.putBool("r1_neg", false);
    }
    pref.putString("r1_name", request->getParam("relay1_name", true)->value());
    pref.putString("r1_on_h", getHour(request->getParam("relay1_on", true)->value()));
    pref.putString("r1_on_m", getMinutes(request->getParam("relay1_on", true)->value()));
    pref.putString("r1_off_h", getHour(request->getParam("relay1_off", true)->value()));
    pref.putString("r1_off_m", getMinutes(request->getParam("relay1_off", true)->value()));
    Serial.println("Save settings relay 1");
  }

  // relay 2
  if (request->hasParam("relay2_on", true) && request->hasParam("relay2_off", true) && request->hasParam("relay2_name", true)) {
    if (request->hasParam("relay2_enable", true)) {
      pref.putBool("r2_enable", true);
    } else {
      pref.putBool("r2_enable", false);
    }
    if (request->hasParam("relay2_negation", true)) {
      pref.putBool("r2_neg", true);
    } else {
      pref.putBool("r2_neg", false);
    }
    pref.putString("r2_name", request->getParam("relay2_name", true)->value());
    pref.putString("r2_on_h", getHour(request->getParam("relay2_on", true)->value()));
    pref.putString("r2_on_m", getMinutes(request->getParam("relay2_on", true)->value()));
    pref.putString("r2_off_h", getHour(request->getParam("relay2_off", true)->value()));
    pref.putString("r2_off_m", getMinutes(request->getParam("relay2_off", true)->value()));
    Serial.println("Save settings relay 2");
  }
  // relay 3

  if (request->hasParam("relay3_on", true) && request->hasParam("relay3_off", true) && request->hasParam("relay3_name", true)) {
    if (request->hasParam("relay3_enable", true)) {
      pref.putBool("r3_enable", true);
    } else {
      pref.putBool("r3_enable", false);
    }
    if (request->hasParam("relay3_negation", true)) {
      pref.putBool("r3_neg", true);
    } else {
      pref.putBool("r3_neg", false);
    }
    pref.putString("r3_name", request->getParam("relay3_name", true)->value());
    pref.putString("r3_on_h", getHour(request->getParam("relay3_on", true)->value()));
    pref.putString("r3_on_m", getMinutes(request->getParam("relay3_on", true)->value()));
    pref.putString("r3_off_h", getHour(request->getParam("relay3_off", true)->value()));
    pref.putString("r3_off_m", getMinutes(request->getParam("relay3_off", true)->value()));
    Serial.println("Save settings relay 3");
  }

  // relay 4
  if (request->hasParam("relay4_on", true) && request->hasParam("relay4_off", true) && request->hasParam("relay4_name", true)) {
    if (request->hasParam("relay4_enable", true)) {
      pref.putBool("r4_enable", true);
    } else {
      pref.putBool("r4_enable", false);
    }
    if (request->hasParam("relay4_negation", true)) {
      pref.putBool("r4_neg", true);
    } else {
      pref.putBool("r4_neg", false);
    }
    pref.putString("r4_name", request->getParam("relay4_name", true)->value());
    pref.putString("r4_on_h", getHour(request->getParam("relay4_on", true)->value()));
    pref.putString("r4_on_m", getMinutes(request->getParam("relay4_on", true)->value()));
    pref.putString("r4_off_h", getHour(request->getParam("relay4_off", true)->value()));
    pref.putString("r4_off_m", getMinutes(request->getParam("relay4_off", true)->value()));
    Serial.println("Save settings relay 4");
  }

  // ntp
  if (request->hasParam("ntp", true)) {
    pref.putString("ntp", request->getParam("ntp", true)->value());
    Serial.println("Save settings NTP");
  }

  AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "");
  response->addHeader("Location", "/");
  request->send(response);
}

void webserver_setup() {
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/html", pageIndex());
  });

  server.on("/style.css", [](AsyncWebServerRequest *request) {
    request->send(200, "text/css", style());
  });

  server.on("/script.js", [](AsyncWebServerRequest *request) {
    request->send(200, "text/javascript", script());
  });

  server.on("/settings", [](AsyncWebServerRequest *request) {
    request->send(200, "text/html", pageSettings());
  });

  server.on("/settings_save", HTTP_POST, handle_setting_save);

  server.on("/switch_state", HTTP_GET, handle_switch_state);

  server.on("/switch", HTTP_GET, handle_switch);

  server.on("/gettemp", [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", (String)(int)temperature);
  });

  server.on("/gethumidity", [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", (String)(int)humidity);
  });

  server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){
    // the request handler is triggered after the upload has finished... 
    // create the response, add header, and send response
    AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "");
  response->addHeader("Location", "/");
  request->send(response);
    //AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", (Update.hasError())?"FAIL":"OK");
    //response->addHeader("Connection", "close");
    //response->addHeader("Access-Control-Allow-Origin", "*");
    //request->send(response);

    ESP.restart();    
  },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
    //Upload handler chunks in data
    //Serial.setDebugOutput(true);
        WiFiUDP::stopAll();
    if(!index){ // if index == 0 then this is the first frame of data
      Serial.printf("Upload start: %s\n", filename.c_str());
      
      // calculate sketch space required for the update
      uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
      if(!Update.begin(maxSketchSpace)){//start with max available size
        Update.printError(Serial);
      } 

      Update.runAsync(true); // tell the updaterClass to run in async mode
    }

    //Write chunked data to the free sketch space
    if(Update.write(data, len) != len){
        Update.printError(Serial);
    }
    
    if(final){ // if the final flag is set then this is the last frame of data
      if(Update.end(true)){ //true to set the size to the current progress
          Serial.printf("Update Success: %u B\nRebooting...\n", index+len);
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "");
  response->addHeader("Location", "/");
  request->send(response);
        } else {
          Update.printError(Serial);
        }
    }
  });

  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not found");
  });

  server.begin();
  Serial.println("HTTP server started");
}