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

 Copyright 2018 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 "MvQEcLayerDb.h"

#include <QtGlobal>
#include <QDebug>
#include <QPainter>
#include <QLinearGradient>

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>

// for logging
#include "mars.h"

#include "MvQStyleDb.h"
#include "MvQTheme.h"
#include "MvMiscellaneous.h"

MvQEcLayerDb* MvQEcLayerDb::instance_ = nullptr;

//==========================================
//
// MvQEcLayerDbItem
//
//==========================================

MvQEcLayerDbItem::MvQEcLayerDbItem(QString name) :
    name_(name)
{
}

void MvQEcLayerDbItem::resolveStyles(QStringList lst)
{
    if (!lst.contains(defaultStyle_)) {
        lst.prepend(defaultStyle_);
    }

    foreach (QString s, lst) {
        int idx = MvQStyleDb::instance()->indexOf(s.toStdString());
        if (idx != -1)
            styleIds_ << idx;
    }
}


QString MvQEcLayerDbItem::previewFile() const
{
    return QString::fromStdString(metview::stylePreviewDirFile(defaultStyle_.toStdString() + ".png"));
}

QPixmap MvQEcLayerDbItem::makePixmap(QSize pixSize)
{
    QPixmap p(previewFile());
    p = p.scaled(pixSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    QRect rect(0, 0, p.width() - 1, p.height() - 1);
    QPainter painter(&p);

    painter.setPen(MvQTheme::imageBorder());
    painter.drawRect(rect);

    return p;
}

void MvQEcLayerDbItem::paint(QPainter* painter, QRect rect)
{
    QPixmap p(previewFile());
    if (!p.isNull()) {
        p = p.scaledToHeight(rect.height(), Qt::SmoothTransformation);
    }

    painter->drawPixmap(rect.topLeft(), p);
    painter->drawRect(QRect(rect.topLeft(), p.size()));
}

//==========================================
//
// MvQEcLayerDb
//
//==========================================

MvQEcLayerDb* MvQEcLayerDb::instance()
{
    if (!instance_)
        instance_ = new MvQEcLayerDb();

    return instance_;
}

MvQEcLayerDb::MvQEcLayerDb()
{
    load();

    keywords_.sort();
    groups_.sort();
}

QStringList MvQEcLayerDb::names() const
{
    QStringList lst;
    for (int i = 0; i < items_.count(); i++) {
        lst << items_[i]->name();
    }

    lst.sort();
    return lst;
}


QStringList MvQEcLayerDb::titleWords() const
{
    QSet<QString> ts;
    for (int i = 0; i < items_.count(); i++) {
        for (auto s : items_[i]->title().split(" ",
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
                                               Qt::SkipEmptyParts)) {
#else
                                               QString::SkipEmptyParts)) {
#endif
            s.remove(")").remove("(");
            if (s.size() >= 2) {
                ts.insert(s);
            }
        }
    }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
    return {ts.begin(), ts.end()};
#else
    return QStringList::fromSet(ts);
#endif
}


void MvQEcLayerDb::load()
{
    std::string fName = metview::ecchartsDirFile("layers.json");

    QFile fIn(fName.c_str());
    if (!fIn.open(QIODevice::ReadOnly | QIODevice::Text)) {
        marslog(LOG_WARN, "MvQEcLayerDb::load() --> Could not open json config file=%s", fName.c_str());
        return;
    }

    QByteArray json = fIn.readAll();
    QJsonDocument doc = QJsonDocument::fromJson(json);

    // This document is an array of groups
    Q_ASSERT(doc.isObject());

    // This document is an array of groups
    // Q_ASSERT(doc.isArray());
    // QJsonArray docArr=doc.array();

    // Iterate through the ojects in a group
    // Iterate through the ojects in a group
    const QJsonObject& jsonob = doc.object();
    for (QJsonObject::const_iterator it = jsonob.constBegin(); it != jsonob.constEnd(); it++) {
        QString name = it.key();
        // qDebug() << name;

        auto* item = new MvQEcLayerDbItem(name);

        if (name.count() > maxNameLen_) {
            maxNameLen_ = name.count();
        }
        QStringList styles;

        QJsonObject ob = it.value().toObject();
        for (QJsonObject::const_iterator itOb = ob.constBegin();
             itOb != ob.constEnd(); itOb++) {
            QString obName = itOb.key();

            if (obName == "description") {
                item->description_ = itOb.value().toString();
            }
            else if (obName == "group") {
                item->group_ = itOb.value().toString();
                if (!groups_.contains(item->group_))
                    groups_ << item->group_;
            }
            else if (obName == "title") {
                item->title_ = itOb.value().toString();
            }
            else if (obName == "keywords") {
                toStringList(itOb.value().toArray(), item->keywords_, keywords_);
            }
            else if (obName == "style") {
                item->defaultStyle_ = itOb.value().toString();
            }
            else if (obName == "styles") {
                toStringList(itOb.value().toArray(), styles);
            }
            else if (obName == "step") {
                toStringList(itOb.value().toArray(), item->steps_);
            }
            else if (obName == "quantile") {
                toStringList(itOb.value().toArray(), item->quantiles_);
            }
        }

        item->resolveStyles(styles);

        items_ << item;
    }
}

void MvQEcLayerDb::collect(QStringList lst, QStringList& allLst) const
{
    foreach (QString s, lst) {
        if (!allLst.contains(s))
            allLst << s;
    }
}

void MvQEcLayerDb::toStringList(const QJsonArray& chArr, QStringList& lst) const
{
    for (int j = 0; j < chArr.count(); j++) {
        QString s = chArr[j].toString();
        lst << s;
    }
}

void MvQEcLayerDb::toStringList(const QJsonArray& chArr, QStringList& lst, QStringList& allLst) const
{
    for (int j = 0; j < chArr.count(); j++) {
        QString s = chArr[j].toString();
        lst << s;
        if (!allLst.contains(s))
            allLst << s;
    }
}

MvQEcLayerDbItem* MvQEcLayerDb::find(const std::string& name) const
{
    int idx = indexOf(name);
    return (idx != -1) ? items_[idx] : 0;
}

int MvQEcLayerDb::indexOf(const std::string& name) const
{
    QString qn = QString::fromStdString(name);
    for (int i = 0; i < items_.count(); i++) {
        if (items_[i]->name() == qn)
            return i;
    }
    return -1;
}
