#include "alarm.h"
#include "Times.h"
#include <HTTPClient.h>
// #include <Dictionary.h>
// #include <typeinfo>
#include <iostream>
#include <ArduinoJson.h>

class ServerConnector {
private:
  AlarmStatus* status;
  long lastUpdate;
  WiFiClient* wifiClient;
  CardReader* _cardReader;

  const String AUTHORIZED_ENTRANCE = "AUTHORIZE_ENTRANCE";
  const String ServerAddress = "10.88.88.169:9003";
  const String EVEN_TYPE_FIRED = "Fired";
  const String EVENT_TYPE_EVENT_UPDATE = "EventUpdate";
  const String EVENT_TYPE_UPDATE = "Update";

  const String FIELD_DISARM = "disarm";
  const String FIELD_ALLOWED_CARDS = "cards";

  void HandTask() {
    // ServerConnector * this = (ServerConnector *) pvParameters;
    for (;;) {
      //every 1 minutes ask for update: DISSARM & CARDS
      if (lastUpdate + FromSeconds(15) < millis()) {
        lastUpdate = millis();
        RequestUpdate();
      }

      if (!status->sendNotif && !status->isFired) {
        vTaskDelay(100 / portTICK_PERIOD_MS);
        continue;
      }
      if (status->sendNotif) {
        status->eventId = millis();
      }
      SendAlarm();
      status->sendNotif = false;
      vTaskDelay(FromSeconds(3) / portTICK_PERIOD_MS);
    }
  }

  void SendAlarm() {
    String eventtype = status->sendNotif ? EVEN_TYPE_FIRED : EVENT_TYPE_EVENT_UPDATE;
    String url = "http://" + ServerAddress + "/?eventId=" + String(status->eventId) + "&eventType=" + eventtype + "&eventData=t";
    String response = MakeHttpCall(url);
    if(response == ""){
      Serial.println("Alarm Server not recheable");
      return;
    }

    if (response == AUTHORIZED_ENTRANCE) {
      //Todo Extract to Disarm method
      Serial.println("[HTTP] Entrace authorized by server.");
      status->isFired = false;
      status->isArmed = status->doorStatus == DOOR_CLOSED;
    }
  }

  void RequestUpdate() {
    String url = "http://" + ServerAddress + "/?eventId=0&eventType=" + EVENT_TYPE_UPDATE + "&eventData=t";
    String response = MakeHttpCall(url);
    if(response == ""){
      Serial.println("Update Server not recheable");
      return;
    }
    
    Dictionary& d = *(new Dictionary());
    JsonDocument doc;
    // deserializeJson(doc, response);

    DeserializationError error = deserializeJson(doc, response);

  
    if (error) {
      Serial.print("Serialize error: ");
      Serial.println(error.c_str());
      return;
    }

    
    if(doc[FIELD_DISARM].is<bool>() && doc[FIELD_DISARM] ){
      Serial.println("Disarm request by server");
      status->isArmed=false;
      status->isFired=false;
    }
    
    // Serial.print(FIELD_ALLOWED_CARDS);
    int cardsNo = doc[FIELD_ALLOWED_CARDS].size();

    for(int i=0; i < cardsNo ; i++){
      _cardReader->AddUser(doc[FIELD_ALLOWED_CARDS][i]["id"], doc[FIELD_ALLOWED_CARDS][i]["name"]);
    }
    
  }

  String MakeHttpCall(String url) {
    if (!wifiClient) {
      Serial.printf("[HTTPS] Unable to connect\n");
      return "";
    }

    HTTPClient https;


    Serial.println("[HTTPS] " + url);

    if (!https.begin(*wifiClient, url)) {  // HTTPS
      Serial.println("not able to start http call");
      return "";
    }
    // Serial.print("[HTTPS] GET...\n");
    // start connection and send HTTP header
    int httpCode = https.GET();
    // httpCode will be negative on error

    if (httpCode <= 0) {
      Serial.printf("[HTTP] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      return "";
    }

    // HTTP header has been send and Server response header has been handled
    // Serial.printf("[HTTPS] GET Finished... code: %d\n", httpCode);
    // file found at server
    if (httpCode != HTTP_CODE_OK) {
      https.end();
      return "";
    }


    // print server response payload
    String payload = https.getString();
    https.end();
    return payload;
  }
public:

  ServerConnector(AlarmStatus* statusObj, CardReader* cardReader) {
    status = statusObj;
    wifiClient = new WiFiClient();
    _cardReader = cardReader;
  }

  void StartNotifierAsync() {
    xTaskCreate(this->asynTask, "sendNotif", 6000, (void*)this, 2, NULL);
  }

  static void asynTask(void* pvParameter) {
    ServerConnector* serverInstance = (ServerConnector*)pvParameter;
    serverInstance->HandTask();
  }
};
