/***************************** LICENSE START ***********************************

 Copyright 2021 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQJson.h"

#include <QStringList>

#include "MvLog.h"
#include "MvQStreamOper.h"

QJsonObject MvQJson::fromRequest(const MvRequest& inR)
{
    QJsonObject o;
    if (const char* verb = inR.getVerb()) {
        o["verb"] = QString(verb);
    }

    QJsonObject p;
    MvRequest r = inR.justOneRequest();
    int numP = r.countParameters();
    for (int i = 0; i < numP; i++) {
        if (const char* pch = r.getParameter(i)) {
            if (pch[0] != '_') {
                int numV = r.iterInit(pch);
                QStringList lst;
                for (int i = 0; i < numV; i++) {
                    const char* vch = nullptr;
                    r.iterGetNextValue(vch);
                    if (vch) {
                        lst << QString(vch);
                    }
                }
                if (lst.count() == 1) {
                    p[QString(pch)] = lst[0];
                }
                else if (lst.count() > 1) {
                    p[QString(pch)] = QJsonArray::fromStringList(lst);
                }
            }
        }
    }
    o["params"] = p;

    return o;
}

void MvQJson::toRequest(const QJsonObject& o, MvRequest& r)
{
    if (o.isEmpty())
        return;

    auto verb = o["verb"].toString();
    r = MvRequest(verb.toStdString().c_str());
    auto p = o["params"].toObject();
    for (auto it = p.constBegin(); it != p.constEnd(); ++it) {
        auto kStr = it.key().toStdString();
        const char* kCh = kStr.c_str();
        auto v = it.value();
        if (!v.isArray()) {
            setRequestValue(r, kCh, v);
        }
        else {
            auto a = v.toArray();
            if (a.size() > 0) {
                auto t = a[0].type();
                for (auto av : a) {
                    addRequestValue(r, kCh, av, t);
                }
            }
        }
    }
}

void MvQJson::setRequestValue(MvRequest& r, const char* par, const QJsonValue& v)
{
    if (v.isDouble()) {
        r(par) = v.toDouble();
    }
    else {
        r(par) = v.toString().toStdString().c_str();
    }
}

void MvQJson::addRequestValue(MvRequest& r, const char* par, const QJsonValue& v, QJsonValue::Type t)
{
    if (t == QJsonValue::Double) {
        r.addValue(par, v.toDouble());
    }
    else {
        r.addValue(par, v.toString().toStdString().c_str());
    }
}

QPointF MvQJson::toPointF(const QJsonArray& a)
{
    if (a.count() == 2) {
        return {a[0].toDouble(), a[1].toDouble()};
    }
    return {};
}

QList<QPointF> MvQJson::toPointFList(const QJsonArray& a, QString geoJsonType)
{
    MvLog().dbg() << "MvQJson::toPointFList " << a.size() << " " << geoJsonType;
    QList<QPointF> v;
    if (geoJsonType.isEmpty() || geoJsonType == "LineString") {
        if (a.size() > 0 && a[0].isArray()) {
            for (int i = 0; i < a.count(); i++) {
                v << toPointF(a[i].toArray());
            }
        }
    }
    else if (geoJsonType == "Point") {
        if (a.size() == 2) {
            v << toPointF(a);
        }
    }
    else if (geoJsonType == "Polygon") {
        if (a.size() > 0 && a[0].isArray()) {
            auto b = a[0].toArray();
            if (b.size() > 0 && b[0].isArray()) {
                for (int i = 0; i < b.count(); i++) {
                    v << toPointF(b[i].toArray());
                }
            }
        }
    }
    return v;
}

QJsonArray MvQJson::fromPointFList(QList<QPointF> lst, QString geoJsonType)
{
    if (geoJsonType.isEmpty() || geoJsonType == "LineString") {
        QJsonArray a;
        for (auto p : lst) {
            a << fromPointF(p);
        }
        return a;
    }
    else if (geoJsonType == "Point") {
        if (lst.size() == 1) {
            return fromPointF(lst[0]);
        }
    }
    else if (geoJsonType == "Polygon") {
        QJsonArray a, b;
        for (auto p : lst) {
            a << fromPointF(p);
        }
        b << a;
        return b;
    }
    return {};
}

QJsonArray MvQJson::fromPointF(const QPointF& p)
{
    QJsonArray a;
    a << p.x() << p.y();
    return a;
}

QRectF MvQJson::toRectF(const QJsonArray& a)
{
    if (a.count() == 4) {
        return {a[0].toDouble(), a[1].toDouble(), a[2].toDouble(), a[3].toDouble()};
    }
    return {};
}

QJsonArray MvQJson::fromRectF(const QRectF& r)
{
    QJsonArray a;
    a << r.x() << r.y() << r.width() << r.height();
    return a;
}

QString MvQJson::toString(const QJsonValue& v)
{
    if (v.isString()) {
        return v.toString();
    }
    else if (v.isDouble()) {
        return QString::number(v.toDouble());
    }
    else if (v.isBool()) {
        return QString::number(v.toBool());
    }
    return v.toString();
}

double MvQJson::toDouble(const QJsonValue& v)
{
    if (v.isString()) {
        return v.toString().toDouble();
    }
    else if (v.isDouble()) {
        return v.toDouble();
    }
    else if (v.isBool()) {
        return v.toBool();
    }
    return v.toDouble();
}

bool MvQJson::toBool(const QJsonValue& v)
{
    static double eps = 1E-5;
    if (v.isString()) {
        auto s = v.toString();
        return (s == "0") ? false : true;
    }
    else if (v.isDouble()) {
        auto d = v.toDouble();
        return (fabs(d) < eps) ? false : true;
    }
    else if (v.isBool()) {
        return v.toBool();
    }
    return v.toBool();
}
