// includes
#include <SoftwareSerial.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <ESP_Mail_Client.h>
#include <BlynkSimpleEsp32.h>
#include <TinyGPSPlus.h>
#include <LiquidCrystal_I2C.h>
// defines............//

#define WIFI_SSID "use your wifi ssid"
#define WIFI_PASSWORD "use your wifi ssid"
#define BLYNK_TEMPLATE_ID "use your info"        //blynk
#define BLYNK_TEMPLATE_NAME "use your info"      //blynk
#define BLYNK_AUTH_TOKEN "use your info"         //blynk
const char* telegramBotToken = "use your info";  //telegram
const char* chatId = "use your info";            //telegram
#define AUTHOR_EMAIL "use your info"             // The sign in credentials  for email
#define AUTHOR_PASSWORD "use your info"          // The sign in credentials  for email
/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com */
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465
/* Recipient's email*/
#define RECIPIENT_EMAIL "use your info"

// pins...//

const int inputPin = 15;         // Sensor pin
SoftwareSerial mySerial(5, 18);  // for sim800L  Connect SIM800L module Rx to Pin 3 (Tx) of Arduino & Tx to Pin 2 (Rx) of Arduino
LiquidCrystal_I2C lcd(0x27, 16, 2);
/* Declare the global used SMTPSession object for SMTP transport */
SMTPSession smtp;
TinyGPSPlus gps;
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  Serial2.begin(9600);  //for gps
  Serial.println("Initializing...");
  Serial.println();
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(300);
  }
  Serial.println();
  Serial.print("Connected with IP: ");
  Serial.println(WiFi.localIP());
  Serial.println();
  Blynk.begin(BLYNK_AUTH_TOKEN, WIFI_SSID, WIFI_PASSWORD);
  pinMode(inputPin, INPUT);
  delay(9000);
  lcd.init();
  lcd.clear();
  lcd.backlight();
  lcd.setCursor(2, 0);
  lcd.print("ALL OK");
  pinMode(13, OUTPUT);
}


void loop() {
  Blynk.run();
  int inputValue = digitalRead(inputPin);

  if (inputValue == LOW) {  // low means fire found
    lcd.clear();
    lcd.setCursor(2, 0);  //Set cursor to character 2 on line 0
    lcd.print("Fire detected");
    while (Serial2.available() > 0)
      if (gps.encode(Serial2.read())) {
        gpsdatalink();
        digitalWrite(13, HIGH);  //buzzer
        lcd.clear();
        lcd.setCursor(2, 0);  //Set cursor to character 2 on line 0
        lcd.print("sending Alert!");
        delay(1000);
      }
    if (millis() > 5000 && gps.charsProcessed() < 10) {
      Serial.println(F("No GPS detected: check wiring."));
      while (true)
        ;
    }
    Blynk.virtualWrite(V0, 1);  // Turn on the virtual LED widget
    delay(1000);
    lcd.clear();
    lcd.setCursor(2, 0);  //Set cursor to character 2 on line 0
    lcd.print("Email Sent");
    delay(100);
    lcd.setCursor(2, 1);
    lcd.print("SMS Sent");
    delay(100);
    lcd.clear();
    lcd.setCursor(2, 0);
    lcd.print("Telegram sent");
    delay(100);
    lcd.clear();
    Serial.println("Status:B_LED ON");
  } else {  // fire is not found
    Serial.println("Input pin is HIGH");
    Blynk.virtualWrite(V0, 0);  // Turn off the virtual LED widget
    Serial.println("Status: OFF");
    digitalWrite(13, LOW);  //buzzer
  }
}



void gpsdatalink() {
  String mapLink;
  if (gps.location.isValid()) {
    String lat = String(gps.location.lat(), 6);
    String lng = String(gps.location.lng(), 6);
    // Create the Google Maps link
    mapLink = "Fire is detected !!!! @ https://www.google.com/maps/place/" + lat + "," + lng;

    // Send the Google Maps link via Telegram
    email(mapLink);
    sendTelegramMessage(mapLink);
    sms(mapLink);
    Serial.println();
    delay(1000);
  }

  else {
    Serial.println(F("INVALID"));
  }
}


