/*
  ESP32 Single-Device MQTT Publisher & Subscriber Demo

  Concept:
  This ESP32 will act as BOTH a publisher and a subscriber using two tasks
  on its two different cores, demonstrating a full communication loop.

  - A task on Core 1 ("Publisher") will publish a message every 5 seconds
    to the topic "esp32/self_test".

  - A task on Core 0 ("MQTT Handler") will manage the MQTT connection.
    This task subscribes to "esp32/self_test" and listens for messages.

  - When the publisher sends a message, it travels to the broker on your PC
    and is immediately sent back to the ESP32, which triggers the 'callback'
    function to print the received message.
*/

#include <WiFi.h>
#include <PubSubClient.h>

// --- WiFi and MQTT Configuration ---
const char* ssid = "Sunrise_8635303";         // YOUR WIFI SSID
const char* password = "ztjpmang8htdusnL";     // YOUR WIFI PASSWORD
const char* mqtt_server = "192.168.1.33"; // IP address of your PC running the broker

const char* mqtt_topic = "esp32/self_test";
const char* clientID = "ESP32-DualRole-Demo-1"; // Must be unique on the network

WiFiClient espClient;
PubSubClient client(espClient);

// --- MQTT Callback Function ---
// This function is executed whenever a message is received on a subscribed topic.
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.println("-------------------------");
  Serial.print("<<< MESSAGE RECEIVED on topic: [");
  Serial.print(topic);
  Serial.println("] >>>");

  // Create a clean, null-terminated string from the payload
  char message[length + 1];
  memcpy(message, payload, length);
  message[length] = '\0';
  
  Serial.print("Message: ");
  Serial.println(message);
  Serial.println("-------------------------");
}

// --- Task 1: Publisher Task (Core 1) ---
void publisherTask(void *pvParameters) {
  Serial.print("Publisher Task running on core ");
  Serial.println(xPortGetCoreID());

  int msgCount = 0;
  while (true) {
    // Wait for 5 seconds before publishing
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    
    // Only publish if the client is actually connected to the broker
    if (client.connected()) {
      msgCount++;
      char msg[50];
      snprintf(msg, 50, "Self-test message, count: %d", msgCount);
      
      Serial.print("\n[Publisher Task] Publishing message: ");
      Serial.println(msg);
      
      client.publish(mqtt_topic, msg);
    } else {
      Serial.println("[Publisher Task] Cannot publish, MQTT client not connected.");
    }
  }
}

// --- Task 2: MQTT Handler Task (Core 0) ---
void mqttHandlerTask(void *pvParameters) {
  Serial.print("MQTT Handler Task running on core ");
  Serial.println(xPortGetCoreID());

  while (true) {
    // If not connected, attempt to reconnect
    if (!client.connected()) {
      // Loop until we're reconnected
      while (!client.connected()) {
        Serial.println("[MQTT Task] Attempting MQTT connection...");
        if (client.connect(clientID)) {
          Serial.println("[MQTT Task] ==> CONNECTED <==");
          // Once connected, subscribe to the topic
          client.subscribe(mqtt_topic);
          Serial.print("[MQTT Task] Subscribed to topic: ");
          Serial.println(mqtt_topic);
        } else {
          Serial.print("[MQTT Task] Connection failed, rc=");
          Serial.print(client.state());
          Serial.println(". Retrying in 5 seconds...");
          vTaskDelay(5000 / portTICK_PERIOD_MS); // Use vTaskDelay for non-blocking wait
        }
      }
    }
    
    // This is the most important line for PubSubClient.
    // It keeps the connection alive and checks for incoming messages.
    client.loop();

    // Briefly yield the CPU to other tasks on this core
    vTaskDelay(10 / portTICK_PERIOD_MS);
  }
}


void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to WiFi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("--- ESP32 Single-Device MQTT Demo ---");
  
  setup_wifi();
  
  client.setServer(mqtt_server, 1883);
  client.setCallback(mqttCallback); // Set the function to run when a message is received

  // Create the MQTT handler task on Core 0 (good for network comms)
  xTaskCreatePinnedToCore(
      mqttHandlerTask, "MQTTHandler", 8192, NULL, 1, NULL, 0);

  // Create the publisher task on Core 1 (for application logic)
  xTaskCreatePinnedToCore(
      publisherTask, "Publisher", 4096, NULL, 1, NULL, 1);
}

void loop() {
  // The main loop is empty. Everything is handled by our two tasks.
  vTaskDelay(portMAX_DELAY); // Sleep indefinitely
}