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

 Copyright 2013 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 "Metview.h"
#include "MvPath.hpp"

#include <iostream>
#include <stdexcept>


class Base : public MvService
{
protected:
    Base(const char* a) :
        MvService(a) {}
};

class ScmVisualiser : public Base
{
public:
    ScmVisualiser() :
        Base("SCM_VISUALISER") {}
    void serve(MvRequest&, MvRequest&);

private:
    bool getDataPath(MvRequest& in, const char* dataParamName, const char* dataPathParamName, std::string& result);
    std::string getList(MvRequest& in, const char* parameter, std::string& separator, int& numElements);
    std::string getString(MvRequest& in, const char* parameter, const char* defaultVal);
};


// ScmVisualiser::getDataPath
// for user parameters which have a data icon and  the alternative of
// a path icon. Returns the path of the data icon if it's there;
// otherwise thh path parameters if it's there. The path is returned
// in the 'result' string.
// Returns true if it found a path, otherwise false.

bool ScmVisualiser::getDataPath(MvRequest& in, const char* dataParamName, const char* dataPathParamName, std::string& result)
{
    MvRequest dataR = in(dataParamName);
    const char* dataPath = dataR("PATH");

    if (dataPath) {
        result = std::string(dataPath);
    }
    else {
        const char* cval = in(dataPathParamName);
        if (cval) {
            result = std::string(cval);
            if (result == "OFF")
                result.clear();
        }
    }

    if (result.empty()) {
        setError(1, "ScmVisualiser-> No value found for paramaters: %s and %s", dataParamName, dataPathParamName);
        return false;
    }

    return true;
}


// ScmVisualiser::getList
// Parses a list-type variable from a request and returns it
// as a separated string. Also returns the number of elements found.

std::string ScmVisualiser::getList(MvRequest& in, const char* parameter, std::string& separator, int& numElements)
{
    std::string result;

    int nrParams = in.countValues(parameter);
    numElements = nrParams;

    if (nrParams == 0)  // is this case even possible?
        result = std::string("dummy");
    else
        result = std::string(in(parameter));

    if (nrParams > 1) {
        for (int n = 1; n < nrParams; ++n) {
            result += separator;
            result += std::string(in(parameter, n));
        }
    }

    return result;
}


// ScmVisualiser::getString
// Tries to get the given pstring arameter from the request, returning
// a user-specified default if it is not there.

std::string ScmVisualiser::getString(MvRequest& in, const char* parameter, const char* defaultVal)
{
    const char* val = in(parameter);

    if (!val)
        val = defaultVal;


    return std::string(val);
}


