#include "Global.h"
#include "classes/IoTItem.h"
#include "ESPConfiguration.h"
#include "NTP.h"

void *getAPI_Date2(String params);

class Loging2 : public IoTItem
{
private:
    String logid1;
    String logid2;
    String id;
    String tmpValue;
    String filesList = "";

    int _publishType = -2;
    int _wsNum = -1;

    int points;
    // int keepdays;

    IoTItem *dateIoTItem;

    String prevDate = "";
    bool firstTimeInit = true;

    // long interval;

public:
    Loging2(String parameters) : IoTItem(parameters)
    {
        jsonRead(parameters, F("logid1"), logid1);
        jsonRead(parameters, F("logid2"), logid2);
        jsonRead(parameters, F("id"), id);
        jsonRead(parameters, F("points"), points);
        if (points > 300)
        {
            points = 300;
            SerialPrint("E", F("Loging2"), "'" + id + "' user set more points than allowed, value reset to 300");
        }
        long interval;
        jsonRead(parameters, F("int"), interval); // в минутах
        setInterval(interval * 60);
        // jsonRead(parameters, F("keepdays"), keepdays, false);

        // создадим экземпляр класса даты
        dateIoTItem = (IoTItem *)getAPI_Date2("{\"id\": \"" + id + "-date\",\"int\":\"20\",\"subtype\":\"date\"}");
        IoTItems.push_back(dateIoTItem);
        SerialPrint("I", F("Loging2"), "created date instance " + id);
    }

    void doByInterval()
    {
        // если объект логгирования не был создан
        if (!isItemExist(logid1))
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' loging object not exist, return");
            return;
        }

        String value = getItemValue(logid1);
        // если значение логгирования пустое
        if (value == "")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' loging value is empty, return");
            return;
        }
        String value2 = getItemValue(logid2);
        // если значение логгирования пустое
        if (value == "")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' loging value is empty, return");
            return;
        }

        // если время не было получено из интернета
        if (!isTimeSynch)
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' Сant loging - time not synchronized, return");
            return;
        }

        regEvent(value, F("Loging2"));

        String logData2;

        jsonWriteInt(logData2, "x", unixTime, false);
        jsonWriteFloat(logData2, "y1", value.toFloat(), false);
        jsonWriteFloat(logData2, "y2", value2.toFloat(), false);

        // прочитаем путь к файлу последнего сохранения
        String filePath = readDataDB(id);

        // если данные о файле отсутствуют, создадим новый
        if (filePath == "failed" || filePath == "")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' file path not found, start create new file");
            createNewFileWithData(logData2);
            return;
        }
        else
        {
            // если файл все же есть но был создан не сегодня, то создаем сегодняшний
            if (getTodayDateDotFormated() != getDateDotFormatedFromUnix(getFileUnixLocalTime(filePath)))
            {
                SerialPrint("E", F("Loging2"), "'" + id + "' file too old, start create new file");
                createNewFileWithData(logData2);
                return;
            }
        }

        // считаем количество строк и определяем размер файла
        size_t size = 0;
        int lines = countJsonObj(filePath, size);
        SerialPrint("i", F("Loging2"), "'" + id + "' " + "lines = " + String(lines) + ", size = " + String(size));

        // если количество строк до заданной величины и дата не менялась
        if (lines <= points && !hasDayChanged())
        {
            // просто добавим в существующий файл новые данные
            addNewDataToExistingFile(filePath, logData2);
            // если больше или поменялась дата то создадим следующий файл
        }
        else
        {
            createNewFileWithData(logData2);
        }
        // запускаем процедуру удаления старых файлов если память переполняется
        deleteLastFile();
    }
