#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// Set the LCD address for each display
LiquidCrystal_I2C lcdA(0x20, 16, 2); // Address 0x20 for Material A
LiquidCrystal_I2C lcdB(0x27, 16, 2); // Address 0x27 for Material B

// WiFi credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// Server IP Address
const char* server_ip = "localhost:5000";  // Update with your actual server IP

// Server URLs (use HTTPS)
const String SERVER_ADD_STOCK_URL = "http://" + String(server_ip) + "/api/add_stock";
const String SERVER_REMOVE_STOCK_URL = "http://" + String(server_ip) + "/api/remove_stock";
const String SERVER_GET_CITY_URL = "http://" + String(server_ip) + "/api/get_latest_city";

// Sensor pins
const int trigPinA = 5;
const int echoPinA = 17;
const int trigPinB = 16;
const int echoPinB = 4;

// LED pins
const int ledRedPinA = 15;
const int ledGreenPinA = 2;
const int ledYellowPinA = 13;
const int ledRedPinB = 23;
const int ledGreenPinB = 25;
const int ledYellowPinB = 26;

// Distance threshold
const int thresholdDistanceA = 200; // 200 cm
const int thresholdDistanceB = 150; // 150 cm (example threshold for Material B)

// Material details
const char* kodeA = "SF421015";
const char* materialNameA = "Material A";
String materialDateA = "2024-07-14"; // Example date

const char* kodeB = "SF421018";
const char* materialNameB = "Material B";
String materialDateB = "2024-07-14"; // Example date for Material B

// Variable to store the last processed distance
int lastProcessedDistanceA = -1;
int lastProcessedDistanceB = -1;

// Time intervals
unsigned long lastMeasurementTimeA = 0;
unsigned long lastMeasurementTimeB = 0;
const unsigned long measurementInterval = 5000; // 5 seconds

unsigned long lastDisplayUpdateTimeA = 0;
unsigned long lastDisplayUpdateTimeB = 0;
const unsigned long displayInterval = 2000; // 2 seconds
bool displayMaterialNameA = true;
bool displayMaterialNameB = true;

// Variables for blinking the yellow LED
unsigned long lastBlinkTimeA = 0;
unsigned long lastBlinkTimeB = 0;
const unsigned long blinkInterval = 200; // 200 ms
bool yellowLEDAState = false;
bool yellowLEDBState = false;

String latestCity = "";  // Variable to store the latest city

void setup() {
  Serial.begin(115200);
  Serial.println("Initializing LCDs...");

  // Initialize the LCDs
  lcdA.init();
  lcdA.backlight();
  lcdB.init();
  lcdB.backlight();

  // Print a message to the LCDs
  lcdA.setCursor(0, 0);
  lcdA.print("Smart Monitoring");
  lcdA.setCursor(0, 1);
  lcdA.print("Warehouse Stock");

  lcdB.setCursor(0, 0);
  lcdB.print("Smart Monitoring");
  lcdB.setCursor(0, 1);
  lcdB.print("Warehouse Stock");

  delay(2000); // Display initial message for 2 seconds

  // Display material names
  lcdA.clear();
  lcdA.setCursor(0, 0);
  lcdA.print(materialNameA);

  lcdB.clear();
  lcdB.setCursor(0, 0);
  lcdB.print(materialNameB);

  // Set up sensor pins
  pinMode(trigPinA, OUTPUT);
  pinMode(echoPinA, INPUT);
  pinMode(trigPinB, OUTPUT);
  pinMode(echoPinB, INPUT);

  // Set up LED pins
  pinMode(ledRedPinA, OUTPUT);
  pinMode(ledGreenPinA, OUTPUT);
  pinMode(ledYellowPinA, OUTPUT);
  pinMode(ledRedPinB, OUTPUT);
  pinMode(ledGreenPinB, OUTPUT);
  pinMode(ledYellowPinB, OUTPUT);

  // Connect to Wi-Fi
  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);

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

  Serial.println("\nConnected to WiFi");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Update LCDs
  lcdA.setCursor(0, 1);
  lcdA.print("WiFi Connected");
  lcdB.setCursor(0, 1);
  lcdB.print("WiFi Connected");

  delay(2000); // Display WiFi Connected for 2 seconds

  // Display material names
  lcdA.clear();
  lcdA.setCursor(0, 0);
  lcdA.print(materialNameA);

  lcdB.clear();
  lcdB.setCursor(0, 0);
  lcdB.print(materialNameB);

  // Get the latest city from the server
  getLatestCity();

  Serial.println("Setup completed.");
}

