/***************************** 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 "MvNetCDF.h"

#include "MvRttov.h"

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


class RttovVisualiser : public Base
{
public:
    RttovVisualiser(const char* a) :
        Base(a) { saveToPool(false); }
    void serve(MvRequest&, MvRequest&);

private:
    bool exportRttovToNetCDF(MvRttov& rttov, std::string& fileName);
    bool getDataPath(MvRequest& in, const char* dataParamName, const char* dataPathParamName, std::string& result);
    std::string getString(MvRequest& in, const char* parameter, const char* defaultVal);
};


// RttovVisualiser::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 RttovVisualiser::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, "RttovVisualiser-> No value found for paramaters: %s and %s", dataParamName, dataPathParamName);
        return false;
    }

    return true;
}


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

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

    if (!val)
        val = defaultVal;


    return std::string(val);
}


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


    //    const char *verb = in.getVerb();
    std::string dataPath;
    bool ok;
    const char* defVal = "dummy";


    // get the path to the data from the requst
    if (!getDataPath(in, "RTTOV_DATA", "RTTOV_FILENAME", dataPath))
        return;


    // create and initialise an MvRttov object to handle the data file
    MvRttov rttov;
    ok = rttov.parseFile(dataPath);

    if (!ok) {
        setError(1, "RttovVisualiser-> %s", rttov.errorMessage().c_str());
        return;
    }


    // export as netCDF
    std::string outNetCDFFileName(marstmp());
    exportRttovToNetCDF(rttov, outNetCDFFileName);


    // note that we create a 'raw' NETCDF requests rather than a
    // NETCDF_VISUALISER request. This is so as to avoid going through
    // StdAppManager, which can force the caching of the result

    std::string str = getString(in, "RTTOV_PLOT_TYPE", defVal);

    if (str == "CHANNEL_TB_GRAPH" || str == defVal) {
        MvRequest netCDFVisualiserReq("NETCDF_XY_POINTS");
        netCDFVisualiserReq("NETCDF_FILENAME") = outNetCDFFileName.c_str();
        netCDFVisualiserReq("NETCDF_X_VARIABLE") = "channel";
        netCDFVisualiserReq("NETCDF_Y_VARIABLE") = "tb";
        netCDFVisualiserReq("NETCDF_VALUE_VARIABLE") = "tb";
        netCDFVisualiserReq("_VERB") = "NETCDF_XY_POINTS";

        out = netCDFVisualiserReq;
    }

    else if (str == "JACOBIAN_MATRIX") {
        MvRequest netCDFVisualiserReq("NETCDF_XY_MATRIX");
        netCDFVisualiserReq("NETCDF_FILENAME") = outNetCDFFileName.c_str();
        netCDFVisualiserReq("NETCDF_X_VARIABLE") = "channel";
        netCDFVisualiserReq("NETCDF_Y_VARIABLE") = "pressure";
        netCDFVisualiserReq("NETCDF_VALUE_VARIABLE") = "jacobian";
        netCDFVisualiserReq("_VERB") = "NETCDF_XY_MATRIX";

        out = netCDFVisualiserReq;
    }

    else if (str == "JACOBIAN_CHANNEL_CURVE") {
        defVal = "1";
        std::string channelString = getString(in, "RTTOV_JACOBIAN_CHANNEL", defVal);
        channelString = "channel/" + channelString;

        MvRequest netCDFVisualiserReq("NETCDF_XY_POINTS");
        netCDFVisualiserReq("NETCDF_FILENAME") = outNetCDFFileName.c_str();
        netCDFVisualiserReq("NETCDF_X_VARIABLE") = "jacobian";
        netCDFVisualiserReq("NETCDF_Y_VARIABLE") = "pressure";
        netCDFVisualiserReq("NETCDF_VALUE_VARIABLE") = "jacobian";
        netCDFVisualiserReq("NETCDF_DIMENSION_SETTING") = channelString.c_str();
        netCDFVisualiserReq("_VERB") = "NETCDF_XY_POINTS";

        // currently, NETCDF_XY_POINTS does not honour the order of the
        // values in the y-axis variable, so we have to create a Cartesian
        // View which reverses the axis
        MvRequest viewReq("CARTESIANVIEW");
        viewReq("X_AUTOMATIC") = "on";
        viewReq("Y_AUTOMATIC") = "on";
        viewReq("Y_AUTOMATIC_REVERSE") = "on";

        out = netCDFVisualiserReq + viewReq;
    }

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


bool RttovVisualiser::exportRttovToNetCDF(MvRttov& rttov, std::string& fileName)
{
    MvNetCDF netcdf(fileName, 'w');

    // add the 'channel' variable
    long channelsSize = (long)rttov.channels().size();
    std::vector<long> channels_dimsize(1, channelsSize);
    std::vector<std::string> channels_name(1, "channel");
    MvNcVar* ncChannels = netcdf.addVariable(channels_name[0], ncInt, channels_dimsize, channels_name);
    ncChannels->addAttribute("long_name", "channel");
    ncChannels->put(rttov.channels(), channelsSize);

    // add the 'tb' variable
    long tbsSize = (long)rttov.tbs().size();
    std::vector<long> tbs_dimsize(1, tbsSize);
    std::vector<std::string> tbs_name(1, "tb");
    MvNcVar* ncTbs = netcdf.addVariable(tbs_name[0], ncDouble, tbs_dimsize, tbs_name);
    ncTbs->addAttribute("long_name", "brightness temperature");
    ncTbs->addAttribute("units", "kelvin");
    ncTbs->put(rttov.tbs(), tbsSize);

    // add the Jacobian pressures
    long jpsSize = (long)rttov.jacobianPressures().size();
    std::vector<long> jps_dimsize(1, jpsSize);
    std::vector<std::string> jps_name(1, "pressure");
    MvNcVar* ncJps = netcdf.addVariable(jps_name[0], ncDouble, jps_dimsize, jps_name);
    ncJps->addAttribute("long_name", "pressure");
    ncJps->addAttribute("units", "hPa");
    ncJps->put(rttov.jacobianPressures(), jpsSize);


    // add the Jacobians themselves
    std::vector<long> jvals_dimsize(2);
    std::vector<std::string> jacobiansDimNames(2);
    jvals_dimsize[0] = channelsSize;
    jvals_dimsize[1] = jpsSize;
    jacobiansDimNames[0] = "channel";
    jacobiansDimNames[1] = "pressure";
    MvNcVar* ncJVals = netcdf.addVariable("jacobian", ncDouble, jvals_dimsize, jacobiansDimNames);

    for (size_t c = 0; c < (unsigned)channelsSize; c++) {
        ncJVals->setCurrent(c);
        ncJVals->put(&(rttov.jacobianValues(c)[0]), 1, jpsSize);
    }


    netcdf.addAttribute("title", "Output from RTTOV");

    netcdf.close();

    return true;
}


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

    //    RttovVisualiser rttovOutVis("RTTOV_OUTPUT_FILE");
    RttovVisualiser rttovVisVis("RTTOV_VISUALISER");

    theApp.run();
}
