#include "WiFi.h"
#include "AsyncUDP.h"
#include "ArduinoJson.h"
#include "WebServer.h"
#include "FirebaseESP32.h"

const char * ssid = "SSID";
const char * password = "PASSWORD";

AsyncUDP udp;
IPAddress IPadresa;
WebServer server(3388);

const unsigned char* DiscoveryPacket = reinterpret_cast<const unsigned char *>( "HelloLocalHomeSDK" );
const char * deviceId = "rgblight123";

int LED = 26;
int LED_OnOff = 25;
int LED_R = 17;
int LED_G = 16;
int LED_B = 27;

/*********** Domaci spinac ************/

int button = 12;
bool vypinacDoma = false;
bool stavTlacitka = false;
bool posledniStavTlacitka = false;

/************* FIREBASE *************/

#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"

#define API_KEY "API_KEY"     // pro anonymního uživatele
#define DATABASE_URL "https://<project_id>-default-rtdb.europe-west1.firebasedatabase.app/"

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

unsigned long prevTime = 0;
unsigned long prevTimeWiFiControl = 0;
unsigned long tim = 0;
bool bool_OnOff = false;
bool bool_OnOffPrev = false;
int int_RGB_all = 0;
int int_RGB_allPrev = 0;

uint16_t hex_RGB[3] = {0,0,0};
bool signupOK = false;

bool zmenaLocal_OnOff = 0;
bool zmenaLocal_RGB = 0;
bool zmenaVypinac_OnOff = 0;
bool zmenaFirebase_OnOff = 0;
bool zmenaFirebase_RGB = 0;

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

  pinMode(LED, OUTPUT);
  pinMode(LED_OnOff, OUTPUT);

  ledcAttachPin(LED_R, 1); // assign RGB led pins to channels
  ledcAttachPin(LED_G, 2);
  ledcAttachPin(LED_B, 3);

  ledcSetup(1, 8000, 8); // 8 kHz PWM, 8-bit resolution
  ledcSetup(2, 8000, 8);
  ledcSetup(3, 8000, 8);

  digitalWrite(LED, LOW);
  digitalWrite(LED_OnOff, LOW);

  pinMode(button, INPUT_PULLUP);    
    
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("WiFi Failed");
    while(1) {
      delay(1000);
    }
  }

  firebaseLogin();
  udpLoop();

  server.on("/", HTTP_ANY, handleSmartDevice); // HTTP_ANY je POST i GET
  server.begin();
  Serial.println("SERVER BEGIN!!");
    
}

void loop()
{
/************** Kontrola pripojeni a reconnect ****************/
  tim = millis();
  if ((WiFi.status() != WL_CONNECTED) && (tim - prevTimeWiFiControl > 10000)) {
    Serial.println("Reconnecting to WiFi");
    WiFi.disconnect();
    WiFi.reconnect();
    prevTimeWiFiControl = tim;
  }

/************** Domaci spinac ***************/  
    stavTlacitka = digitalRead(button);
    stav_tlacitka(stavTlacitka, posledniStavTlacitka, vypinacDoma);

    if (vypinacDoma != bool_OnOff){
      zmenaVypinac_OnOff = true;
      bool_OnOff = vypinacDoma;
      Serial.println("Vypinac OnOff zmena");
    }

    server.handleClient();
    delay(20);

/************** Firebase ***************/
  if(zmenaLocal_OnOff == false && zmenaLocal_RGB == false && zmenaVypinac_OnOff == false){
    if (Firebase.ready() && ((tim - prevTime > 500) || prevTime == 0)) {
      prevTime = tim;
      delay(20);
      if (Firebase.RTDB.getJSON(&fbdo, "/rgblight")){
  
        StaticJsonDocument < 200 > doc;
        deserializeJson(doc, fbdo.jsonString());
          
        bool_OnOff = doc["OnOff"]["on"];
        int_RGB_all = doc["ColorSetting"]["color"]["spectrumRGB"];

      /************** Zjisteni presne zmeny ***************/
        if (bool_OnOff != bool_OnOffPrev){
          zmenaFirebase_OnOff = true;
          Serial.println("Firebase OnOff zmena");
        }
        if (int_RGB_all != int_RGB_allPrev){
          zmenaFirebase_RGB = true;
          Serial.println("Firebase RGB zmena");
        }
     
      } else {
        Serial.println("Error pri cteni z databaze");
        Serial.println(fbdo.errorReason());
      }   
    }}
    delay(20);
    /************** Zjisteni prikazu a vykonani prislusne cinnosti ***************/
    if(zmenaLocal_OnOff == true || zmenaFirebase_OnOff == true || zmenaVypinac_OnOff == true){
      ovladani_LED_RGB();
      Serial.println("Local nebo Firebase zmena v OnOff");
      if (zmenaLocal_OnOff == true || zmenaVypinac_OnOff == true){      // pokud je zmena v Local - update firebase
        Firebase.RTDB.setBool(&fbdo, "/rgblight/OnOff/on", bool_OnOff);
        Serial.println("Prepis OnOff do Firebase");
        }
      zmenaLocal_OnOff = false;
      zmenaFirebase_OnOff = false;
      zmenaVypinac_OnOff = false;
      Serial.println("");
    }
    if(zmenaLocal_RGB == true || zmenaFirebase_RGB == true){
      ovladani_LED_RGB();
      Serial.println("Local nebo Firebase zmena v RGB");
      if (zmenaLocal_RGB == true){        // pokud je zmena v Local - update firebase
        Firebase.RTDB.setInt(&fbdo, "/rgblight/ColorSetting/color/spectrumRGB", int_RGB_all);
        Serial.println("Prepis RGB do Firebase");
        }
      zmenaLocal_RGB = false;
      zmenaFirebase_RGB = false;
      Serial.println("");
    }
}

