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

 Copyright 2012 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 "MvQBufrDataItem.h"

#include <QDebug>
#include <QJsonArray>

//#define _UI_MVQBUFRDATAITEM_DEBUG

MvQBufrDataItem::MvQBufrDataItem(MvQBufrDataItem* parent) :
    parent_(parent)
{
    parent_ = parent;
    if (parent_)
        parent_->addChild(this);
}

MvQBufrDataItem::~MvQBufrDataItem()
{
    qDeleteAll(children_);
}

void MvQBufrDataItem::addChild(MvQBufrDataItem* item)
{
    children_ << item;
}

MvQBufrDataItem* MvQBufrDataItem::childAt(int row)
{
    return children_.value(row);
}

int MvQBufrDataItem::childCount() const
{
    return children_.count();
}

int MvQBufrDataItem::indexInParent() const
{
    if (parent_)
        return parent_->children_.indexOf(const_cast<MvQBufrDataItem*>(this));

    return 0;
}

QString MvQBufrDataItem::data(QString key) const
{
    return data_.value(key);
}

QString MvQBufrDataItem::key() const
{
    return data_.value("key");
}


QString MvQBufrDataItem::getValue(int subset, bool includeCount) const
{
    if (valueArray_.count() == 0 || key() == "unexpandedDescriptors")
        return data_.value("value");
    else if (subset >= 0 && subset < valueArray_.count()) {
        auto v = valueArray_[subset];
        if (includeCount) {
            v += " (" + QString::number(valueArray_.count()) + " items)";
        }
        return v;
    }
    return {};
}

MvQBufrDataItem* MvQBufrDataItem::loadDump(QJsonDocument doc, QString& err)
{
    auto* rootItem = new MvQBufrDataItem();

    if (doc.isNull() || !doc.isObject())
        return rootItem;

    QJsonValue root(doc.object().value("messages"));
    if (root.isArray() && root.toArray().count() == 1) {
        QJsonArray data = root.toArray().first().toArray();
        if (data.isEmpty()) {
            if (root.toArray().first().isString()) {
                QString msgStr = root.toArray().first().toString();
                if (msgStr.contains("error", Qt::CaseInsensitive))
                    err = msgStr;
            }
            return rootItem;
        }

        // Header
        auto* headerItem = new MvQBufrDataGroupItem(rootItem);
        headerItem->title_ = "Header";
        bool inHeader = true;

        // Data
        auto* dataItem = new MvQBufrDataGroupItem(rootItem);
        dataItem->title_ = "Data";

        for (auto v : data) {
            // It is a header item
            if (!v.isArray()) {
                QJsonObject o = v.toObject();
                if (inHeader)
                    addItem(v.toObject(), headerItem);

                if (o.value("key").toString() == "unexpandedDescriptors")
                    inHeader = false;
            }
            // Data items
            else {
                inHeader = false;
                MvQBufrDataItem::loadDump(v, dataItem);
            }
        }

        // loadDump(data,rootItem);
    }

    return rootItem;
}

void MvQBufrDataItem::loadDump(const QJsonValue& value, MvQBufrDataItem* parent)
{
    int arrayCnt = 0;
    int keyCnt = 0;

    for (auto v : value.toArray()) {
        if (v.isArray())
            arrayCnt++;
        else
            keyCnt++;
    }

    for (auto v : value.toArray()) {
        // Normal object
        if (!v.isArray()) {
            QJsonObject o = v.toObject();
            addItem(o, parent);
        }
        // Array
        else {
            // Each will be a group
            if (arrayCnt > 1) {
                auto* item = new MvQBufrDataGroupItem(parent);
                loadDump(v, item);

                if (item->childCount() > 0) {
                    item->data_ = item->childAt(0)->data_;
                    item->valueArray_ = item->childAt(0)->valueArray_;
                    item->title_ = item->key() + ": ";
                    // item->title_=item->key() + ": " +item->value();
                }
            }
            else {
                loadDump(v, parent);
            }
        }
    }
}

void MvQBufrDataItem::addItem(QJsonObject o, MvQBufrDataItem* parent)
{
    if (!o.keys().contains("key"))
        return;

#ifdef _UI_MVQBUFRDATAITEM_DEBUG
    qDebug() << "object -->";
    qDebug() << "  parent" << parent;
    qDebug() << "  keys: " << o.keys();
#endif
    auto* item = new MvQBufrDataItem(parent);

    for (auto key : o.keys()) {
#ifdef _UI_MVQBUFRDATAITEM_DEBUG
        qDebug() << "   --> key:" << key;
#endif
        QJsonValue v = o.value(key);
        if (!v.isObject() && !v.isArray()) {
            item->data_[key] = toString(key, v);
        }
        else if (v.isArray() && key == "value") {
            QJsonArray vec = v.toArray();
            for (auto&& i : vec) {
                item->valueArray_ << toString(key, i);
            }
            item->data_[key] = beautify(item->valueArray_);
        }
        else if (v.isObject()) {
            addItem(v.toObject(), item);
        }
    }
#ifdef _UI_MVQBUFRDATAITEM_DEBUG
    qDebug() << "  " << item->data_;
    qDebug() << "<-- object";
#endif
}

QString MvQBufrDataItem::beautify(QString val)
{
    static QHash<QString, QString> beautifiers;
    if (beautifiers.isEmpty()) {
        beautifiers["null"] = "missing";
        beautifiers[""] = "missing";
    }

    if (beautifiers.contains(val)) {
        return beautifiers.value(val);
    }
    return val;
}

QString MvQBufrDataItem::beautify(QStringList val)
{
    QString s;
    int num = val.size();
    if (num <= 4) {
        s += val.join(",");
    }
    else {
        s = val[0] + ", " + val[1] + ", ..., " + val[num - 2] + ", " + val[num - 1];
    }

    s = "[" + s + "]";

    if (num > 4)
        s += "  (" + QString::number(num) + " items)";

    return s;
}

QString MvQBufrDataItem::toString(const QString& key, QJsonValue v)
{
    static const QString missingStr = "missing";
    if (v.isNull()) {
        return missingStr;
    }
    else if (v.isString()) {
        auto s = v.toString();
        return (s.isEmpty()) ? missingStr : s;
    }
    else {
        QString s = v.toVariant().toString();
        if (key == "value") {
            s = beautify(s);
        }
        return s;
    }
    return {};
}

QString MvQBufrDataItem::extra()
{
    if (isGroup() || childCount() == 0)
        return {};

    QString s;
    for (int i = 0; i < children_.count(); i++) {
        if (!s.isEmpty())
            s += " ";
        s += "[" + children_.at(i)->key() + "=" + children_.at(i)->value() + "]";
    }

    return s;
}

QString MvQBufrDataGroupItem::title(int subset) const
{
    return title_ + value(subset);
}