/*
    void SetDoByInterval(String valse)
    {
        String value = valse;
        // если значение логгирования пустое
        if (value == "")
        {
            SerialPrint("E", F("Loging2Event"), "'" + id + "' loging value is empty, return");
            return;
        }
        // если время не было получено из интернета
        if (!isTimeSynch)
        {
            SerialPrint("E", F("Loging2Event"), "'" + id + "' Сant loging - time not synchronized, return");
            return;
        }
        regEvent(value, F("Loging2Event"));
        String logData2;
        jsonWriteInt(logData2, "x", unixTime, false);
        jsonWriteFloat(logData2, "y1", value.toFloat(), false);
        jsonWriteFloat(logData2, "y2", value.toFloat(), false);
        // прочитаем путь к файлу последнего сохранения
        String filePath = readDataDB(id);

        // если данные о файле отсутствуют, создадим новый
        if (filePath == "failed" || filePath == "")
        {
            SerialPrint("E", F("Loging2Event"), "'" + id + "' file path not found, start create new file");
            createNewFileWithData(logData2);
            return;
        }
        else
        {
            // если файл все же есть но был создан не сегодня, то создаем сегодняшний
            if (getTodayDateDotFormated() != getDateDotFormatedFromUnix(getFileUnixLocalTime(filePath)))
            {
                SerialPrint("E", F("Loging2Event"), "'" + id + "' file too old, start create new file");
                createNewFileWithData(logData2);
                return;
            }
        }

        // считаем количество строк и определяем размер файла
        size_t size = 0;
        int lines = countJsonObj(filePath, size);
        SerialPrint("i", F("Loging2Event"), "'" + id + "' " + "lines = " + String(lines) + ", size = " + String(size));

        // если количество строк до заданной величины и дата не менялась
        if (lines <= points && !hasDayChanged())
        {
            // просто добавим в существующий файл новые данные
            addNewDataToExistingFile(filePath, logData2);
            // если больше или поменялась дата то создадим следующий файл
        }
        else
        {
            createNewFileWithData(logData2);
        }
        // запускаем процедуру удаления старых файлов если память переполняется
        deleteLastFile();
    }
*/
    void createNewFileWithData(String &logData)
    {
        logData = logData + ",";
        String path = "/lg2/" + id + "/" + String(unixTimeShort) + ".txt"; // создадим путь вида /lg/id/133256622333.txt
        // создадим пустой файл
        if (writeEmptyFile(path) != "success")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' file writing error, return");
            return;
        }

        // запишем в него данные
        if (addFile(path, logData) != "success")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' data writing error, return");
            return;
        }
        // запишем путь к нему в базу данных
        if (saveDataDB(id, path) != "success")
        {
            SerialPrint("E", F("Loging2"), "'" + id + "' db file writing error, return");
            return;
        }
        SerialPrint("i", F("Loging2"), "'" + id + "' file created http://" + WiFi.localIP().toString() + path);
    }

    void addNewDataToExistingFile(String &path, String &logData)
    {
        logData = logData + ",";
        if (addFile(path, logData) != "success")
        {
            SerialPrint("i", F("Loging2"), "'" + id + "' file writing error, return");
            return;
        };
        SerialPrint("i", F("Loging2"), "'" + id + "' loging in file http://" + WiFi.localIP().toString() + path);
    }

    // данная функция уже перенесена в ядро и будет удалена в последствии
    bool hasDayChanged()
    {
        bool changed = false;
        String currentDate = getTodayDateDotFormated();
        if (!firstTimeInit)
        {
            if (prevDate != currentDate)
            {
                changed = true;
                SerialPrint("i", F("NTP"), F("Change day event"));
#if defined(ESP8266)
                FileFS.gc();
#endif
#if defined(ESP32)
#endif
            }
        }
        firstTimeInit = false;
        prevDate = currentDate;
        return changed;
    }

    void publishValue()
    {
        String dir = "/lg2/" + id;
        filesList = getFilesList(dir);

        SerialPrint("i", F("Loging2"), "file list: " + filesList);

        int f = 0;

        bool noData = true;

        while (filesList.length())
        {
            String path = selectToMarker(filesList, ";");

            path = "/lg2/" + id + path;

            f++;

            unsigned long fileUnixTimeLocal = getFileUnixLocalTime(path);

            unsigned long reqUnixTime = strDateToUnix(getItemValue(id + "-date"));
            if (fileUnixTimeLocal > reqUnixTime && fileUnixTimeLocal < reqUnixTime + 86400)
            {
                noData = false;
                String json = getAdditionalJson();
                if (_publishType == TO_MQTT)
                {
                    publishChartFileToMqtt(path, id, calculateMaxCount());
                }
                else if (_publishType == TO_WS)
                {
                    sendFileToWsByFrames(path, "charta", json, _wsNum, WEB_SOCKETS_FRAME_SIZE);
                }
                else if (_publishType == TO_MQTT_WS)
                {
                    sendFileToWsByFrames(path, "charta", json, _wsNum, WEB_SOCKETS_FRAME_SIZE);
                    publishChartFileToMqtt(path, id, calculateMaxCount());
                }
                SerialPrint("i", F("Loging2"), String(f) + ") " + path + ", " + getDateTimeDotFormatedFromUnix(fileUnixTimeLocal) + ", sent");
            }
            else
            {
                SerialPrint("i", F("Loging2"), String(f) + ") " + path + ", " + getDateTimeDotFormatedFromUnix(fileUnixTimeLocal) + ", skipped");
            }

            filesList = deleteBeforeDelimiter(filesList, ";");
        }
        // если данных нет отправляем пустой грфик
        if (noData)
        {
            clearValue();
        }
    }

    String getAdditionalJson()
    {
        String topic = mqttRootDevice + "/" + id;
        String json = "{\"maxCount\":" + String(calculateMaxCount()) + ",\"topic\":\"" + topic + "\"}";
        return json;
    }

    void publishChartToWsSinglePoint(String value)
    {
        String topic = mqttRootDevice + "/" + id;
        String value2 = getItemValue(logid2);
        String json = "{\"maxCount\":" + String(calculateMaxCount()) + ",\"topic\":\"" + topic + "\",\"status\":[{\"x\":" + String(unixTime) + ",\"y1\":" + value + ",\"y2\":" + value2 + "}]}";
        sendStringToWs("chartb", json, -1);
    }

    void clearValue()
    {
        String topic = mqttRootDevice + "/" + id;
        String json = "{\"maxCount\":0,\"topic\":\"" + topic + "\",\"status\":[]}";
        sendStringToWs("chartb", json, -1);
    }

    void clearHistory()
    {
        String dir = "/lg2/" + id;
        cleanDirectory(dir);
    }

    void deleteLastFile()
    {
        IoTFSInfo tmp = getFSInfo();
        SerialPrint("i", "Loging2", String(tmp.freePer) + " % free flash remaining");
        if (tmp.freePer <= 20.00)
        {
            String dir = "/lg/" + id;
            filesList = getFilesList(dir);
            int i = 0;
            while (filesList.length())
            {
                String path = selectToMarker(filesList, ";");
                path = dir + path;
                i++;
                if (i == 1)
                {
                    removeFile(path);
                    SerialPrint("!", "Loging2", String(i) + ") " + path + " => oldest files been deleted");
                    return;
                }

                filesList = deleteBeforeDelimiter(filesList, ";");
            }
        }
    }

    void setPublishDestination(int publishType, int wsNum)
    {
        _publishType = publishType;
        _wsNum = wsNum;
    }

    String getValue()
    {
        return "";
    }
    /*
        void loop() {
            if (enableDoByInt) {
                currentMillis = millis();
                difference = currentMillis - prevMillis;
                if (difference >= interval) {
                    prevMillis = millis();
                    if (interval != 0) {
                        this->doByInterval();
                    }
                }
            }
        }
    */
    void regEvent(const String &value, const String &consoleInfo, bool error = false, bool genEvent = true)
    {
        String userDate = getItemValue(id + "-date");
        String currentDate = getTodayDateDotFormated();
        // отправляем в график данные только когда выбран сегодняшний день
        if (userDate == currentDate)
        {
            // generateEvent(_id, value);
            // publishStatusMqtt(_id, value);

            publishChartToWsSinglePoint(value);
            // SerialPrint("i", "Sensor " + consoleInfo, "'" + _id + "' data: " + value + "'");
        }
    }

    // просто максимальное количество точек
    int calculateMaxCount()
    {
        return 86400;
    }

    // путь вида: /lg/log/1231231.txt
    unsigned long getFileUnixLocalTime(String path)
    {
        return gmtTimeToLocal(selectToMarkerLast(deleteToMarkerLast(path, "."), "/").toInt() + START_DATETIME);
    }
 /*   
    void setValue(const IoTValue &Value, bool genEvent = true)
    {
        value = Value;
        this->SetDoByInterval(String(value.valD));
        SerialPrint("i", "Loging2", "setValue:" + String(value.valD));
        regEvent(value.valS, "Loging2", false, genEvent);
    }
*/    
};