void loop() {
  // Get the current time
  unsigned long currentTime = millis();

  // Update LCD display for Material A
  if (currentTime - lastDisplayUpdateTimeA >= displayInterval) {
    lastDisplayUpdateTimeA = currentTime;
    lcdA.clear();
    lcdA.setCursor(0, 0);
    if (displayMaterialNameA) {
      lcdA.print(materialNameA);
    } else {
      lcdA.print(materialDateA);
    }
    displayMaterialNameA = !displayMaterialNameA;
  }

  // Update LCD display for Material B
  if (currentTime - lastDisplayUpdateTimeB >= displayInterval) {
    lastDisplayUpdateTimeB = currentTime;
    lcdB.clear();
    lcdB.setCursor(0, 0);
    if (displayMaterialNameB) {
      lcdB.print(materialNameB);
    } else {
      lcdB.print(materialDateB);
    }
    displayMaterialNameB = !displayMaterialNameB;
  }

  // Check if it's time to take a new measurement for Material A
  if (currentTime - lastMeasurementTimeA >= measurementInterval) {
    lastMeasurementTimeA = currentTime;
    processMaterial(trigPinA, echoPinA, lastProcessedDistanceA, materialNameA, materialDateA, ledRedPinA, ledGreenPinA, ledYellowPinA, kodeA, thresholdDistanceA, lcdA, lastBlinkTimeA, yellowLEDAState);
  }

  // Check if it's time to take a new measurement for Material B
  if (currentTime - lastMeasurementTimeB >= measurementInterval) {
    lastMeasurementTimeB = currentTime;
    processMaterial(trigPinB, echoPinB, lastProcessedDistanceB, materialNameB, materialDateB, ledRedPinB, ledGreenPinB, ledYellowPinB, kodeB, thresholdDistanceB, lcdB, lastBlinkTimeB, yellowLEDBState);
  }

  // Handle blinking yellow LED for Material A
  if (yellowLEDAState && (currentTime - lastBlinkTimeA >= blinkInterval)) {
    lastBlinkTimeA = currentTime;
    digitalWrite(ledYellowPinA, !digitalRead(ledYellowPinA));
  }

  // Handle blinking yellow LED for Material B
  if (yellowLEDBState && (currentTime - lastBlinkTimeB >= blinkInterval)) {
    lastBlinkTimeB = currentTime;
    digitalWrite(ledYellowPinB, !digitalRead(ledYellowPinB));
  }

  delay(100); // Short delay to allow other tasks to run
}

