#include "WiFi.h"
#include <PubSubClient.h>
#include <ArduinoJson.h>
#define REDPIN 3
#define GREENPIN 4
#define BLUEPIN 5
#define COLD_WHITE 18
#define WARM_WHITE 19
#define FADESPEED 20     // make this higher to slow down
#define ID_Device "123666888"
const char* mqtt_server = "mqttvcloud.innoway.vn";
const char* mqttUser = "minhhk1";
const char* mqttPassword = "KXXlVA14vyvy5wbuMR5MOVOUPUUfSWxh";
WiFiClient espClient;
PubSubClient client(espClient);

int control_white = 0;
int control_rgb = 0;


void taskBlinkWhite(void *pvParameters) {
  for (;;) {
    if (control_white == 0) {
      analogWrite(WARM_WHITE, 256);
      vTaskDelay(pdMS_TO_TICKS(500)); // Thay thế delay(1000) bằng vTaskDelay
      analogWrite(WARM_WHITE, 0);
      vTaskDelay(pdMS_TO_TICKS(500)); // Thay thế delay(1000) bằng vTaskDelay
    }
    vTaskDelay(pdMS_TO_TICKS(50));
  }
}

void control_rgb_2(int red, int blue, int green){
  analogWrite(REDPIN, red);
  analogWrite(GREENPIN, blue);
  analogWrite(BLUEPIN, green);
}

void hanldeRGB(String messageTemp){
  int colonIndex = messageTemp.indexOf(':');
  String numbersString = messageTemp.substring(colonIndex + 1);
  int numbers[3];
  int count = 0;
  int commaIndex = 0;
  do {
      commaIndex = numbersString.indexOf(',');
      String numberString = numbersString.substring(0, commaIndex);
      numbers[count] = numberString.toInt();
      numbersString = numbersString.substring(commaIndex + 1);
      count++;
  } while (commaIndex != -1 && count < 3);
  control_rgb_2(numbers[0],numbers[1],numbers[2]);
  for (int i = 0; i < count; i++) {
      Serial.print("Number ");
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.println(numbers[i]);
  }
}

void taskBlinkRGB(void *pvParameters) {
  for (;;) {
    int r, g, b;
      if(control_rgb == 0){
        for (r = 0; r < 256; r++) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(REDPIN, r);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
      // fade from violet to red
        for (b = 255; b > 0; b--) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(BLUEPIN, b);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
      // fade from red to yellow
        for (g = 0; g < 256; g++) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(GREENPIN, g);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
      // fade from yellow to green
        for (r = 255; r > 0; r--) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(REDPIN, r);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
      // fade from green to teal
        for (b = 0; b < 256; b++) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(BLUEPIN, b);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
      // fade from teal to blue
        for (g = 255; g > 0; g--) {
            if(control_rgb == 1){
              break;
            }
            analogWrite(GREENPIN, g);
            vTaskDelay(pdMS_TO_TICKS(FADESPEED));
        }
    }
    vTaskDelay(pdMS_TO_TICKS(50));
  }
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();
  // Feel free to add more if statements to control more GPIOs with MQTT
  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  if (String(topic) == "message/control/white") {
    Serial.print("Changing output to ");
    if (messageTemp.indexOf("on") != -1){
      control_white = 1;
      Serial.println("on white");
      analogWrite(WARM_WHITE, 256);
      send_message_mqtt("white","on");
    }
    else if(messageTemp.indexOf("off") != -1){
      control_white = 1;
      Serial.println("off white");
      analogWrite(WARM_WHITE, 0);
      send_message_mqtt("white","off");
    }
    else if(messageTemp.indexOf("blink") != -1){
      control_white = 0;
      Serial.println("blink white");
      send_message_mqtt("white","blink");
    }
    else if(messageTemp.indexOf("dim") != -1){
      control_white = 1;
      int dimlevel = messageTemp.substring(messageTemp.indexOf(':') + 1).toInt();
      analogWrite(WARM_WHITE, int(dimlevel/100.0*255));
      send_message_mqtt("white","dim");
    }
  }
  else if(String(topic) == "message/control/rgb"){
      Serial.print("Changing output RGB");
      if (messageTemp.indexOf("on") != -1){
        control_rgb = 0;
        Serial.println("on rgb");
        send_message_mqtt("rgb","on");
      }
      else if(messageTemp.indexOf("off") != -1){
        control_rgb = 1;
        Serial.println("off rgb");
        send_message_mqtt("rgb","off");
        control_rgb_2(0,0,0);
      }
      else if(messageTemp.indexOf("auto") != -1){
        control_rgb = 0;
        send_message_mqtt("rgb","on");
        Serial.println("auto rgb");
      }
      else if(messageTemp.indexOf("manual") != -1){
        control_rgb = 1;
        send_message_mqtt("rgb","on");
        Serial.println("manual rgb");
        hanldeRGB(messageTemp);
      }
  }
  Serial.println("Exit Call back");
}

const char *ssid = "Wokwi-GUEST";
const char *password = "";
void setup() {
  Serial.begin(115200);
  pinMode(COLD_WHITE, OUTPUT);
  analogWrite(COLD_WHITE, 256);
  //Init WiFi as Station, start SmartConfig
  // WiFi.mode(WIFI_AP_STA);
  // WiFi.beginSmartConfig();
  WiFi.begin(ssid, password); // for simulation in Wokwi

  //Wait for SmartConfig packet from mobile
  // Serial.println("Waiting for SmartConfig.");
  // while (!WiFi.smartConfigDone()) {
  //   vTaskDelay(pdMS_TO_TICKS(500));
  //   Serial.print(".");
  // }

  // Serial.println("");
  // Serial.println("SmartConfig received.");

  //Wait for WiFi to connect to AP
  Serial.println("Waiting for WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    vTaskDelay(pdMS_TO_TICKS(500));
    Serial.print(".");
  }

  Serial.println("WiFi Connected.");

  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);
  pinMode(WARM_WHITE, OUTPUT);
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  xTaskCreate(taskBlinkWhite, "BlinkWhite", 1000, NULL, 1, NULL);
  xTaskCreate(taskBlinkRGB, "BlinkRGB", 1000, NULL, 1, NULL);
} 

void send_message_mqtt(String type, String status){
    StaticJsonDocument<100> jsonDocument;
    jsonDocument["id"] = ID_Device;
    jsonDocument["status"] = status;
    jsonDocument["type"] = type;
    char buffer[100];
    serializeJson(jsonDocument, buffer);
    client.publish("message/status/rgb", buffer);
}


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP8266Client", mqttUser, mqttPassword)) {
      analogWrite(COLD_WHITE, 0);
      control_rgb = 0;
      Serial.println("connected to broker");
      client.subscribe("message/control/white");
      client.subscribe("message/control/rgb");
      client.subscribe("message/response/rgb");
      send_message_mqtt("white","on");
      send_message_mqtt("rgb","on");
    } else {
      control_white = 1;
      if(control_rgb == 0){
        control_rgb = 1;
        control_rgb_2(0,0,0);
      }
      analogWrite(WARM_WHITE, 0);
      analogWrite(COLD_WHITE, 256);
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      vTaskDelay(pdMS_TO_TICKS(FADESPEED));
    }
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  vTaskDelay(pdMS_TO_TICKS(5));
}