#define DEBUG_WEBSOCKETS
#include <SocketIOclient.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>

#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <DNSServer.h>
#include <time.h>

/* Voltaj Modu Ayarlanıyor Cihazdaki Voltajları Ölçmek İçin */
ADC_MODE(ADC_VCC);

/**/
ESP8266WiFiMulti wifiMulti;
//DNSServer dnsServer;
ESP8266WebServer server(80);
/**/
const int KAPALI = 1;
const int ACIK = 0;
constexpr uint8_t RST_PIN = D4;  // Configurable, see typical pin layout above
constexpr uint8_t SS_PIN = D8;

const String cihaz_id = "FikoYT-Kart-Okuyucu-1";
const String uuid = "FikoYT-Uyar-Kart-Okuyucu-1";

String eski_uid = "";
/**/
MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance

/* Socketio Tanıtılıyor */
SocketIOclient socketIO;

/**/

/**/
void socketIOEvent(socketIOmessageType_t type, uint8_t *payload, size_t length) {
  switch (type) {
    case sIOtype_DISCONNECT:
      // Serial.printf("[IOc] Disconnected!\n");
      break;
    case sIOtype_CONNECT:
      // Serial.printf("[IOc] Connected to url: %s\n", payload);

      // join default namespace (no auto join in Socket.IO V3)
      socketIO.send(sIOtype_CONNECT, "/");
      yolla_baglandi();
      yolla_olay("FikoYT.uyar.devices.card.update", eski_uid);
      break;
    case sIOtype_EVENT:
      {
        char *sptr = NULL;
        int id = strtol((char *)payload, &sptr, 10);
        // Serial.printf("[IOc] get event: %s id: %d\n", payload, id);
        if (id) {
          payload = (uint8_t *)sptr;
        }
        DynamicJsonDocument doc(1024);
        DeserializationError error = deserializeJson(doc, payload, length);
        if (error) {
          // Serial.print(F("deserializeJson() failed: "));
          // Serial.println(error.c_str());
          return;
        }

        String eventName = doc[0];
        // Serial.printf("[IOc] event name: %s\n", eventName.c_str());

        // Message Includes a ID for a ACK (callback)
        if (id) {
          // creat JSON message for Socket.IO (ack)
          DynamicJsonDocument docOut(1024);
          JsonArray array = docOut.to<JsonArray>();

          // add payload (parameters) for the ack (callback function)
          JsonObject param1 = array.createNestedObject();
          param1["now"] = millis();

          // JSON to String (serializion)
          String output;
          output += id;
          serializeJson(docOut, output);

          // Send event
          socketIO.send(sIOtype_ACK, output);
        }
      }
      break;
    case sIOtype_ACK:
      // Serial.printf("[IOc] get ack: %u\n", length);
      break;
    case sIOtype_ERROR:
      // Serial.printf("[IOc] get error: %u\n", length);
      break;
    case sIOtype_BINARY_EVENT:
      // Serial.printf("[IOc] get binary: %u\n", length);
      break;
    case sIOtype_BINARY_ACK:
      // Serial.printf("[IOc] get binary ack: %u\n", length);
      break;
  }
}


void socketIOEvent1(socketIOmessageType_t type, uint8_t *payload, size_t length) {
  switch (type) {
    case sIOtype_DISCONNECT:
      {
        DynamicJsonDocument doc(4096);
        auto error = deserializeJson(doc, payload);
        if (error) {
          // Serial.print(F("deserializeJson() failed with code "));
          // Serial.println(error.c_str());
          return;
        }
        break;
      }
    case sIOtype_CONNECT:
      socketIO.send(sIOtype_CONNECT, "/uyar-dokum.merkez");
      yolla_baglandi();
      yolla_olay("FikoYT.uyar.devices.card.update", eski_uid);
      return;
      break;
    case sIOtype_EVENT:
      {
        DynamicJsonDocument doc(4096);
        auto error = deserializeJson(doc, payload);
        if (error) {
          // Serial.print(F("deserializeJson() failed with code "));
          // Serial.println(error.c_str());
          return;
        }
      }
      break;
    case sIOtype_ERROR:
      {
        DynamicJsonDocument doc(4096);
        auto error = deserializeJson(doc, payload);
        if (error) {
          // Serial.print(F("deserializeJson() failed with code "));
          // Serial.println(error.c_str());
          return;
        }
      }
      break;
  }
}