void processMaterial(int trigPin, int echoPin, int &lastProcessedDistance, const char* materialName, String materialDate, int ledRedPin, int ledGreenPin, int ledYellowPin, const char* kode, int thresholdDistance, LiquidCrystal_I2C &lcd, unsigned long &lastBlinkTime, bool &yellowLEDState) {
  Serial.println("Measuring distance...");
  int currentDistance = getDistance(trigPin, echoPin);

  // Only process if the distance has changed
  if (currentDistance != lastProcessedDistance) {
    Serial.println("Processing distance...");
    lcd.setCursor(0, 1);
    lcd.print("                "); // Clear the second line

    if (lastProcessedDistance >= 0) { // Ensure lastProcessedDistance is valid
      int quantityChange = abs(currentDistance - lastProcessedDistance) / 50 * 50;

      if (quantityChange > 0) { // Ensure quantityChange is not zero
        if (currentDistance > lastProcessedDistance) {
          // Distance increased, remove stock
          handleLED(ledRedPin);
          lcd.setCursor(0, 1);
          lcd.print("Removing Stock  ");
          Serial.println("Sending to server...");
          sendToServer(SERVER_REMOVE_STOCK_URL.c_str(), quantityChange, "out", kode, latestCity.c_str()); // Send the city name
        } else {
          // Distance decreased, add stock
          handleLED(ledGreenPin);
          lcd.setCursor(0, 1);
          lcd.print("Adding Stock    ");
          Serial.println("Sending to server...");
          sendToServer(SERVER_ADD_STOCK_URL.c_str(), quantityChange, "in", kode, latestCity.c_str()); // Send the city name
        }
      } else {
        lcd.setCursor(0, 1);
        lcd.print("-------      ");
      }
    } else {
      handleLED(ledYellowPin);
      lcd.setCursor(0, 1);
      lcd.print("No Transaction  ");
    }

    lastProcessedDistance = currentDistance;
    Serial.println("Distance processed.");
  }

  // Display stock condition
  if (currentDistance <= thresholdDistance) {
    digitalWrite(ledGreenPin, HIGH);
    digitalWrite(ledRedPin, LOW);
    yellowLEDState = false;
    digitalWrite(ledYellowPin, LOW);
    lcd.setCursor(0, 1);
    lcd.print("Stock Safe     ");
  } else {
    digitalWrite(ledRedPin, HIGH);
    digitalWrite(ledGreenPin, LOW);
    yellowLEDState = false;
    digitalWrite(ledYellowPin, LOW);
    lcd.setCursor(0, 1);
    lcd.print("Stock Limit      ");
  }

  // Blink yellow LED if expired
  if (materialDate == "2024-07-14") { // Example condition for expiration
    yellowLEDState = true;
    lastBlinkTime = millis();
  }
}

int getDistance(int trigPin, int echoPin) {
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);

  // Sets the trigPin on HIGH state for 10 microseconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  long duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  int distance = duration * 0.034 / 2;

  Serial.print("Distance: ");
  Serial.println(distance);

  return distance;
}

void sendToServer(const char* serverUrl, int quantity, String type, const char* kode, const char* kota) {
  Serial.print("Sending to server: ");
  Serial.println(serverUrl);

  if (WiFi.status() == WL_CONNECTED) {
    WiFiClientSecure client;
    client.setInsecure(); // Disable SSL certificate verification

    HTTPClient http;
    http.begin(client, serverUrl);

    http.addHeader("Content-Type", "application/json");

    String payload;
    payload = "{\"type\": \"" + type + "\", \"kode\": \"" + String(kode) + "\", \"quantity\": " + String(quantity) + ", \"kota\": \"" + String(kota) + "\"}";

    Serial.print("Payload: ");
    Serial.println(payload);

    int httpResponseCode = http.POST(payload);

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println("HTTP Response code: " + String(httpResponseCode));
      Serial.println("Response: " + response);
    } else {
      Serial.println("Error on sending POST: " + String(httpResponseCode));
      Serial.println(http.errorToString(httpResponseCode));
    }

    http.end();
  } else {
    Serial.println("WiFi Disconnected");
  }
}

void handleLED(int ledPin) {
  // Turn off all LEDs
  digitalWrite(ledRedPinA, LOW);
  digitalWrite(ledGreenPinA, LOW);
  digitalWrite(ledYellowPinA, LOW);
  digitalWrite(ledRedPinB, LOW);
  digitalWrite(ledGreenPinB, LOW);
  digitalWrite(ledYellowPinB, LOW);

  // Turn on the specified LED
  digitalWrite(ledPin, HIGH);
}

void getLatestCity() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(SERVER_GET_CITY_URL);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println("City fetched: " + response);

      StaticJsonDocument<200> doc;
      deserializeJson(doc, response);
      if (doc.containsKey("latest_city")) {
        latestCity = doc["latest_city"].as<String>();
        Serial.println("Latest city: " + latestCity);
      }
    } else {
      Serial.println("Error on HTTP request");
    }
    http.end();
  } else {
    Serial.println("WiFi Disconnected");
  }
}
$abcdeabcde151015202530354045505560fghijfghij
$abcdeabcde151015202530354045505560fghijfghij