void sendTelegramMessage(const String& message) {
  // Create the URL with the required parameters
  String url = "https://api.telegram.org/bot";
  url += telegramBotToken;
  url += "/sendMessage?chat_id=";
  url += chatId;
  url += "&text=";
  url += urlEncode(message);

  // Send the GET request
  HTTPClient http;
  http.begin(url);
  int httpResponseCode = http.GET();

  // Check the response
  if (httpResponseCode == HTTP_CODE_OK) {
    Serial.println("Telegram alert sent successfully");
  } else {
    Serial.print("Failed to send Telegram alert. Error code: ");
    Serial.println(httpResponseCode);
  }

  // Close the connection
  http.end();
}

String urlEncode(const String& str) {
  String encodedString = "";
  char c;
  char code0;
  char code1;
  const char* strchar = str.c_str();

  for (int i = 0; i < str.length(); i++) {
    c = strchar[i];

    if (c == ' ') {
      encodedString += '+';
    } else if (isalnum(c)) {
      encodedString += c;
    } else {
      code1 = (c & 0xf) + '0';
      if ((c & 0xf) > 9) {
        code1 = (c & 0xf) - 10 + 'A';
      }

      c = (c >> 4) & 0xf;
      code0 = c + '0';
      if (c > 9) {
        code0 = c - 10 + 'A';
      }

      encodedString += '%';
      encodedString += code0;
      encodedString += code1;
    }
    yield();
  }

  return encodedString;
}




/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status) {
  /* Print the current status */
  Serial.println(status.info());

  /* Print the sending result */
  if (status.success()) {
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Message sent failed: %d\n", status.failedCount());
    Serial.println("----------------\n");

    for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);
      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %s\n", MailClient.Time.getDateTimeString(result.timestamp, "%B %d, %Y %H:%M:%S").c_str());
      ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
      ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
    }
    Serial.println("----------------\n");
    // You need to clear sending result as the memory usage will grow up.
    smtp.sendingResult.clear();
  }
}

void email(const String& msg) {
  /*  Set the network reconnection option */
  MailClient.networkReconnect(true);
  smtp.debug(1);

  /* Set the callback function to get the sending results */
  smtp.callback(smtpCallback);

  /* Declare the Session_Config for user defined session credentials */
  Session_Config config;

  /* Set the session config */
  config.server.host_name = SMTP_HOST;
  config.server.port = SMTP_PORT;
  config.login.email = AUTHOR_EMAIL;
  config.login.password = AUTHOR_PASSWORD;
  config.login.user_domain = "";

  config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
  config.time.gmt_offset = 6;
  config.time.day_light_offset = 0;

  /* Declare the message class */
  SMTP_Message message;

  /* Set the message headers */
  message.sender.name = F("ESP");
  message.sender.email = AUTHOR_EMAIL;
  message.subject = F("Fire is detected !!!!");
  message.addRecipient(F("ESP32"), RECIPIENT_EMAIL);


  //Send raw text message
  String textMsg = msg;
  message.text.content = textMsg.c_str();
  message.text.charSet = "us-ascii";
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
  message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;


  /* Connect to the server */
  if (!smtp.connect(&config)) {
    ESP_MAIL_PRINTF("Connection error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
    return;
  }

  if (!smtp.isLoggedIn()) {
    Serial.println("\nNot yet logged in.");
  } else {
    if (smtp.isAuthenticated())
      Serial.println("\nSuccessfully logged in.");
    else
      Serial.println("\nConnected with no Auth.");
  }

  /* Start sending Email and close the session */
  if (!MailClient.sendMail(&smtp, &message))
    ESP_MAIL_PRINTF("Error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
}






// For data transmission from Serial to Software Serial port & vice versa
void updateSerial() {
  delay(500);
  while (Serial.available()) {
    mySerial.write(Serial.read());  //Forward what Serial received to Software Serial Port
  }

  while (mySerial.available()) {
    Serial.write(mySerial.read());  //Forward what Software Serial received to Serial Port
  }
}



void sms(const String& messageContent) {
  // Send attention command to check if all fine, it returns OK
  mySerial.println("AT");
  updateSerial();

  // Configuring module in TEXT mode
  mySerial.println("AT+CMGF=1");
  updateSerial();
  mySerial.println("AT+CMGS=\"use your info\"");  // 1)
  updateSerial();
  Serial.println("1");
  mySerial.print(messageContent);  //text content
  updateSerial();
  Serial.println("2");
  mySerial.write(26);  // 3)
}