#ifndef SENSORSERVICE_H
#define SENSORSERVICE_H

#include "HttpClient.h"
#include "SensorGroups_r.h"
#include "getsensors_p.h"
#include "getsensors_r.h"
#include "utils.h"

#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QTime>
#include <QGeoCoordinate>
#include <QList>

class PlotData {
public:
    QList<double> X;
    QList<double> Y;
    PlotData(QList<double>& x, QList<double> y)
        :   X(x)
        ,   Y(y)
    {}
};

struct sensorGroupTree {
    QMap<QString, QString> groupToLocalGroup;
    QMap<QString, QMap<QString, QString>> groupToSensorWithLocal;

    sensorGroupTree(QMap<QString, QString>& a, QMap<QString, QMap<QString, QString>> b)
        : groupToLocalGroup(a)
        , groupToSensorWithLocal(b)
    {}
};

struct Settings {
    QString serial;
    QString factory;
    QString exhuyant;
    QString fleet;
    QString side;
    QString gosNumber;
    QString factoryNumber;
    QString group;
    QString adres;
    QString userName;
    QString userPassword;
    QString argusSerial;
    QList<QPair<QString, QString>> servers;
    Settings(QString seriala,
             QString factorya,
             QString operatoroa,
             QString fleeta,
             QString sidea,
             QString gosNumbera,
             QString factoryNumbera,
             QString groupa,
             QString adresa,
             QString userNamea,
             QString userPassworda,
             QString argusSeriala,
             QList<QPair<QString, QString>> serversa)
        : serial(seriala)
        , factory(factorya)
        , exhuyant(operatoroa)
        , fleet(fleeta)
        , side(sidea)
        , gosNumber(gosNumbera)
        , factoryNumber(factoryNumbera)
        , group(groupa)
        , adres(adresa)
        , userName(userNamea)
        , userPassword(userPassworda)
        , argusSerial(argusSeriala)
        , servers(serversa)
    {}
    Settings()
        : serial("")
        , factory("")
        , exhuyant("")
        , fleet("")
        , side("")
        , gosNumber("")
        , factoryNumber("")
        , group("")
        , adres("")
        , userName("")
        , userPassword("")
        , argusSerial("")
        ,servers({})
    {}
};

class SensorService : public QObject
{
    Q_OBJECT

public:
    explicit SensorService(HttpClient *client, QObject *parent = nullptr)
        : QObject(parent)
        , httpClient(client)
    {}

    Settings getSettings()
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/settings/deviceSettings");

        Settings settings;

        if (responseJson.isEmpty()) {
            return settings;
        }

        if (responseJson.contains("SYSTEM")) {
            QJsonObject systemObj = responseJson.value("SYSTEM").toObject();
            settings.serial = systemObj.value("VIN").toString();
            settings.factory = systemObj.value("MANUFACTURER").toString();
            settings.exhuyant = systemObj.value("OPERATOR").toString();
            settings.fleet = systemObj.value("TP").toString();
            settings.gosNumber = systemObj.value("GOS_NUMBER").toString();
            settings.factoryNumber = systemObj.value("REG_NUMBER").toString();
            settings.group = systemObj.value("GROUP").toString();
        }

        if (responseJson.contains("SENSORS")) {
            QJsonObject sensorsObj = responseJson.value("SENSORS").toObject();
            if (sensorsObj.contains("*SYSTEM")) {
                QJsonObject systemSensorsObj = sensorsObj.value("*SYSTEM").toObject();
                if (systemSensorsObj.contains("*ARGUS")) {
                    QJsonObject argusObj = systemSensorsObj.value("*ARGUS").toObject();

                    QString extra = argusObj.value("EXTRA").toString();
                    QJsonDocument extraDoc = QJsonDocument::fromJson(extra.toUtf8());
                    if (!extraDoc.isNull() && extraDoc.isObject()) {
                        QJsonObject extraObj = extraDoc.object();
                        settings.adres = extraObj.value("IP").toString();
                        settings.userName = extraObj.value("DB_USER").toString();
                        settings.userPassword = extraObj.value("DB_PASSWORD").toString();
                        settings.argusSerial = extraObj.value("SERIAL").toString();
                    }
                }
            }
        }

