#define BLYNK_TEMPLATE_ID "TMPL6afC2bef1"
#define BLYNK_TEMPLATE_NAME "LOCATION"
#define BLYNK_AUTH_TOKEN "zD0wjWKI0NX-scjllw3O6OhYktX9Rgx5"
#define BLYNK_PRINT Serial

#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <math.h>
#include <WiFiClientSecure.h>

SoftwareSerial ss(16, 17);
TinyGPSPlus gps;

WidgetMap myMap(V0);

double lat1;
double lon1;
double lat2;
double lon2;
double distance;
bool updateLocation = true;
int locationIndex = 0;
bool initialDataReceived = false;
double value8;
double value9;
double value10;
double value11;
String value12;

const char* host = "script.google.com";
//const char* urll = "http://script.google.com"
String DIS_ID = "AKfycbxxp0zzEemC02YY94FMjsk050idLwL5VqDd25L_tqA8SHc5j0pDxvPn-X8qeFnfOm0";

const char* openElevationAPI = "https://api.open-elevation.com/api/v1/lookup";

void sendElevationRequest(float lat, float lon) {
  HTTPClient http;

  String url = String(openElevationAPI) + "?locations=" + String(lat, 6) + "," + String(lon, 6);

  if (http.begin(url)) {
    int httpCode = http.GET();

    if (httpCode == HTTP_CODE_OK) {
    Serial.println("HTTP request successful");

      DynamicJsonDocument doc(1024);

      if (deserializeJson(doc, http.getString()) == DeserializationError::Ok) {
        float elevation = doc["results"][0]["elevation"];
        Serial.println("Elevation: " + String(elevation));
      } else {
        Serial.println("Failed to parse JSON");
      }
    } else {
      Serial.println("HTTP request failed with error code: " + String(httpCode));
    }

    http.end();
  } else {
    Serial.println("Unable to connect to the API");
  }
}

BLYNK_CONNECTED() {
  if (!initialDataReceived) {
    Blynk.syncVirtual(V1, V2, V8, V9, V10, V11, V12);
    initialDataReceived = true;
  }
}

BLYNK_WRITE(V1) {
  lat1 = param.asDouble();
}

BLYNK_WRITE(V2) {
  lon1 = param.asDouble();
}

BLYNK_WRITE(V8) {
  value8 = param.asDouble();
  Serial.print("Value from V8: ");
  Serial.println(value8, 2);
}

BLYNK_WRITE(V9) {
  value9 = param.asDouble();
  Serial.print("Value from V9: ");
  Serial.println(value9, 2);
}

BLYNK_WRITE(V10) {
  value10 = param.asDouble();
  Serial.print("Value from V10: ");
  Serial.println(value10, 2);
}

BLYNK_WRITE(V11) {
  value11 = param.asDouble();
  Serial.print("Value from V11: ");
  Serial.println(value11, 2);
}

BLYNK_WRITE(V12) {
  value12 = param.asStr();
  Serial.print("Value from V12: ");
  Serial.println(value12);
}

BLYNK_WRITE(V3) {
  int switchStateValue = param.asInt();

  if (switchStateValue == 1) {
    String latStr = String(lat2, 6);
    String lonStr = String(lon2, 6);

    String locationName = "LoSpot-" + String(locationIndex);
    Blynk.virtualWrite(V0, String(param.asStr()) + " - " + locationName);
    myMap.location(locationIndex, lat2, lon2, locationName);
    locationIndex++;

    WiFiClientSecure client;
    // Use the right port for HTTPS
    const int httpsPort = 443;

    // Use HTTPS
    if (client.connect(host, httpsPort)) {
      String url = "/macros/s/" + DIS_ID + "/exec";
    
      // Prepare data payload
      String payload = "value1=" + String(lat1, 6) +
                       "&value2=" + String(lon1, 6) +
                       "&value3=" + String(lat2, 6) +
                       "&value4=" + String(lon2, 6) +
                       "&value5=" + String(distance) +
                       "&value6=" + locationName +
                       "&value7=" + String(value8, 2) +
                       "&value8=" + String(value9, 2) +
                       "&value9=" + String(value10, 2) +
                       "&value10=" + String(value11, 2) +
                       "&value11=" + String(value12);

      // Send HTTPS POST request
      client.print(String("POST ") + url + " HTTP/1.1\r\n" +
                   "Host: " + host + "\r\n" +
                   "Content-Type: application/x-www-form-urlencoded\r\n" +
                   "Content-Length: " + payload.length() + "\r\n" +
                   "Connection: close\r\n\r\n" +
                   payload);

      // Wait for the server's response
      delay(500);

      while (client.available()) {
        String line = client.readStringUntil('\r');
        Serial.print(line);
      }

      client.stop();
    } else {
      Serial.println("Unable to connect to the server");
    }

    delay(1000);
  }
}



BLYNK_WRITE(V7) {
  int switchStateValue = param.asInt();

  if (switchStateValue == 1) {
    lat1 = gps.location.lat();
    lon1 = gps.location.lng();

    String latStr = String(lat1, 6);
    String lonStr = String(lon1, 6);

    String locationName = "LoInit-" + String(locationIndex);
    Blynk.virtualWrite(V0, String(param.asStr()) + " - " + locationName);
    myMap.location(locationIndex, lat1, lon1, locationName);
    locationIndex++;
    Blynk.virtualWrite(V1, lat1);
    Blynk.virtualWrite(V2, lon1);

   
}}

void restartD1Mini() {
  ESP.restart();  /*ESP restart function*/
}

BLYNK_WRITE(V21) {
  int buttonState = param.asInt();
  if (buttonState == 1) {
    restartD1Mini();
  }
}

void setup() {
  Serial.begin(9600);
  ss.begin(9600);
  WiFi.begin("Wokwi-GUEST");

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting...");
  }

  Blynk.config(BLYNK_AUTH_TOKEN);
}

void loop() {
  while (ss.available() > 0) {
    gps.encode(ss.read());
    if (gps.location.isUpdated()) {
      lat2 = gps.location.lat();
      lon2 = gps.location.lng();
      if (updateLocation) {
        Blynk.virtualWrite(V4, lat2);
        Blynk.virtualWrite(V5, lon2);

        distance = calculateDistance(lat1, lon1, lat2, lon2);
        Blynk.virtualWrite(V6, distance);

        sendElevationRequest(lat2, lon2);

        delay(500);
      }
    }
  }

  Blynk.run();
}

double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
  double earthRadiusKm = 6371.0;
  double dLat = (lat2 - lat1) * (M_PI / 180.0);
  double dLon = (lon2 - lon1) * (M_PI / 180.0);
  double a = sin(dLat / 2) * sin(dLat / 2) + cos(lat1 * (M_PI / 180.0)) * cos(lat2 * (M_PI / 180.0)) * sin(dLon / 2) * sin(dLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = earthRadiusKm * c * 1000;
  return distance;
}
GPS FakeBreakout