#include "HUSKYLENS.h"
#include "Wire.h"
HUSKYLENS huskylens;

#include <WiFi.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include "SPIFFS.h"

AsyncWebServer server(80);

String savePath = "";
bool rebooting = false;

const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";

String ssid;
String pass;
String WIFiState;
String ip;
String gateway;
String ChangedAlgorithm = "";

const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* param_Coordinates_path = "Coordinates_path";
const char* param_Area_path = "Area_path";

int buildinLED = 2;
const int maxTags = 4;

int selectedArea = 0;

IPAddress localIP;
IPAddress localGateway;
IPAddress subnet(255, 255, 0, 0);

unsigned long previousMillis = 0;
const long interval = 10000;

unsigned long tagOutOfViewStartTime[maxTags] = { 0 };
unsigned long timeOutOfView[maxTags] = { 0 };
const unsigned long tagCooldownDuration = 20000;
bool tagDetected[maxTags] = { false };


unsigned long timeOutOfViewForTag[maxTags] = { -1 };
bool tagPreviouslyDetected[maxTags] = { false };

const int maxInsects = 4;
bool InsectDetected[maxInsects] = {false};
unsigned long lastProcessTime[maxInsects] = {0};
const unsigned long InsectCooldownDuration = 20000;

bool object = false;
bool tag = true;

// Set LED GPIO
const int ledPin = 4;
// Stores LED state
String ledState;

void initSPIFFS() {
  if (!SPIFFS.begin(true)) {
    Serial.println("An error has occurred while mounting SPIFFS");
  }
  Serial.println("SPIFFS mounted successfully");
}

void setNewName(String newname, uint8_t ID) {
  while (!huskylens.setCustomName(newname, ID)) {
    Serial.println(F("Custom name failed!"));
    delay(100);
  }
}

void SendData(int tagID, unsigned long TagTimeOutOfView) {
  File file = SPIFFS.open("/SavedCoordinates.txt", "r");
  if (!file) {
    Serial.println("Error opening file");
    return;
  }
  String coordinates = file.readString();
  coordinates.trim();
  Serial.println(coordinates);
  file.close();

  int tijd = TagTimeOutOfView;

  Serial.println("Sending data...");

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    
    // change the "YourIPaddress" to your PC's IP address
    String serverName = String("http://"YourIPaddress"/hornet.php?hoornaar=1") + "&ident=" + String(tagID) + "&locatie=" + String(coordinates) + "&tijd=" + String(tijd) + "&reserve=1";
    http.begin(client, serverName);
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    String httpRequestData = serverName;
    Serial.print("httpRequestData: ");
    Serial.println(httpRequestData);

    int httpResponseCode = http.POST(httpRequestData);
    //int httpResponseCode = http.POST("www.google.com");
    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
    } else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  } else {
    Serial.println("WiFi Disconnected");
    WiFi.begin(ssid.c_str(), pass.c_str());
  }
}

void SendDataInsect(int insectID) {
  Serial.println("Sending data...");


  File file = SPIFFS.open("/SavedCoordinates.txt", "r");
  if (!file) {
    Serial.println("Error opening file");
    return;
  }
  String coordinates = file.readString();
  coordinates.trim();
  Serial.println(coordinates);
  file.close();

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;

    // change the "YourIPaddress" to your PC's IP address
    String serverName = String("http://"YourIPaddress"/hornet.php?hoornaar=1") + "&ident=" + String(insectID) + "&locatie=" + String(coordinates) + "&tijd=20" + "&reserve=2";
    http.begin(client, serverName);
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    String httpRequestData = serverName;
    Serial.print("httpRequestData: ");
    Serial.println(httpRequestData);

    int httpResponseCode = http.POST(httpRequestData);
    //int httpResponseCode = http.POST("www.google.com");
    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
    } else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  } else {
    Serial.println("WiFi Disconnected");
    WiFi.begin(ssid.c_str(), pass.c_str());
  }
}

void setup() {
  Serial.begin(115200);
  initSPIFFS();

  ssid = readFile(SPIFFS, ssidPath);
  pass = readFile(SPIFFS, passPath);

  if (initWiFi()) {
    IdohaveWIFI();
    server.begin();
  } else {
    Serial.println("Setting AP (Access Point)");
    WiFi.softAP("Hornets", NULL);
    IPAddress IP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(IP);
    IdonthaveWIFI();
    server.begin();
  }

  Wire.begin();
  while (!huskylens.begin(Wire)) {
    Serial.println(F("Begin failed!"));
    Serial.println(F("1.Please recheck the \"Protocol Type\" in HUSKYLENS (General Settings>>Protocol Type>>I2C)"));
    Serial.println(F("2.Please recheck the connection."));
    delay(100);
  }

  delay(100);
  setNewName("Tag1", 1);
  setNewName("Tag2", 2);
  setNewName("Tag3", 3);
  setNewName("Tag4", 4);
  pinMode(buildinLED, OUTPUT);
  huskylens.clearCustomText();
  delay(1000);
  String ip = WiFi.localIP().toString();
  huskylens.customText(ip, 20, 30);

  for (int i = 0; i < maxTags; i++) {
    tagDetected[i] = false; // Initially set all tags to 'not detected.'
  }

  for (int i = 0; i < maxInsects; i++) {
    InsectDetected[i] = false; // Initially set all insects to 'not detected.'
  }


}