void udpLoop(){
  if(udp.listen(3311)) {
    //Serial.print("UDP Listening on IP: ");
    //Serial.println(WiFi.localIP());
    udp.onPacket([](AsyncUDPPacket packet) {
      Serial.print("Delka packetu: ");
      Serial.print(packet.length());
      Serial.print(" Data: ");
      Serial.write(packet.data(), packet.length());
      Serial.println();
    
      if (compare(packet.data(),DiscoveryPacket) == 0){
        digitalWrite(LED, HIGH);    // HIGH == probehlo IDENTIFY
        packet.printf(deviceId,packet.remotePort());
        Serial.print("Google Home IP a port: ");
        Serial.print(packet.remoteIP());
        Serial.print(":");
        Serial.print(packet.remotePort());
        Serial.println();
        }
      });
  }
}

/************** Pro porovnani discovery packetu ***************/
int compare(unsigned char * a, const unsigned char * b){
  int i;
  for(i=0;i<17;i++){
  if(a[i]!=b[i]){
    return 1;
    }
  }
  return 0;
}

void handleSmartDevice() {
    
  Serial.println("HTTP handle");
  Serial.println(server.arg("plain")); // vypsání přijaté zpráva
  StaticJsonDocument < 200 > doc;
  deserializeJson(doc, server.arg("plain"));   

  /************ Decoder zpravy ****************/
  if (server.arg("plain").indexOf("{""\"on\":") >= 0){
  bool_OnOff = doc["on"];}
  else if (server.arg("plain").indexOf("\"spectrumRGB\":") >= 0){
  int_RGB_all = doc["color"]["spectrumRGB"];
  }

 /************** Zjisteni presne zmeny ***************/
  if (bool_OnOff != bool_OnOffPrev){
    zmenaLocal_OnOff = true;
    Serial.println("Local OnOff zmena");
    }
  if (int_RGB_all != int_RGB_allPrev){
    zmenaLocal_RGB = true;
    Serial.println("Local RGB zmena");
    }

  server.send(200, "text/html", "OK");
}

void stav_tlacitka(bool tlacitko, bool &posledniStavTlacitka, bool &stavSvetla){

  if (tlacitko != posledniStavTlacitka) {
    if (tlacitko == false) {
      if(stavSvetla == false) stavSvetla=true; 
      else stavSvetla = false;
    }}
  posledniStavTlacitka = tlacitko;
}

void firebaseLogin(){

  config.api_key = API_KEY;
  config.database_url = DATABASE_URL;

  if (Firebase.signUp(&config, &auth, "", "")){
    Serial.println("Prihlaseni do Firebase");
  }
  else{
    Serial.println("CHYBA prihlaseni do Firebase");
    Serial.printf("%s\n", config.signer.signupError.message.c_str());
  }

  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
}

void ovladani_LED_RGB(){
  hex_RGB[1] = int_RGB_all & 0xFF;
  hex_RGB[2] = (int_RGB_all & 0xFF00) >> 8;
  hex_RGB[3] = int_RGB_all >> 16;

  if (bool_OnOff == true) {
    digitalWrite(LED_OnOff,HIGH);
    for (size_t i = 1; i < 4; i++)
    {
      ledcWrite(i,127+(128-hex_RGB[i]));  //common anode RGB
    }
  }
  else {
    digitalWrite(LED_OnOff,LOW);
    for (size_t i = 1; i < 4; i++) ledcWrite(i,0);
  }
  
  /********** Výpis při změně *************/

  if(bool_OnOff!=bool_OnOffPrev){
    Serial.print("OnOff: ");
    Serial.println(bool_OnOff);
    }

  if(int_RGB_all!=int_RGB_allPrev){
    Serial.print("spectrumRGB: ");
    Serial.println(int_RGB_all, HEX);
    }

  vypinacDoma = bool_OnOff;
  bool_OnOffPrev = bool_OnOff;
  int_RGB_allPrev = int_RGB_all;
} 