void ScmVisualiser::serve(MvRequest& in, MvRequest& out)
{
    std::cout << "--------------ScmVisualiser::serve() in--------------" << std::endl;
    in.print();

    std::string ScmVisMacro;
    char* mvbin = getenv("METVIEW_BIN");
    if (mvbin == nullptr) {
        setError(1, "ScmVisualiser-> No METVIEW_BIN env variable is defined. Cannot locate mv_scm_vis.mv macro!");
        return;
    }
    else {
        ScmVisMacro = std::string(mvbin) + "/mv_scm_vis.mv";
    }

    std::vector<std::string> params;
    std::string str;
    std::string listSeparator = ",";
    const char* defVal = "dummy";


    // set the MARS expand flags to avoid unwanted expansion of lists
    expand_flags(EXPAND_DATE | EXPAND_TIME | EXPAND_2ND_NAME | EXPAND_LAST_NAME);


    // plot type
    str = getString(in, "SCM_PLOT_TYPE", defVal);
    params.push_back(str);


    // Input data
    std::string dataPath;
    bool ok = getDataPath(in, "SCM_DATA", "SCM_DATA_FILENAME", dataPath);
    if (!ok)
        return;  // error message already generated
    params.push_back(dataPath);


    // data title
    str = getString(in, "SCM_DATA_TITLE", defVal);
    params.push_back(str);


    // comparison data
    std::string compareDataBool = getString(in, "SCM_COMPARE_DATA", defVal);
    params.push_back(compareDataBool);


    std::string comparisonDataTitle;
    if (compareDataBool == "ON") {
        ok = getDataPath(in, "SCM_COMPARISON_DATA", "SCM_COMPARISON_DATA_FILENAME", dataPath);
        if (!ok)
            return;  // error message already generated

        comparisonDataTitle = getString(in, "SCM_COMPARISON_DATA_TITLE", defVal);
    }
    else {
        dataPath = "dummy";
        comparisonDataTitle = "dummy";
    }

    params.push_back(dataPath);
    params.push_back(comparisonDataTitle);


    // comparison mode
    str = getString(in, "SCM_COMPARISON_MODE", defVal);
    params.push_back(str);


    // var selection type
    str = "LIST";  // getString(in, "SCM_VARIABLE_SELECTION_TYPE", defVal);
    params.push_back(str);


    // data type (input/output from SCM)
    std::string dataType = getString(in, "SCM_DATA_TYPE", defVal);


    // variable lists
    int num1dVars, num2dVars, numItems;


    if (dataType == "OUTPUT") {
        str = getList(in, "SCM_OUTPUT_1D_VARIABLES", listSeparator, num1dVars);
        params.push_back(str);

        str = getList(in, "SCM_OUTPUT_2D_VARIABLES", listSeparator, num2dVars);
        params.push_back(str);
    }
    else {
        str = getList(in, "SCM_INPUT_1D_VARIABLES", listSeparator, num1dVars);
        params.push_back(str);

        str = getList(in, "SCM_INPUT_2D_VARIABLES", listSeparator, num2dVars);
        params.push_back(str);
    }

    str = getList(in, "SCM_TIMES", listSeparator, numItems);
    params.push_back(str);


    // output mode and path
    std::string outputMode = getString(in, "SCM_OUTPUT_MODE", defVal);
    params.push_back(outputMode);

    str = getString(in, "SCM_OUTPUT_FILE_PATH", defVal);


    // output path - add directory if not already supplied
    std::string userResPath;
    if (!str.empty()) {
        userResPath = std::string(str);

        if (userResPath.find("/") == std::string::npos) {
            const char* callerIcon = in("_NAME");
            if (callerIcon) {
                std::string callerDir(callerIcon);
                char* mvudir = getenv("METVIEW_USER_DIRECTORY");
                if (mvudir) {
                    callerDir = std::string(mvudir) + "/" + callerDir;
                }
                std::string::size_type pos = callerDir.find_last_of("/");
                if (pos != std::string::npos) {
                    callerDir = callerDir.substr(0, pos);
                }

                userResPath = callerDir + "/" + userResPath;
            }
        }
    }

    params.push_back(userResPath);


    str = getList(in, "SCM_X_MIN_LIST", listSeparator, numItems);
    params.push_back(str);

    str = getList(in, "SCM_X_MAX_LIST", listSeparator, numItems);
    params.push_back(str);

    str = getList(in, "SCM_Y_MIN_LIST", listSeparator, numItems);
    params.push_back(str);

    str = getList(in, "SCM_Y_MAX_LIST", listSeparator, numItems);
    params.push_back(str);


    str = getList(in, "SCM_VALUE_MIN_LIST", listSeparator, numItems);
    params.push_back(str);

    str = getList(in, "SCM_VALUE_MAX_LIST", listSeparator, numItems);
    params.push_back(str);


    // plot the grid
    std::string plotGridBool = getString(in, "SCM_GRID", defVal);
    params.push_back(plotGridBool);


    // sanity check for consistency
    if ((num1dVars > 1 || num2dVars > 1) && outputMode == "SCREEN") {
        setError(1, "ScmVisualiser-> Cannot (currently) plot multiple variables to SCREEN. Please specify an output file.");
        return;
    }


    // Build request to be sent to Macro
    MvRequest req("MACRO");

    std::string processName = MakeProcessName("ScmVisualiser");
    MvRequest macroReq("MACRO");
    req("PATH") = ScmVisMacro.c_str();
    req("_CLASS") = "MACRO";
    req("_ACTION") = "execute";
    req("_REPLY") = processName.c_str();

    // Define argument list for the macro!
    for (auto& param : params) {
        req.addValue("_ARGUMENTS", param.c_str());
    }

    // Run macro
    int error;
    sendProgress("ScmVisualiser-> Execute macro: %s", ScmVisMacro.c_str());
    MvRequest reply = MvApplication::waitService("macro", req, error);

    // Call myself to process the macro reply request
    if (!error && reply) {
        const char* myname = reply("_REPLY");
        MvApplication::callService(myname, reply, nullptr);
    }
    else {
        setError(1, "ScmVisualiser-> Failed to run SCM visualiser! Macro failed!");
        return;
    }

    // out=MvRequest("FLEXTRA_INPUT");
    // out("INPUT_DATA_PATH")=outPath.c_str();
    // out("AVAILABLE_FILE_PATH")=fAvailable.c_str();

    std::cout << "--------------ScmVisualiser::serve() out--------------" << std::endl;
    out.print();
}


int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv, "ScmVisualiser");

    ScmVisualiser flextra;

    theApp.run();
}