void loop() {
  unsigned long currentTime = millis();

  if (tag == true) { 
    for (int tagID = 1; tagID <= maxTags; tagID++) {
      if (huskylens.request() && huskylens.requestBlocks(tagID)) {
        if (huskylens.count(tagID) > 0) {
          if (tagOutOfViewStartTime[tagID - 1] != 0) {
            // Calculate time the tag has been out of view
            timeOutOfView[tagID - 1] = currentTime - tagOutOfViewStartTime[tagID - 1];

            // Check if time out of view exceeds cooldown duration
            if (timeOutOfView[tagID - 1] >= tagCooldownDuration) {

              // Print message indicating how long the tag was out of view in seconds
              Serial.print("Tag ");
              Serial.print(tagID);
              Serial.print(" was ");
              Serial.print(((timeOutOfView[tagID - 1] + 0) / 1000)); // Time in seconds
              Serial.println(" Seconds out of view.");


              // Open a file and read an area value from it
              File file2 = SPIFFS.open("/SavedArea.txt", "r");
              if (!file2) {
                Serial.println("Error opening file");
                return;
              }
              String areaString = file2.readString();
              areaString.trim(); // Remove any whitespace at the beginning and end of the string.

              // Convert the area value to an integer
              int area = areaString.toInt();
              Serial.println(area);
              file2.close();

              // Calculate TagTimeOutOfView based on time out of view and area value
              unsigned long TagTimeOutOfView = (((timeOutOfView[tagID - 1] + 0) * 1.566) / 1000) + area;
              Serial.println(TagTimeOutOfView);

              SendData(tagID, TagTimeOutOfView);
            }

            // Reset the time out of view when the tag comes back into view.
            tagOutOfViewStartTime[tagID - 1] = 0;
            timeOutOfView[tagID - 1] = 0; 
          }
        } else {
          // Handle when the tag is not detected
          if (!tagPreviouslyDetected[tagID - 1]) {
            // If the tag has not been detected before, ignore the time out of view.
            continue;
          }
          // Set the start time for time out of view if it's not already set
          if (tagOutOfViewStartTime[tagID - 1] == 0) {
            tagOutOfViewStartTime[tagID - 1] = currentTime;
          }
        }
        // Mark the tag as previously detected.
        tagPreviouslyDetected[tagID - 1] = true;
      }
      
      // Check again for the tag and its detection status
      if (huskylens.request() && huskylens.requestBlocks(tagID)) {
        if (huskylens.count(tagID) > 0) {
          // Handle when a new tag is detected
          if (!tagDetected[tagID - 1]) {
            tagDetected[tagID - 1] = true;
            Serial.print("New tag detected: ");
            Serial.println(tagID);
          }
        } else {
          // Handle when a tag is no longer in view
          if (tagDetected[tagID - 1]) {
            tagDetected[tagID - 1] = false;
            Serial.print("Tag ");
            Serial.print(tagID);
            Serial.println(" is not in view.");
          }
        }
      }
    }
  }

  if (object ==  true) {

    for (int insectID = 1; insectID <= maxInsects; insectID++) {
      if (huskylens.request() && huskylens.requestBlocks(insectID)) {
        if (huskylens.count(insectID) > 0) {
          // Insect is in view
          unsigned long currentTime = millis();

          // Check if the insect is newly detected or cooldown time has elapsed
          if (!InsectDetected[insectID - 1] || (currentTime - lastProcessTime[insectID - 1] >= InsectCooldownDuration)) {
            // Print a message indicating a new insect detection
            Serial.print("New insect detected: ");
            Serial.println(insectID);

            SendDataInsect(insectID);  // Call the SendDataInsect function.
            InsectDetected[insectID - 1] = true;  // Mark the insect as detected.
            lastProcessTime[insectID - 1] = currentTime; // Update the time of the last processed insect.
          }
        }
      } else {
        // Insect is not in view.
        InsectDetected[insectID - 1] = false;  // Reset the detected flag if the insect is not in view.
      }
    }

  }
}
