#include <Arduino.h>
#include <ArduinoLog.h>

#include "config.h"
#include <Pins.h>
#include <NFC.h>
#include <OTAProxy.h>
#include <helpers.h>
#include <Desfire.h>
#include <Display.h>

#include <WiFi.h>
#include <WebServer.h>

#include <WiFiClient.h>
#include <PubSubClient.h>
#include <SPI.h>

WiFiClient espClient;
// Config_Data config;

PubSubClient *mqtt;
NFC *nfc;
OTAProxy *ota;
Display *display;

unsigned long otatimeout = 3000;
unsigned long lastotatime;

void setup_wifi()
{
    delay(10);
    Serial.println("Connecting Wifi ...");

    WiFi.mode(WIFI_STA);
    WiFi.begin(WLAN_SSID, WLAN_PASS);

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
    }

    randomSeed(micros());
    Serial.println("WiFi connected");
}

void reconnect()
{
    while (!mqtt->connected())
    {
        String clientId = "FabReader_";
        clientId += String(FABREADERID);

        Serial.println("Connecting MQTT ...");
        bool connected = false;
        if (strcmp(MQTT_USERNAME, "") == 0)
        {
            connected = mqtt->connect(clientId.c_str());
        }
        else
        {
            connected = mqtt->connect(clientId.c_str(), MQTT_USERNAME, MQTT_PASSWORD);
        }

        if (connected)
        {
            Serial.println("MQTT connected");
            char id[6] = "00000";
            sprintf(id, "%05d", FABREADERID);
            mqtt->publish("fabreader", id);

            char topic_requestOTA[] = "fabreader/00000/requestOTA";
            sprintf(topic_requestOTA, "fabreader/%05d/requestOTA", FABREADERID);
            mqtt->subscribe(topic_requestOTA);

            char topic_stopOTA[] = "fabreader/00000/stopOTA";
            sprintf(topic_stopOTA, "fabreader/%05d/stopOTA", FABREADERID);
            mqtt->subscribe(topic_stopOTA);

            char topic_display[] = "fabreader/00000/display/#";
            sprintf(topic_display, "fabreader/%05d/display/#", FABREADERID);
            mqtt->subscribe(topic_display);

            display->writeTitle("Connected");
            display->writeInfo("");
        }
        else
        {
            display->writeTitle("Reconnect");
            display->writeInfo("MQTT");

            Serial.print("failed, rc=");
            Serial.print(mqtt->state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

void callback(char *topic, byte *payload, unsigned int length)
{
    Serial.println("Receive Message");
    Serial.println(topic);
    if (ota->hasActiveOTA())
    {
        ota->continueOTA(topic, payload, length);
    }
    display->updateByMQTT(topic, payload, length);
}

void setup()
{
    Serial.begin(115200);
    Serial.print("\n\n\n");
    Serial.println("Booting ...");

    pinMode(PIN_BUZZER, OUTPUT);
    pinMode(PIN_BUTTON, INPUT);
    pinMode(PIN_ETH_SPI_SS, OUTPUT);
    digitalWrite(PIN_ETH_SPI_SS, HIGH);

    display = new Display(PIN_SDA, PIN_SCL, FABREADERID);
    display->clearReaderInfo();

    display->writeInfo("Start NFC ...");
    Serial.println("Connecting NFC ...");
    nfc = new NFC(PIN_RFID_SPI_SS, PIN_RFID_RST);
    if (!(nfc->rfid->PCD_PerformSelfTest()))
    {
        Serial.println("NFC Test failed");
    }
    Serial.println("NFC connected");

    display->writeInfo("Start WIFI ...");
    setup_wifi();

    display->writeInfo("Start MQTT ...");
    mqtt = new PubSubClient(espClient);
    mqtt->setServer(MQTT_BROKER, 1883);
    mqtt->setCallback(callback);

    display->writeInfo("Start OTA ...");
    ota = new OTAProxy(mqtt, nfc, FABREADERID);
}

void loop()
{
    if (!mqtt->connected())
    {
        reconnect();
    }
    mqtt->loop();

    if (!ota->hasActiveOTA())
    {
        if (nfc->checkforCard())
        {
            Serial.println("Card detected");
            if (nfc->connecttoCard())
            {
                Serial.println("Card connected");
                lastotatime = millis();

                display->createReaderInfo("Run OTA");
                ota->startOTA();
            }
            else
            {
                display->createReaderInfo("Retry Card");
            }
        }
    }
    if (ota->hasActiveOTA())
    {
        if (millis() - lastotatime > otatimeout)
        {
            ota->cancelOTA();
            display->clearReaderInfo();
        }
    }
}