#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino

//needed for library
//#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include "WiFiManager.h" //https://github.com/tzapu/WiFiManager

#include "WebContent.h"

#include <PubSubClient.h> //MQTT
#include <ArduinoJson.h>

#define RelaisPin D4 // Relais
#define SwitchPin D1 // Active Low, Switch between D1 and GND

#define R_ON 0// active Low = 0, otherwise 1
#define R_OFF 1// active Low = 1, otherwise 0

#define MQTT_ON  "on"
#define MQTT_OFF "off"

#include "config.h"

ESP8266WebServer server(80); // Create a webserver object that listens for HTTP request on port 80

//MQTT
WiFiClient espClient;
PubSubClient mqttClient(MQTT_BROKER, 1883, espClient);

// time in millis the pump has been started
long pumpStarted = 0;

long waitTimeMillis = PUMP_ACTIVE_MILLIS;

boolean pumpActive = false;


void handleRoot(); // function prototypes for HTTP handlers
void handleOn();
void handleOff();
void handleNotFound();
void handleState();

void deactivatePump();
void activatePump();

void mqtt_callback(char* inTopic, byte* payload, unsigned int length);
void reconnect();

void configModeCallback(WiFiManager *myWiFiManager)
{
  Serial.println("Entered config mode");
  Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  Serial.println(myWiFiManager->getConfigPortalSSID());
}

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);

  pinMode(RelaisPin, OUTPUT);
  pinMode(SwitchPin, INPUT_PULLUP);
  digitalWrite(RelaisPin, R_OFF);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
  wifiManager.setAPCallback(configModeCallback);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if (!wifiManager.autoConnect())
  {
    Serial.println("failed to connect and hit timeout");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(1000);
  }

  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");

  mqttClient.setCallback(mqtt_callback);

  server.on("/", handleRoot);        // Call the 'handleRoot' function when a client requests URI "/"
  server.on("/on", handleOn); 
  server.on("/off", handleOff); 
  server.on("/state", handleState);


  server.onNotFound(handleNotFound); // When a client requests an unknown URI (i.e. something other than "/"), call function "handleNotFound"

  server.begin(); // Actually start the server
  Serial.println("HTTP server started");
}

void loop()
{
  server.handleClient(); // Listen for HTTP requests from clients

  if (!mqttClient.connected())
  {
    reconnect();
  }
  mqttClient.loop();


  if (millis()-pumpStarted>waitTimeMillis && pumpActive) {
    deactivatePump();
    waitTimeMillis = PUMP_ACTIVE_MILLIS;
  }
}

void reconnect()
{
  // Loop until we're reconnected
  while (!mqttClient.connected())
  {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_CLIENT_USER, MQTT_CLIENT_PW,
                           MQTT_LAST_WILL_TOPIC, 1, true, MQTT_LAST_WILL_MSG))
    {
      Serial.println("INFO: connected");

      if (pumpActive) {
        mqttClient.publish(MQTT_STATE_TOPIC, MQTT_ON);
      } else {
        mqttClient.publish(MQTT_STATE_TOPIC, MQTT_OFF);
      }

      // Signal online to Home Assistant availability tracker
      mqttClient.publish(MQTT_LAST_WILL_TOPIC, "online");

      mqttClient.subscribe(MQTT_TF_TOPIC);
    } else
    {
      Serial.print("ERROR: failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void mqtt_callback(char* inTopic, byte* payload, unsigned int length){
  Serial.println("Got RF data");
  Serial.write(payload, length);
  Serial.println();
  
  // convert the byte non terminated payload to string:
  char pltext[length+1];
  strncpy(pltext, (char*)payload, length);
  pltext[length]='\0';
  // convert payload string to int:
  if (String(pltext)==MQTT_ON) {
    activatePump();
  } else if (String(pltext)==MQTT_OFF) {
    deactivatePump();
  } else {
    long pumpLength = atol(pltext);
    if (pumpLength > 0) {
      waitTimeMillis = pumpLength;
      activatePump();
    } else {
      Serial.println("mqtt payload unknown!");
    }
    
  }

  Serial.println("RF data sent");
}

void handleRoot()
{
  server.send(200, "text/html", index_html);
}

void activatePump() {
  pumpStarted = millis();
  digitalWrite(RelaisPin, R_ON);
  Serial.println("Pump activated");
  pumpActive = true;
}

void deactivatePump() {
  digitalWrite(RelaisPin, R_OFF);
  Serial.println("Pump deactivated");
  pumpActive = false;
}

void handleOn()
{
  activatePump();
  server.send(200, "text/plain", "on");
  mqttClient.publish(MQTT_STATE_TOPIC, MQTT_ON);
}

void handleOff()
{
  deactivatePump();
  server.send(200, "text/plain", "off");
  mqttClient.publish(MQTT_STATE_TOPIC, MQTT_OFF);
}

void handleState()
{
  String sstate = "off";
  if (pumpActive) {
    sstate = "on";
  }

  server.send(200,"text/plain", sstate);
}

void handleNotFound()
{
  server.send(404, "text/plain", "404: Not found"); // Send HTTP status 404 (Not Found) when there's no handler for the URI in the request
}