void *getAPI_Loging2(String subtype, String param)
{
    if (subtype == F("Loging2"))
    {
        return new Loging2(param);
    }
    else
    {
        return nullptr;
    }
}

class Date : public IoTItem
{
private:
    bool firstTime = true;

public:
    String id;
    Date(String parameters) : IoTItem(parameters)
    {
        jsonRead(parameters, F("id"), id);
        value.isDecimal = false;
    }

    void setValue(const String &valStr, bool genEvent = true)
    {
        value.valS = valStr;
        setValue(value, genEvent);
    }

    void setValue(const IoTValue &Value, bool genEvent = true)
    {
        value = Value;
        regEvent(value.valS, "", false, genEvent);
        // отправка данных при изменении даты
        for (std::list<IoTItem *>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it)
        {
            if ((*it)->getSubtype() == "Loging2")
            {
                if ((*it)->getID() == selectToMarker(id, "-"))
                {
                    (*it)->setPublishDestination(TO_MQTT_WS, -1);
                    (*it)->publishValue();
                }
            }
        }
    }

    void setTodayDate()
    {
        setValue(getTodayDateDotFormated());
        SerialPrint("E", F("Loging2"), "today date set " + getTodayDateDotFormated());
    }

    void doByInterval()
    {
        if (isTimeSynch)
        {
            if (firstTime)
            {
                setTodayDate();
                firstTime = false;
            }
        }
    }
};

void *getAPI_Date2(String param)
{
    return new Date(param);
}