        if (responseJson.contains("HOSTS")) {
            QJsonArray hostsArray = responseJson.value("HOSTS").toArray();
            for (const QJsonValue &hostValue : hostsArray) {
                QJsonObject hostObj = hostValue.toObject();
                QString domain = hostObj.value("DOMAIN").toString();
                int port = hostObj.value("PORT").toInt();
                settings.servers.append(qMakePair(domain, QString::number(port)));
            }
        }

        return settings;
    }

    void updateSettings(Settings& settings)
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/settings/deviceSettings");

        if (responseJson.contains("SYSTEM")) {
            QJsonObject systemObj = responseJson.value("SYSTEM").toObject();
            systemObj["VIN"] = settings.serial;
            systemObj["MANUFACTURER"] = settings.factory;
            systemObj["OPERATOR"] = settings.exhuyant;
            systemObj["TP"] = settings.fleet;
            systemObj["GOS_NUMBER"] = settings.gosNumber;
            systemObj["REG_NUMBER"] = settings.factoryNumber;
            systemObj["GROUP"] = settings.group;

            responseJson["SYSTEM"] = systemObj;
        }

        if (responseJson.contains("SENSORS")) {
            QJsonObject sensorsObj = responseJson.value("SENSORS").toObject();
            if (sensorsObj.contains("*SYSTEM")) {
                QJsonObject systemSensorsObj = sensorsObj.value("*SYSTEM").toObject();
                if (systemSensorsObj.contains("*ARGUS")) {
                    QJsonObject argusObj = systemSensorsObj.value("*ARGUS").toObject();

                    QJsonObject extraObj;
                    extraObj["IP"] = settings.adres;
                    extraObj["DB_USER"] = settings.userName;
                    extraObj["DB_PASSWORD"] = settings.userPassword;
                    extraObj["SERIAL"] = settings.argusSerial;

                    QJsonDocument extraDoc(extraObj);
                    argusObj["EXTRA"] = QString(extraDoc.toJson(QJsonDocument::Compact));

                    systemSensorsObj["*ARGUS"] = argusObj;
                    sensorsObj["*SYSTEM"] = systemSensorsObj;
                    responseJson["SENSORS"] = sensorsObj;
                }
            }
        }

        if (responseJson.contains("HOSTS")) {
            QJsonArray hostsArray;
            for (const auto& server : settings.servers) {
                QJsonObject newHostObj;
                newHostObj["DOMAIN"] = server.first;
                newHostObj["PORT"] = server.second.toInt();
                hostsArray.append(newHostObj);
            }

            responseJson["HOSTS"] = hostsArray;
        }

        httpClient->post(utils::API_URL + "/settings/deviceSettings", responseJson);
    }

    QList<Sensor> getSensors(const QString &group)
    {
        GetSensorsP requestParams;
        requestParams.setGroup(group);
        QByteArray requestData = requestParams.createRequestJson();

        QJsonObject responseJson = httpClient->post(utils::API_URL + "/data/getDeviceSensors",
                                                    QJsonDocument::fromJson(requestData).object());

        GetSensorsR response;
        response.parseResponse(responseJson);

        return response.getSensors();
    }

    QStringList getSensorGroups()
    {
        QJsonObject responseJson = httpClient->post(utils::API_URL + "/data/getDeviceSensorGroups");

        GetSensorsGroupsR response;
        response.parseResponse(responseJson);

        return response.getGroups();
    }

    QMap<QString, QString> getLocalizedSensorGroups()
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/data/getDeviceSensorsTree");

        QMap<QString, QString> localizedGroupsMap;

        if (responseJson.contains("groups") && responseJson["groups"].isObject()) {
            QJsonObject groupsObject = responseJson["groups"].toObject();

            for (const QString &groupKey : groupsObject.keys()) {
                QJsonObject groupInfo = groupsObject[groupKey].toObject();

                if (groupInfo.contains("name") && groupInfo["name"].isString()) {

                    if(groupInfo["name"].toString() == "*SYSTEM") {
                        continue;
                    }

                    QString localizedName = groupInfo["name"].toString();

                    localizedGroupsMap[localizedName] = groupKey;
                }
            }
        }

        return localizedGroupsMap;
    }

    QMap<QString, QMap<QString, QPair<QString, QString>>> getTranslate()
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/data/getDeviceSensorsTree");

        QMap<QString, QMap<QString, QPair<QString, QString>>> translate;

        if (responseJson.contains("groups") && responseJson["groups"].isObject()) {
            QJsonObject groupsObject = responseJson["groups"].toObject();

            for (const QString &groupKey : groupsObject.keys()) {
                QJsonObject groupInfo = groupsObject[groupKey].toObject();

                if (groupInfo.contains("sensors") && groupInfo["sensors"].isObject() &&
                    groupInfo.contains("name") && groupInfo["name"].isString()) {

                    if(groupInfo["name"].toString() == "*SYSTEM") {
                        continue;
                    }

                    QJsonObject sensors = groupInfo["sensors"].toObject();

                    QString localizedGroup = groupInfo["name"].toString();

                    for (auto sensorKey : sensors.keys()) {
                        if (sensors[sensorKey].isString()) {
                            translate[groupKey][sensorKey].first = localizedGroup;
                            translate[groupKey][sensorKey].second = sensors[sensorKey].toString();
                        }
                    }
                }
            }
        }

        return translate;
    }

    QMap<QString, QString> getSensorToLocalSensor()
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/data/getDeviceSensorsTree");

        QMap<QString, QString> localizedGroupsMap;

        if (responseJson.contains("groups") && responseJson["groups"].isObject()) {
            QJsonObject groupsObject = responseJson["groups"].toObject();

            for (const QString &groupKey : groupsObject.keys()) {
                QJsonObject groupInfo = groupsObject[groupKey].toObject();

                if (groupInfo.contains("name") && groupInfo["name"].isString()) {

                    if(groupInfo["name"].toString() == "*SYSTEM") {
                        continue;
                    }

                    QString localizedName = groupInfo["name"].toString();

                    localizedGroupsMap[localizedName] = groupKey;
                }

            }
        }

        return localizedGroupsMap;
    }

    sensorGroupTree getSensorGroupsTree()
    {
        QJsonObject responseJson = httpClient->get(utils::API_URL + "/data/getDeviceSensorsTree");

        GetSensorsGroupsR response;

        QMap<QString, QString> groupToLocalGroup;
        QMap<QString, QMap<QString, QString>> groupToSensorWithLocal;

        if (responseJson.contains("groups") && responseJson["groups"].isObject()) {
            QJsonObject groups = responseJson["groups"].toObject();

            for (auto groupKey : groups.keys()) {
                QJsonObject group = groups[groupKey].toObject();

                if(group["name"].toString() == "*SYSTEM") {
                    continue;
                }

                if (group.contains("name") && group["name"].isString()) {
                    groupToLocalGroup[groupKey] = group["name"].toString();
                }

                if (group.contains("sensors") && group["sensors"].isObject()) {
                    QJsonObject sensors = group["sensors"].toObject();
                    QMap<QString, QString> sensorsMap;

                    for (auto sensorKey : sensors.keys()) {
                        if (sensors[sensorKey].isString()) {
                            sensorsMap[sensorKey] = sensors[sensorKey].toString();
                        }
                    }
                    groupToSensorWithLocal[groupKey] = sensorsMap;
                }
            }
        }

        return sensorGroupTree(groupToLocalGroup, groupToSensorWithLocal);
    }

    PlotData getDeviceSensorData(const QString &group, const QString &sensorId,
                            qint64 start,
                            qint64 end)
    {
        QJsonObject rootObj;
        rootObj["start"] = start;
        rootObj["end"] = end;
        rootObj["sensor"] = group + '/' + sensorId;
        QJsonDocument doc(rootObj);
        QByteArray requestData = doc.toJson();

        QJsonObject responseJson = httpClient->post(utils::API_URL + "/data/getDeviceSensorData",
                                                    QJsonDocument::fromJson(requestData).object());

        QList<double> x;
        QList<double> y;

        if (responseJson.contains("data") && responseJson["data"].isArray() &&
            responseJson.contains("timestamp") && responseJson["timestamp"].isArray())
        {
            QJsonArray dataArray = responseJson["data"].toArray();
            QJsonArray timestampArray = responseJson["timestamp"].toArray();

            if (dataArray.size() == timestampArray.size()) {
                for (int i = 0; i < dataArray.size(); ++i) {
                    int value = dataArray[i].toInt();
                    qint64 timestamp = timestampArray[i].toInt();
                    x.append(timestamp);
                    y.append(value);
                }
            } else {
                x.append(QTime::currentTime().msecsSinceStartOfDay());
                y.append(0);
            }
        } else {
            x.append(QTime::currentTime().msecsSinceStartOfDay());
            y.append(0);
        }
        if(x.empty()) {
            x.append(QTime::currentTime().msecsSinceStartOfDay());
            y.append(0);
        }
        return PlotData(x, y);
    }

    PlotData getDeviceSensorData(const QString &groupWithSensor,
                                 qint64 start,
                                 qint64 end)
    {
        QJsonObject rootObj;
        rootObj["start"] = start;
        rootObj["end"] = end;
        rootObj["sensor"] = groupWithSensor;
        QJsonDocument doc(rootObj);
        QByteArray requestData = doc.toJson();

        QJsonObject responseJson = httpClient->post(utils::API_URL + "/data/getDeviceSensorData",
                                                    QJsonDocument::fromJson(requestData).object());

        QList<double> x;
        QList<double> y;

        if (responseJson.contains("data") && responseJson["data"].isArray() &&
            responseJson.contains("timestamp") && responseJson["timestamp"].isArray())
        {
            QJsonArray dataArray = responseJson["data"].toArray();
            QJsonArray timestampArray = responseJson["timestamp"].toArray();

            if (dataArray.size() == timestampArray.size()) {
                for (int i = 0; i < dataArray.size(); ++i) {
                    int value = dataArray[i].toInt();
                    qint64 timestamp = timestampArray[i].toInt();
                    x.append(timestamp);
                    y.append(value);
                }
            } else {
                x.append(QTime::currentTime().msecsSinceStartOfDay());
                y.append(0);
            }
        } else {
            x.append(QTime::currentTime().msecsSinceStartOfDay());
            y.append(0);
        }
        if(x.empty()) {
            x.append(QTime::currentTime().msecsSinceStartOfDay());
            y.append(0);
        }
        return PlotData(x, y);
    }

    QVariantList getPoints(qint64 start, qint64 end)
    {
        QJsonObject rootObj;
        rootObj["start_time"] = start;
        rootObj["end_time"] = end;
        QJsonDocument doc(rootObj);
        QByteArray requestData = doc.toJson();

        QJsonObject responseJson = httpClient->post(utils::API_URL + "/data/geo/getPoints",
                                                    QJsonDocument::fromJson(requestData).object());

        QVariantList points;
        if (responseJson.contains("time") && responseJson["time"].isArray() &&
            responseJson.contains("latitude") && responseJson["latitude"].isArray() &&
            responseJson.contains("longitude") && responseJson["longitude"].isArray())
        {
            QJsonArray timeArray = responseJson["time"].toArray();
            QJsonArray latitudeArray = responseJson["latitude"].toArray();
            QJsonArray longitudeArray = responseJson["longitude"].toArray();

            if (timeArray.size() == latitudeArray.size() && timeArray.size() == longitudeArray.size()) {
                for (int i = 0; i < timeArray.size(); ++i) {
                    qint64 timestamp = timeArray[i].toInt();
                    points.append(QVariant::fromValue(QGeoCoordinate(latitudeArray[i].toDouble(), longitudeArray[i].toDouble())));
                }
            }
        }

        return points;
    }

private:
    HttpClient *httpClient;
};

#endif // SENSORSERVICE_H