/**/
String getID() {
  unsigned long hex_num;
  hex_num = mfrc522.uid.uidByte[0] << 24;
  hex_num += mfrc522.uid.uidByte[1] << 16;
  hex_num += mfrc522.uid.uidByte[2] << 8;
  hex_num += mfrc522.uid.uidByte[3];
  return String(hex_num);
}

//*****************************************************************************************//

void sayfaAna(void) {
  String veri = "<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> <meta http-equiv=\"cache-control\" content=\"max-age=0\" /><meta http-equiv=\"cache-control\" content=\"no-cache\" /><meta http-equiv=\"expires\" content=\"0\" /><meta http-equiv=\"expires\" content=\"Tue, 01 Jan 1980 1:00:00 GMT\" /><meta http-equiv=\"pragma\" content=\"no-cache\" /><body>";
  veri += "<center><h1>";
  veri += "FikoYT Sistem";
  veri += "</h1></center>";
  veri += "<br>";
  veri += "Okunan Kart:";
  if (eski_uid)
    veri += eski_uid;

  veri += "<br>";

  veri += "</body></html>";

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

void event(socketIOmessageType_t type, uint8_t *payload, size_t length) {
  // Serial.printf("got message: %s\n", payload);
}

// WebSocket olaylarını yakalama
// void onEvent(WebsocketsEvent event, String data) {
//   if (event == WebsocketsEvent::ConnectionOpened) {
//     Serial.println("[INFO] Bağlantı açıldı!");
//   } else if (event == WebsocketsEvent::ConnectionClosed) {
//     Serial.println("[ERROR] Bağlantı kapandı!");
//   } else if (event == WebsocketsEvent::GotPing) {
//     Serial.println("[INFO] Ping alındı!");
//   } else if (event == WebsocketsEvent::GotPong) {
//     Serial.println("[INFO] Pong alındı!");
//   } else if (event == WebsocketsEvent::Error) {
//     Serial.print("[ERROR] Hata oluştu: ");
//     Serial.println(data);
//   }
// }
//*****************************************************************************************//
void setup() {
  Serial.begin(9600);  // Initialize serial communications with the PC
  SPI.begin();         // Init SPI bus
  mfrc522.PCD_Init();  // Init MFRC522 card
  /**/
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP("FikoYT", "FikoYT1299");
  wifiMulti.addAP("NTMETAL", "++NT_Metal++");
  //WiFi.mode(WIFI_AP_STA);
  //WiFi.softAP("FikoBot_WTEC", "1a2b3c");
  //WiFi.begin(ssid, password);
  // Serial.println("");
  // Bağlantı Yokken Döngüye Gir
  if (wifiMulti.run() == WL_CONNECTED) {
    // Serial.println("");
    // Serial.println("WiFi connected");
    // Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    /* Sockeio Kurulumları */
    socketIO.beginSSL("fikoyt.com", 8000, "/socket.io/?EIO=4");

    //socketIO.onEvent(event);
    socketIO.onEvent(socketIOEvent);
  }
  //  dnsServer.setTTL(300);
  //  dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
  //  dnsServer.start(80, "fikoyt.com", WiFi.localIP());

  server.on("/", sayfaAna);
  server.begin();
}
/**/

/**/
void yolla_olay(String olay, String id) {
  DynamicJsonDocument doc(1024);
  JsonArray array = doc.to<JsonArray>();

  // add evnet name
  // Hint: socket.on('event_name', ....
  array.add(olay);

  // add payload (parameters) for the event
  JsonObject param1 = array.createNestedObject();
  param1["cardUuid"] = id;
  param1["uuid"] = cihaz_id;

  // JSON to String (serializion)
  String output;
  serializeJson(doc, output);

  // Send event
  socketIO.sendEVENT(output);  //(sIOtype_EVENT,output);

  socketIO.loop();
}

void yolla_baglandi() {
  DynamicJsonDocument doc(1024);
  JsonArray array = doc.to<JsonArray>();

  // add evnet name
  // Hint: socket.on('event_name', ....
  array.add("FikoYT.uyar.devices.connected");

  // add payload (parameters) for the event
  JsonObject param1 = array.createNestedObject();
  param1["uuid"] = cihaz_id;
  param1["name"] = "FikoYT Uyar Kart Okuyucu 1";
  param1["type"] = "FikoYT Kart Okuyucu";

  // JSON to String (serializion)
  String output;
  serializeJson(doc, output);

  // Send event
  socketIO.sendEVENT(output);

  socketIO.loop();
}


//*****************************************************************************************//
uint8_t control = 0x00;
bool ilk_okuma_oldu = false;
void kart_dongu() {
  MFRC522::MIFARE_Key key;
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
  MFRC522::StatusCode status;

  //   auto isNewPresent = mfrc522.PICC_IsNewCardPresent();
  //   auto readCardSerial = mfrc522.PICC_ReadCardSerial();

  if (eski_uid != getID()) {

    if (mfrc522.PICC_IsNewCardPresent() & mfrc522.PICC_ReadCardSerial()) {
      ilk_okuma_oldu = true;
      //      Serial.println("Kart Okundu");
      //      Serial.println(getID());
      eski_uid = getID();

      /***************************************/

      //yolla_olay("FikoYT.nt.cihazlar.kart.koyuldu", eski_uid);
      yolla_olay("FikoYT.uyar.devices.card.update", eski_uid);
      // Serial.println("Kart Koyuldu");
       Serial.println("fikoyt.cihaz.kart-okuyucu.kart-koyuldu:"+ eski_uid);
      /***************************************/
    } 
  } else {

    control = 0;
    for (int i = 0; i < 3; i++) {
      if (!mfrc522.PICC_IsNewCardPresent()) {
        if (mfrc522.PICC_ReadCardSerial()) {
          //Serial.print('a');
          control |= 0x16;
        }
        if (mfrc522.PICC_ReadCardSerial()) {
          //Serial.print('b');
          control |= 0x16;
        }
        //Serial.print('c');
        control += 0x1;
      }
      //Serial.print('d');
      control += 0x4;
    }

    if (control == 13 || control == 14) {
      //card is still there
    
    } else {
      if (ilk_okuma_oldu) {

        /***************************************/
        //yolla_olay("FikoYT.nt.cihazlar.kart.alindi", eski_uid);
        yolla_olay("FikoYT.uyar.devices.card.update", "");
        /***************************************/
        // Serial.println("Kart Silindi");
        Serial.println("fikoyt.cihaz.kart-okuyucu.kart-alındı:"+ eski_uid);
        //        Serial.println(getID());
        eski_uid = "";
        //        mfrc522.PICC_HaltA();
        //        mfrc522.PCD_StopCrypto1();
      } else { 
        // Serial.println("ilk_okuma_oldu else");
      }
    }
  }
}
//*****************************************************************************************//

bool wifiAyarYapildi = false;

bool bilgiYolla = true;
const long bilgi_zaman_aralik = 5000;
unsigned long bilgi_onceki_millis = 0;

unsigned long socket_onceki_millis = 0;
const long socket_zaman_aralik = 750;

void loop() {
  unsigned long simdiki_millis = millis();

  if (wifiMulti.run() != WL_CONNECTED) {
    // Serial.println("WiFi Bağlanamadı!");
    delay(500);
  }
  if (wifiMulti.run() == WL_CONNECTED && !wifiAyarYapildi) {
    // Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    wifiAyarYapildi = true;
    yolla_baglandi();
  }

  //-------------------------------------------
  kart_dongu();
  //-------------------------------------------

  //  dnsServer.processNextRequest();
  if ((eski_uid != getID()) && simdiki_millis - socket_onceki_millis >= socket_zaman_aralik) {
    // Serial.println(getID());
    // Eğer Önceki Zaman + Tekrar Süre Büyükse Şimdiki Zamandan
    socket_onceki_millis = simdiki_millis;
    yolla_olay("FikoYT.nt.cihazlar.kart.guncelle", eski_uid);
  }

  if (bilgiYolla && simdiki_millis - bilgi_onceki_millis >= bilgi_zaman_aralik) {
    bilgi_onceki_millis = simdiki_millis;
    yolla_baglandi();
    bilgiYolla = false;
  }

  socketIO.loop();

  server.handleClient();
  //  mfrc522.PICC_HaltA();
  //  mfrc522.PCD_StopCrypto1();
}
