//========== Preprocesor ==========

//---- Required ----
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
//------------------
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

#include "Tasker.h"
#include "OneWire.h"
#include "DallasTemperature.h"

#include "config.h"

//========== Create objects ==========

AsyncWebServer server(80);
Tasker tasker;
OneWire oneWire(DS18B20_PIN);
DallasTemperature sensor(&oneWire);

//========== Init variables ==========

float temperature;
String metrics;
float reqTemp = 21.5;
float offset = 0.25;

//=================================


void setup() {
//========== Wi-Fi setup ==========

  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    //Connection Failed! Rebooting...
    delay(5000);
    ESP.restart();
  }
  
//========== OTA setup ==========

  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  // ArduinoOTA.setHostname("myesp8266");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
  ArduinoOTA.begin();
  SPIFFS.begin();
//========== Pin setup ==========

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);

//========== Sensor setup ==========

  sensor.begin();
  // do not block during temperature conversion
  sensor.setWaitForConversion(false);

//========== Tasks init ==========

  startConversion(); // First temp read
  tasker.setInterval(startConversion, 15000); // read temperature every 15 seconds
  tasker.setInterval(otaHandle, 1000);

  if(THERMOSTAT){
    tasker.setInterval(thermostatCheck, 1000);
  }

//========== Web server setup ==========

  if(METRICS_EXPORT || API){
    if(METRICS_EXPORT){
      server.on("/metrics", HTTP_GET, [](AsyncWebServerRequest *request){
          tasker.setTimeout(ledBlink, 10);
          request->send(200, "text/plain; charset=utf-8", metrics);
      });
    }
    if(API){
      server.on("/set", HTTP_GET, [](AsyncWebServerRequest *request){
        //nutno nastavit maxima a minima
    
        String repply;
        
        if(THERMOSTAT){
          if (request->hasParam("increasereqtemp")) {
            reqTemp += request->getParam("increasereqtemp")->value().toFloat();
            repply = String(reqTemp);
          }else if (request->hasParam("decreasereqtemp")) {
            reqTemp -= request->getParam("decreasereqtemp")->value().toFloat();
            repply = String(reqTemp);
          }else if (request->hasParam("increaseoffset")) {
            offset += request->getParam("increaseoffset")->value().toFloat();
            repply = String(offset);
          }else if (request->hasParam("decreaseoffset")) {
            offset -= request->getParam("decreaseoffset")->value().toFloat();
            repply = String(offset);
          }else{
            repply = "unknown parameter";
          }
        }else{
          if (request->hasParam("relay")) {
            boolean state = request->getParam("relay")->value() == "0";
            digitalWrite(RELAY_PIN, state);
            repply = "success";
          }else{
            repply = "unknown parameter";
          }
        }
    
        request->send(200, "text/plain; charset=utf-8", repply);
      });
      server.on("/get", HTTP_GET, [](AsyncWebServerRequest *request){
        //nutno vyřešit dotazy na více parametrů najednou
        String repply;
    
        if(request->hasParam("temp")) {
          repply = temperature;
        }else if(request->hasParam("relay")){
          repply = !digitalRead(RELAY_PIN);
        }else if(THERMOSTAT && request->hasParam("reqtemp")){
          repply = reqTemp;
        }else if(THERMOSTAT && request->hasParam("offset")) {
          repply = offset;
        }else{
          repply = "unknown parameter";
        }
    
        request->send(200, "text/plain; charset=utf-8", repply);
      });
    }
    if(API && WEBAPP){
        server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
    }
    
    server.onNotFound(notFound);
    server.begin();
  }
//=================================
}

void loop() {
  tasker.loop();
}

void otaHandle(){
  ArduinoOTA.handle();
}

void notFound(AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not found");
}

void thermostatCheck(){
  if(temperature < (reqTemp - offset)){
    digitalWrite(RELAY_PIN, false);
  }else if(temperature > (reqTemp + offset)){
    digitalWrite(RELAY_PIN, true);
  }
}

void readSensor() {
  float tempC = sensor.getTempCByIndex(0);
  
  if(tempC != DEVICE_DISCONNECTED_C){
    // read the actual temperature after it's been converted
    temperature = tempC + CORRECTION;
    // do what you need with the temperature here
  }
  metrics = "temp ";
  metrics += temperature;
}

void startConversion() {
    // start temperature conversion (does not block)
    sensor.requestTemperatures();
    // schedule reading the actual temperature in 750 milliseconds
    tasker.setTimeout(readSensor, 750);
}
void ledBlink() {
    digitalWrite(LED_PIN, LOW);
    delay(200);
    digitalWrite(LED_PIN, HIGH);
}
