/***************************** 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 "GribThermo.h"
#include "DataThermo.h"
#include "MvException.h"
#include "MvNetCDF.h"
#include "MvRequestUtil.hpp"

static const char* sPointSelection[] = {"AREA_AVERAGE", "COORDINATES", "STATION"};

GribThermo::GribThermo() :
    Thermo("GRIB_THERMO")
{
    moduleLabel_ = "GribThermo-> ";
}

GribThermo::GribThermo(const char* kw) :
    Thermo(kw)
{
    moduleLabel_ = "GribThermo-> ";
}

GribThermo::~GribThermo() = default;

bool GribThermo::getAppParameters(MvRequest& expReq)
{
    // It is expected that the input request is expanded

    // Retrieve point selection
    std::string ps = (const char*)expReq("POINT_SELECTION");
    if (ps == sPointSelection[0])  //Area average
    {
        pars_.pointType_ = eAreaAverage;
        expReq.getValue(pars_.lat_, ps.c_str(), 0);   //-- n
        expReq.getValue(pars_.lon_, ps.c_str(), 1);   //-- w
        expReq.getValue(pars_.lat2_, ps.c_str(), 2);  //-- s
        expReq.getValue(pars_.lon2_, ps.c_str(), 3);  //-- e
    }
    else {
        // Retrieve point extraction
        std::string pe   = (const char*)expReq("POINT_EXTRACTION");
        pars_.pointType_ = (pe == "INTERPOLATE") ? eInterpolated : eGridPoint;
        if (ps == sPointSelection[1]) {
            expReq.getValue(pars_.lat_, ps.c_str(), 0);
            expReq.getValue(pars_.lon_, ps.c_str(), 1);
        }
        else if (ps == sPointSelection[2])  //Station
        {
            MvRequest station;
            expReq.getValue(station, "STATION");
            pars_.lat_ = station("LATITUDE");
            pars_.lon_ = station("LONGITUDE");
        }
    }

    // Retrieve dew point formulation
    pars_.dewPointFormulaType_ = (const char*)expReq("DEW_POINT_FORMULATION");

    // Retrieve fields parameter numbers
    pars_.uFieldId_  = (int)expReq("U_WIND_PARAM");
    pars_.vFieldId_  = (int)expReq("V_WIND_PARAM");
    pars_.tempId_    = (int)expReq("TEMPERATURE_PARAM");
    pars_.dewId_     = (int)expReq("SPECIFIC_HUMIDITY_PARAM");
    pars_.lnPressId_ = (int)expReq("LNSP_PARAM");

    return true;
}

bool GribThermo::processData()
{
    // If it is not a GRIB data then it has been already processed
    if (strcmp(dataRequest_.getVerb(), "GRIB") != 0)
        return true;

    // Create a temporary netCDF file
    std::string ncFileName = this->ncFileName();
    MvNetCDF netcdf(ncFileName, 'w');
    if (!netcdf.isValid()) {
        setError(1, "GribThermo-> Can not open a new netCDF file");
        return false;
    }

    // Initialize fildset iterator.
    // The order of the sorting is very important. Parameters STEP, TIME and
    // DATE should be the slowest indexes to be sorted since they define a new
    // plot. The algorithm relies that all fields related to the same Level id
    // are next to each other.
    MvFieldSet fs(dataRequest_);
    MvFieldSetIterator iter(fs);
    iter.sort("LEVELIST");
    iter.sort("STEP");
    iter.sort("TIME");
    iter.sort("DATE");

    // Main loop: process fieldset
    MvField field = iter();
    while (field)  //-- loops dates, i.e. thermoD plots
    {
        // Create data strucutre according to the data representation
        MvThermoDataBase* tep = MvThermoDataFactory(field, pars_);
        if (!tep) {
            setError(1, "GribThermo-> Unimplemented input field level type!");
            return false;
        }

        try {
            // Read and compute all fields related to one plot
            tep->readOneSetOfFields(iter);

            // Compute final values and save results to the netCDF file
            tep->generate(netcdf);
        }
        catch (MvException& e) {
            setError(1, e.what());
            return false;
        }

        // The code below needs to be updated in order to handle a time series of the
        // Thermodynamic diagrams. This version only stores one instance of the wind
        // and title information.

        // Get wind information (T - wind exists)
        windFlag_ = tep->windExist();

        // Get title information
        title_ = tep->title(netcdf);

        // Get first field related to the next plot
        field = tep->returnToken();

        delete tep;
    }

    // Close netCDF file
    netcdf.close();

    return true;
}

bool GribThermo::createOutputRequest(MvRequest& out)
{
    //MUDE ESTE TESTE PARA UMA FUNCAO. SUBSTITUA TAMBEM NA SUBROUTINA ACIMA
    // If it is not a GRIB data then it has been already processed
    //   if ( strcmp(pars_.dataReq_.getVerb(),"GRIB") != 0 )
    //      return createSimpleOutputRequest(out);

    // Get default parameter to be plotted
    //ParamInfo* parInfo = plotVariable(params);
    //if ( !parInfo )
    //   return false;

    // Create netCDF data request
    MvRequest ncDataReq("NETCDF");
    ncDataReq("PATH")      = this->ncFileName().c_str();
    ncDataReq("TEMPORARY") = 1;

    // Create Temperature output request
    int thermoId = rand() % 10000;
    MvRequest outd("NETCDF_XY_POINTS");
    outd("NETCDF_DATA")       = ncDataReq;
    outd("NETCDF_Y_VARIABLE") = sPRESSURE.c_str();
    outd("NETCDF_X_VARIABLE") = sTEMP.c_str();
    outd("_MODULEID")         = thermoId;

    MvRequest gReq("MGRAPH");
    gReq("GRAPH_LINE_COLOUR")    = "red";
    gReq("GRAPH_LINE_THICKNESS") = 8;
    gReq("_CLASS")               = "MGRAPH";
    gReq("_MODULEID")            = thermoId;
    gReq("_SKIP_MACRO")          = 1;  // skip macro converter
    outd                         = outd + gReq;

    // Create Dewpoint output request
    MvRequest outtd("NETCDF_XY_POINTS");
    outtd("NETCDF_DATA")       = ncDataReq;
    outtd("NETCDF_Y_VARIABLE") = sPRESSURE.c_str();
    outtd("NETCDF_X_VARIABLE") = sDEWPOINT.c_str();
    outtd("_MODULEID")         = thermoId;
    outtd("_SKIP_MACRO")       = 1;  // skip macro converter

    gReq("GRAPH_LINE_COLOUR")    = "red";
    gReq("GRAPH_LINE_STYLE")     = "dash",
    gReq("GRAPH_LINE_THICKNESS") = 8;
    outd                         = outd + outtd + gReq;

    // Create wind output request
    if (windFlag_) {
        MvRequest outw("NETCDF_XY_VECTORS");
        outw("NETCDF_DATA")                 = ncDataReq;
        outw("NETCDF_X_POSITION_VARIABLE")  = sXWIND.c_str();
        outw("NETCDF_Y_POSITION_VARIABLE")  = sPWIND.c_str();
        outw("NETCDF_X_COMPONENT_VARIABLE") = sU.c_str();
        outw("NETCDF_Y_COMPONENT_VARIABLE") = sV.c_str();
        outw("_MODULEID")                   = thermoId;
        outw("_SKIP_MACRO")                 = 1;  // skip macro converter

        MvRequest wReq("MWIND");
        wReq("WIND_FIELD_TYPE")  = "flags";
        wReq("WIND_FLAG_COLOUR") = "black";
        wReq("_CLASS")           = "MWIND";
        wReq("_MODULEID")        = thermoId;
        wReq("_SKIP_MACRO")      = 1;  // skip macro converter

        outd = outd + outw + wReq;
    }

    // Add information to help the Macro Converter to translate
    // the output request to a Macro code
    outd("_ORIGINAL_REQUEST") = this->buildMacroConverterRequest();

    // Create a THERMOVIEW request
    MvRequest viewReq("THERMOVIEW");
    if (origReq_ && strcmp(origReq_.getVerb(), "THERMOVIEW") == 0)
        viewReq = origReq_;
    else if ((const char*)origReq_("_CONTEXT")) {
        MvRequest req = origReq_("_CONTEXT");
        if ((const char*)req("_ORIGINAL_REQUEST"))
            viewReq = req("_ORIGINAL_REQUEST");
        // else   // use a default ThermoView request
    }
    else {
        // Initialize request from the Thermo Data module
        metview::CopyAndRemoveParameters(origReq_, viewReq, "_");
        viewReq.unsetParam("DATA");
    }
    viewReq("_MODULEID") = thermoId;

    // Add title information.
    // It must be positioned before the Data+Visdef set of requests to avoid the
    // following problem in uPlot: if another visdef (MGRAPH or MWIND) is defined
    // after the GribThermo requests then it will not be associated to the GribThermo's
    // output data because the existence of a MTEXT request between them.
    MvRequest titleReq("MTEXT");
    titleReq("TEXT_LINE_1")    = title_.c_str();
    titleReq("TEXT_FONT_SIZE") = 0.5;
    titleReq("_MODULEID")      = thermoId;
    titleReq("_SKIP_MACRO")    = 1;  // skip macro converter

    // If action is not visualisation related then return the netcdf data.
    // Also, add the visualisation and original requests as hidden parameters.
    // These may be used later to help the visualisation of the netcdf data.
    if (!this->isVisualise()) {
        out                  = ncDataReq;
        out("_VIEW")         = "THERMOVIEW";
        out("_VISUALISE")    = titleReq + outd;
        out("_VIEW_REQUEST") = viewReq;
        out("_MODULEID")     = thermoId;

        return true;
    }

    // Final output request
    out = viewReq + titleReq + outd;

    return true;
}
