/***************************** 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 <QDebug>

#include "MvQMarsCatalog.h"

#include "json_spirit.h"

using namespace json_spirit;

#include "Metview.h"
#include "LogHandler.h"

//================================
//
//MvQMarsCatalogNode
//
//================================

void MvQMarsCatalogNode::setItems(QStringList items)
{
    items_ = items;
    if (items_.indexOf(selection_) == -1)
        selection_.clear();
}

void MvQMarsCatalogNode::setSelection(QString s)
{
    selection_ = s;
}

bool MvQMarsCatalogNode::isSelectionValid()
{
    return (items_.indexOf(selection_) != -1);
}

//================================
//
// MvQMarsCatalogData
//
//================================

MvQMarsCatalogData::MvQMarsCatalogData()
{
    init();
}

MvQMarsCatalogData::~MvQMarsCatalogData()
{
    clear();
}

void MvQMarsCatalogData::init()
{
    MvQMarsCatalogNode* node = new MvQMarsCatalogNode("class");
    data_ << node;
}

void MvQMarsCatalogData::clear()
{
    foreach (MvQMarsCatalogNode* d, data_) {
        delete d;
    }
    data_.clear();
}

void MvQMarsCatalogData::setSelection(QString nodeName, QString value)
{
    MvQMarsCatalogNode* node = this->node(nodeName);
    if (node) {
        node->setSelection(value);
    }
}

MvQMarsCatalogNode* MvQMarsCatalogData::node(QString name)
{
    foreach (MvQMarsCatalogNode* node, data_) {
        if (node->name() == name) {
            return node;
        }
    }

    return 0;
}

//Failing for this
//"{"class" : "mc","expver" : "0099","stream" : "oper","type" : "ef","year" : "2010","month" : "jul","levtype" : "sfc"}"

void MvQMarsCatalogData::update(QString json_str)
{
    qDebug() << "json" << json_str;

    //Clear the data
    clear();

    Value value;
    json_spirit::read(json_str.toStdString(), value);

    if (value.type() != obj_type)
        return;

    qDebug() << "top type:" << value.type();

    Object obj = value.get_obj();
    Object::const_iterator it;

    //Tree
    for (it = obj.begin(); it != obj.end(); it++) {
        if ((*it).name_ == "tree" && (*it).value_.type() == array_type) {
            decodeJson_tree((*it).value_.get_array());
            break;
        }
    }

    //Axis
    for (it = obj.begin(); it != obj.end(); it++) {
        if ((*it).name_ == "axis" && (*it).value_.type() == obj_type) {
            decodeJson_axis((*it).value_.get_obj());
            break;
        }
    }

    //Choices
    for (it = obj.begin(); it != obj.end(); it++) {
        if ((*it).name_ == "choices" && (*it).value_.type() == obj_type) {
            decodeJson_choices((*it).value_.get_obj());
            break;
        }
    }
}

void MvQMarsCatalogData::decodeJson_tree(const Array& array)
{
    for (Array::const_iterator it = array.begin(); it != array.end(); it++) {
        const Array& arB = (*it).get_array();

        QStringList tvals;
        for (Array::const_iterator itB = arB.begin(); itB != arB.end(); itB++) {
            tvals << QString::fromStdString((*itB).get_str());
        }

        if (tvals.count() == 2) {
            MvQMarsCatalogNode* node = new MvQMarsCatalogNode(tvals[0]);
            node->setSelection(tvals[1]);
            data_ << node;
        }
    }
}

void MvQMarsCatalogData::decodeJson_choices(const Object& obj)
{
    for (Object::const_iterator it = obj.begin(); it != obj.end(); it++) {
        QString name = QString::fromStdString((*it).name_);

        qDebug() << "choices:" << name;

        MvQMarsCatalogNode* node = this->node(name);

        const Array& ar = (*it).value_.get_array();

        QStringList lst;
        for (json_spirit::Array::const_iterator itB = ar.begin(); itB != ar.end(); itB++) {
            lst << QString::fromStdString((*itB).get_str());
        }

        qDebug() << "choices list:" << lst;

        if (node) {
            node->setItems(lst);
        }
    }
}

void MvQMarsCatalogData::decodeJson_axis(const Object& obj)
{
    for (Object::const_iterator it = obj.begin(); it != obj.end(); it++) {
        QString name = QString::fromStdString((*it).name_);

        MvQMarsCatalogNode* node = new MvQMarsCatalogNode(name);
        node->setAxis(true);
        //node->setSelection(tvals[1]);
        data_ << node;

        const Array& ar = (*it).value_.get_array();

        QStringList lst;
        for (json_spirit::Array::const_iterator itB = ar.begin(); itB != ar.end(); itB++) {
            lst << QString::fromStdString((*itB).get_str());
        }

        node->setItems(lst);
    }
}


QString MvQMarsCatalogData::catalogPathToJason()
{
    QString s = "{";
    foreach (MvQMarsCatalogNode* node, data_) {
        if (s.size() > 1) {
            s += ",";
        }

        s += "\"" + node->name() + "\"" +
             " : " +
             "\"" + node->selection() + "\"";
    }
    s += "}";

    return s;
}

QString MvQMarsCatalogData::catalogPathToRequest()
{
    QString s = "retrieve";
    foreach (MvQMarsCatalogNode* node, data_) {
        s += ",\n";
        s += "\t" + node->name() + " = " + node->selection();
    }
    return s;
}

QString MvQMarsCatalogData::catalogPathToMacro()
{
    QString s = "res = retrieve(\n";
    int cnt   = 0;
    foreach (MvQMarsCatalogNode* node, data_) {
        if (cnt > 0) {
            s += ",\n";
        }
        s += "\t" + node->name() + " = " + node->selection();
        cnt++;
    }
    s += ")";
    return s;
}


//================================
//
// MvQMarsCatalog
//
//================================

MvQMarsCatalog::MvQMarsCatalog()
{
    proc_ = 0;

    char* pcom = getenv("MV_PYTHON_COMMAND");
    if (pcom) {
        procCommand_ = QString(pcom);
    }
    if (procCommand_.isEmpty()) {
        procCommand_ = "python";
    }

    char* mvbin = getenv("METVIEW_BIN");
    if (mvbin == 0) {
        marslog(LOG_EROR, "No METVIEW_BIN env variable is defined. Cannot locate mv_mars_catalog.py script!");
        return;
    }
    else {
        procScript_ = QString(mvbin) + "/mv_mars_catalog.py";
        apiPath_    = QString(mvbin) + "/WebMars";
    }

    //procCommand_ = "/var/tmp/dummy-user/PERFORCE/development/metview/src/MarsCatalog/mars_catalog.py";
    //cat req.json | /usr/local/bin/python ./mars_catalog.py
}

void MvQMarsCatalog::fetch(MvQMarsCatalogData* data)
{
    qDebug() << "MvQMarsCatalog::fetch";

    //path_="{\"class\" : \"en\" , \"}";
    QString path = data->catalogPathToJason();

    QStringList args;
    args << procScript_;

    if (!proc_) {
        proc_ = new QProcess(this);

        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
        env.insert("PYTHONPATH", apiPath_);
        proc_->setProcessEnvironment(env);

        connect(proc_, SIGNAL(finished(int, QProcess::ExitStatus)),
                this, SLOT(slotFetchFinished(int, QProcess::ExitStatus)));

        connect(proc_, SIGNAL(error(QProcess::ProcessError)),
                this, SLOT(slotFetchError(QProcess::ProcessError)));

        //connect(proc_,SIGNAL(readyReadStandardOutput()),
        //	this,SLOT(slotready()));
    }

    qDebug() << procCommand_ << path;

    //Start fetch-script
    proc_->start(procCommand_, args);

    //Send json object to the scripts stdin
    std::string str = path.toStdString();
    if (str.size() != proc_->write(str.c_str(), str.size())) {
        //marslog(LOG_WARN,"MvQMarsCatalog::fetch() ---> Cannot write to the script's stdin!");
        qDebug() << "MvQMarsCatalog::fetch() ---> Cannot write to the script's stdin!";
        proc_->close();
    }

    //Finish writing to the stdin
    proc_->closeWriteChannel();
}

void MvQMarsCatalog::slotFetchFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    bool failed = false;

    LogItem* log = new LogItem;
    LogHandler::instance()->add(log);
    log->description("Fetch");

    QString msg;

    if (exitStatus != QProcess::NormalExit) {
        failed = true;
        msg    = tr("Script crashed");
        log->error(msg.toStdString() + "\n");
    }

    if (exitCode != 0) {
        failed = true;
        msg    = tr("Script exited with code: ") + QString::number(exitCode);
        log->error(msg.toStdString() + "\n");
    }

    QByteArray stderr = proc_->readAllStandardError();
    if (stderr.size() > 0) {
        failed = true;
        msg    = tr("Stderr: ") + "\n" + QString(stderr);
        log->error(msg.toStdString());
    }

    if (!failed) {
        QByteArray ba = proc_->readAllStandardOutput();
        qDebug() << "slotFetchFinished --> json" << ba;

        QString str(ba);
        emit fetchFinished(str, true);
        emit statusMessage(tr("Fetch <font color=green>(OK)</font>"));
    }
    else {
        emit fetchFinished(QString(), false);
        emit statusMessage(tr("Fetch <font color=red>(FAILED)</font>"));
    }

    emit logUpdated();
}

void MvQMarsCatalog::slotFetchError(QProcess::ProcessError error)
{
    qDebug() << "Error:" << error;
    proc_->close();
}

void MvQMarsCatalog::slotready()
{
    qDebug() << "ready";
}
