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

/********************************************************************************
 Important information:

 1. The Bottom/Top Levels will be computed automatically from the data, if
    they are not given as a input parameters.

 2. Relationship among fields and interpolation:

                  ML    ML&LNSP   PL    PL&LNSP
         INTY    ok      ok       ok     ok1
         INTN    ok     error     ok     ok1

   INTY/N = interpolation yes/no
   ok1    = LNSP is ignored

 3. If fieldset contains ML&LNSP, a conversion to PL will be computed automatically
 ********************************************************************************/

#include "Vprofile.h"
#include "MvMiscellaneous.h"
#include "MvSci.h"

// Application mode
enum
{
    VP_POINT,
    VP_GRIDPOINT,
    VP_AREA
};

// Type of VerticalProfile Output Mode
enum
{
    VP_NORMAL,
    VP_RTTOV
};

// Constructors
Vprofile::Vprofile() :
    Xsect("MVPROFILE")
{
    type_ = "MVPROFILE";
    view_ = "MVPROFILEVIEW";
}

Vprofile::Vprofile(const char* kw) :
    Xsect(kw)
{
    type_ = kw;
    view_ = "MVPROFILEVIEW";
}

bool Vprofile::getCoordinates(MvRequest& in, ApplicationInfo& appInfo)
{
    // Get input values
    double X1 = 0., X2 = 0., Y1 = 0., Y2 = 0.;
    if (strcmp(in("INPUT_MODE"), "AREA") == 0) {
        inputMode_ = VP_AREA;
        Y1 = in("AREA", 0);
        X1 = in("AREA", 1);
        Y2 = in("AREA", 2);
        X2 = in("AREA", 3);

        // Check coordinates
        if (!checkCoordinates(X1, Y1, X2, Y2))
            return false;
    }
    else {
        inputMode_ = (strcmp(in("INPUT_MODE"), "POINT") == 0) ? VP_POINT : VP_GRIDPOINT;
        Y1 = Y2 = in("POINT", 0);
        X1 = X2 = in("POINT", 1);
    }

    // Save coordinates
    appInfo.setAreaLine(X1, X2, Y1, Y2);

    return true;
}

bool Vprofile::getAppParameters(MvRequest& in, ApplicationInfo& appInfo)
{
    // Retrieve geographical coordinates
    if (!this->getCoordinates(in, appInfo))
        return false;

    // Retrieve remaining parameters
    outputMode_ = VP_NORMAL;
    if ((const char*)in("OUTPUT_MODE") &&
        strcmp(in("OUTPUT_MODE"), "RTTOV") == 0) {
        // Get RTTOV parameters
        outputMode_ = VP_RTTOV;
        waterType_ = (const char*)in("WATER_TYPE") ? (int)in("WATER_TYPE") : 0;
        cloudTP_ = (const char*)in("CLOUD_TOP_PRESSURE") ? (double)in("CLOUD_TOP_PRESSURE") : 0.;
        cloudFraction_ = (const char*)in("CLOUD_FRACTION") ? (double)in("CLOUD_FRACTION") : 0.;
    }

    return true;
}

bool Vprofile::consistencyCheck(MvRequest& req1, MvRequest& req2)
{
    // Check input mode
    if ((const char*)req1("INPUT_MODE") != (const char*)req2("INPUT_MODE")) {
        setError(1, "Vprofile-> INPUT_MODE parameter is not consistent");
        return false;
    }

    // Check coordinates
    if (strcmp(req1("INPUT_MODE"), "AREA") == 0) {
        if ((double)req1("AREA", 0) != (double)req2("AREA", 0) ||
            (double)req1("AREA", 1) != (double)req2("AREA", 1) ||
            (double)req1("AREA", 2) != (double)req2("AREA", 2) ||
            (double)req1("AREA", 3) != (double)req2("AREA", 3)) {
            setError(1, "Vprofile-> AREA coordinates are not consistent");
            return false;
        }
    }
    else {
        if ((double)req1("POINT", 0) != (double)req2("POINT", 0) ||
            (double)req1("POINT", 1) != (double)req2("POINT", 1)) {
            setError(1, "Vprofile-> POINT coordinates are not consistent");
            return false;
        }
    }

    return true;
}

// It assumes that the dataset contains only 2 set of Level Type: a) Level Type
// with 1 level (e.g. LNSP), b) Level Type with N levels (e.g. Model or
// Pressure). At the current implementation, it is not possible to have a dataset
// with Model and Pressure levels or different numbers of N levels.
bool Vprofile::writeLevelInfoNetcdf(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params, MvField* field, double* splin)
{
    // Loop through all parameters. It writes to the netCDF the levels from the
    // first parameter that has more than 1 level. It also performs a consistency
    // check to guarantee that all parameters have the same number of levels.
    bool found = false;
    int modlev = appInfo.levelType();
    int nlevelM = 0;
    std::vector<double> y_values;
    for (auto& param : params) {
        // Retrieve the levels info
        LevelMap lmap = param.second->Levels();
        int nlevel = static_cast<int>(lmap.size());

        // Ignore it if level Type is of type Surface or LNSP
        if (nlevel == 1)
            continue;

        // Only save the first set of levels.
        // For the remaining sets, do a simple consistency check.
        if (!found) {
            y_values.reserve(nlevel);
            for (auto& jj : lmap) {
                if (modlev == XS_ML_LNSP) {
                    if (outputMode_ == VP_RTTOV)
                        y_values.push_back(jj.second->YValue());
                    else
                        y_values.push_back(field->meanML_to_Pressure_byLNSP(*splin, (int)(jj.second->YValue())) / 100.);
                }
                else if (modlev == XS_ML || modlev == cML_UKMO_ND)
                    y_values.push_back(jj.second->YValue());
                else
                    y_values.push_back(jj.second->YValue() / 100.);
            }

            found = true;
            nlevelM = nlevel;
        }
        else {
            // Currently, only checks if the number of levels is the same, apart
            // from surface fields
            if (!param.second->IsSurface() && nlevelM != nlevel) {
                setError(1, "Vprofile-> Data has more than one LevelList");
                return false;
            }
        }
    }

    // Add the 'levels' variable to the netCDF
    std::vector<long> levels_dimsize(1, static_cast<long>(y_values.size()));
    std::vector<std::string> levels_name(1, XS_VARLEVEL);
    MvNcVar* nclevels = cdf.addVariable(levels_name[0], ncDouble, levels_dimsize, levels_name);
    nclevels->addAttribute("long_name", "Atmospheric Levels");
    nclevels->addAttribute("units", "hPa");
    nclevels->addAttribute("positive", "down");
    nclevels->put(y_values, (long)y_values.size());

    return true;
}

// Write geographical coordinates info to the netCDF file.
// In the current implementation the netCDF file will contain
// only one geographical point.
bool Vprofile::writeGeoCoordsNetcdf(MvNetCDF& cdf, ApplicationInfo& appInfo)
{
    // Add Latitude value
    std::vector<std::string> coord_sdim;
    if (outputMode_ == VP_RTTOV)
        coord_sdim.push_back(XS_VARTIME);
    else
        coord_sdim.push_back(XS_VARLAT);

    double val = appInfo.Y1();
    std::vector<long> coord_ndim(1, 1);
    std::string coord_name = XS_VARLAT;
    MvNcVar* nccoord = cdf.addVariable(coord_name, ncDouble, coord_ndim, coord_sdim);
    nccoord->addAttribute("long_name", "latitude");
    nccoord->addAttribute("units", "degrees_north");
    nccoord->put(&val, 1);

    // Add Longitude value
    coord_sdim[0] = (outputMode_ == VP_RTTOV) ? XS_VARTIME : XS_VARLON;
    val = appInfo.X1();
    coord_name = XS_VARLON;
    nccoord = cdf.addVariable(coord_name, ncDouble, coord_ndim, coord_sdim);
    nccoord->addAttribute("long_name", "longitude");
    nccoord->addAttribute("units", "degrees_east");
    nccoord->put(&val, 1);

    return true;
}

bool Vprofile::writeGlobalAttributesNetcdf(MvNetCDF& netcdf, ParamMap& params, ApplicationInfo& appInfo)
{
    // Add data and application Ids
    netcdf.addAttribute("_View", view_.c_str());
    netcdf.addAttribute("type", type_.c_str());
    if (outputMode_ == VP_NORMAL)
        netcdf.addAttribute("dataID", "NETCDF_NORMAL");
    else
        netcdf.addAttribute("dataID", "NETCDF_RTTOV_INPUT");

    // Add title to the Global Attributes.
    // Currently, because Magics only checks the Global Attributes to
    // produce a title and this module plots only the first variable then
    // we are adding this code below. REMOVE it when Magics is capable to
    // produce a title from the Variable Attributes.
    ParamInfo* parInfo = this->plotVariable(appInfo, params);
    if (!parInfo)
        return false;

    std::string title = this->titleVariable(appInfo, parInfo);
    netcdf.addAttribute("title", title.c_str());

    return true;
}

bool Vprofile::generateExtraData(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params, MvFieldSetIterator& iter)
{
    if (outputMode_ != VP_RTTOV)  // nothing to be done
        return true;

    // Add derived fields
    if (!this->generateDerivedData(cdf, appInfo, params, iter))
        return false;

    // Time dimension is fixed to one
    // Add Watertype variable
    std::vector<long> values_ndim(1, appInfo.NTimes());
    std::vector<std::string> values_sdim(1, XS_VARTIME);
    std::string vname("water_type");
    MvNcVar* ncx = cdf.addVariable(vname, ncDouble, values_ndim, values_sdim);
    ncx->addAttribute("long_name", "Water Type");
    ncx->addAttribute("units", "-");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(&waterType_, 1);

    // Add Cloud TopPressure variable
    vname = "p_cloudtop_scs";
    ncx = cdf.addVariable(vname, ncDouble, values_ndim, values_sdim);
    ncx->addAttribute("long_name", "Cloud Top Pressure (Simple Cloud Scheme)");
    ncx->addAttribute("units", "hPa");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(&cloudTP_, 1);

    // Add Cloud Fraction variable
    vname = "cloud_fraction_scs";
    ncx = cdf.addVariable(vname, ncDouble, values_ndim, values_sdim);
    ncx->addAttribute("long_name", "Cloud Fraction (Simple Clooud Scheme)");
    ncx->addAttribute("units", "0-1");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(&cloudFraction_, 1);

    return true;
}

bool Vprofile::generateDerivedData(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params, MvFieldSetIterator& iter)
{
    // Create Pressure field from LNSP
    if (!this->generatePressureFromLNSP(cdf, appInfo, params, iter))
        return false;

    // Create Q2 from 2t, LNSP, 2d
    if (!this->generateQ2(cdf, appInfo, params))
        return false;

    // Create Orography from Z(surface)
    if (!this->generateOrography(cdf, appInfo, params))
        return false;

    // Create Surface Type from LSM and CI
    if (!this->generateSurftype(cdf, appInfo, params))
        return false;

    return true;
}

bool Vprofile::generatePressureFromLNSP(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params, MvFieldSetIterator& iter)
{
    // Check consistency
    if (appInfo.levelType() != XS_ML_LNSP) {
        setError(1, "Vprofile-> No LNSP or model level data found");
        return false;
    }

    // Find the first Model Level field.
    // To be used to compute the Pressure values.
    MvField field;
    iter.rewind();
    while ((field = iter())) {
        if (field.isModelLevel())
            break;
    }

    // Get level values
    int i = 0, nlevels = 0;
    double* vlevels = nullptr;
    ParamIterator ii;
    for (ii = params.begin(); ii != params.end(); ii++) {
        // Find the first model level field not LNSP
        if ((*ii).second->IsSurface() || appInfo.isLNSP((*ii).second->Parameter()))
            continue;

        // Get levels
        LevelMap lmap = (*ii).second->Levels();
        nlevels = lmap.size();
        vlevels = new double[nlevels];
        i = 0;
        for (auto& jj : lmap)
            vlevels[i++] = jj.second->YValue();

        break;
    }

    // Initialise 'pressure' variable in the netcdf file
    std::vector<long> values_ndim;
    values_ndim.push_back(appInfo.NTimes());
    values_ndim.push_back(nlevels);
    std::vector<std::string> values_sdim;
    values_sdim.push_back(XS_VARTIME);
    values_sdim.push_back(XS_VARLEVEL);
    MvNcVar* ncx = cdf.addVariable("pressure", ncDouble, values_ndim, values_sdim);

    // Add attributes
    ncx->addAttribute("long_name", "Pressure - full level");
    ncx->addAttribute("units", "Pa");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);

    // Get LNSP values, compute pressure and update the netcdf file
    int ntimes = 0;
    auto* values = new double[nlevels];
    for (ii = params.begin(); ii != params.end(); ii++) {
        // Find LNSP field
        if (!appInfo.isLNSP((*ii).second->Parameter()))
            continue;

        // Retrieve the levels info (single dimension)
        LevelMap lmap = (*ii).second->Levels();
        auto jj = lmap.begin();
        double lnsp = ((*jj).second->XValues())[0];
        for (i = 0; i < nlevels; i++) {
            values[i] = field.meanML_to_Pressure_byLNSP(lnsp, static_cast<int>(vlevels[i]));
        }

        // Add 'levels' values to the netCDF
        ncx->setCurrent(ntimes);
        ncx->put(values, 1, (long)nlevels);
        ntimes++;
    }

    // Release memory
    delete[] vlevels;
    vlevels = nullptr;
    delete[] values;
    values = nullptr;

    // Check consistency
    if (appInfo.NTimes() != ntimes) {
        setError(1, "Vprofile-> Number of LNSP fields not consistent with other fields");
        return false;
    }

    return true;
}

bool Vprofile::generateQ2(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params)
{
    // Get input fields: two meter temperature, LNSP, two meter dewpoint temperature
    int ntimes = appInfo.NTimes();
    auto* tmt = new double[ntimes];
    auto* lnsp = new double[ntimes];
    auto* tmdt = new double[ntimes];
    auto* values = new double[ntimes];
    int i2t = 0, ilnsp = 0, i2d = 0, icount = 0;
    double* pt = nullptr;
    for (auto& param : params) {
        if (param.second->ParamName() == "2t")
            pt = &tmt[i2t++];
        else if (param.second->ParamName() == "lnsp")
            pt = &lnsp[ilnsp++];
        else if (param.second->ParamName() == "2d")
            pt = &tmdt[i2d++];
        else
            continue;

        // Retrieve the levels info (single dimension)
        LevelMap lmap = param.second->Levels();
        auto jj = lmap.begin();
        *pt = ((*jj).second->XValues())[0];
        icount++;
    }

    // Check consistency
    if (icount / 3 != ntimes) {
        delete[] tmt;
        delete[] lnsp;
        delete[] tmdt;
        delete[] values;
        setError(1, "Vprofile-> Number of 2t/lnsp/2d fields not consistent with other fields");
        return false;
    }

    // Compute Q2 field
    for (int i = 0; i < ntimes; i++)
        values[i] = MvSci::specificHumidityFromTd(tmt[i], exp(lnsp[i]), tmdt[i]);

    // Create Q2 variable in the netcdf file
    std::vector<long> values_ndim(1, appInfo.NTimes());
    std::vector<std::string> values_sdim(1, XS_VARTIME);
    MvNcVar* ncx = cdf.addVariable("q_2", ncDouble, values_ndim, values_sdim);

    // Add attributes and values
    ncx->addAttribute("long_name", "Skin Specific Humidity");
    ncx->addAttribute("units", "kg/kg");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(values, (long)ntimes);

    // Release memory
    delete[] tmt;
    delete[] lnsp;
    delete[] tmdt;
    delete[] values;

    return true;
}

bool Vprofile::generateOrography(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params)
{
    // Get input field: geopotential surface
    int ntimes = appInfo.NTimes();
    auto* values = new double[ntimes];
    int icount = 0;
    for (auto& param : params) {
        if (param.second->ParamName() == "z" && param.second->LevelType() == "sfc") {
            // Retrieve the levels info (single dimension)
            LevelMap lmap = param.second->Levels();
            auto jj = lmap.begin();
            values[icount] = ((*jj).second->XValues())[0] / 9.81;  // g (gravitation acceleration)
            icount++;
        }
    }

    // Check consistency
    if (icount != ntimes) {
        setError(1, "Vprofile-> Number of Z(surface) fields not consistent with other fields");
        return false;
    }

    // Create Orography variable in the netcdf file
    std::vector<long> values_ndim(1, appInfo.NTimes());
    std::vector<std::string> values_sdim(1, XS_VARTIME);
    MvNcVar* ncx = cdf.addVariable("orog", ncDouble, values_ndim, values_sdim);

    // Add attributes and values
    ncx->addAttribute("long_name", "Orography");
    ncx->addAttribute("units", "m");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(values, (long)ntimes);

    // Release memory
    delete[] values;
    values = nullptr;

    return true;
}

bool Vprofile::generateSurftype(MvNetCDF& cdf, ApplicationInfo& appInfo, ParamMap& params)
{
    // Get input fields: LSM and CI (Sea Ice Cover)
    int ntimes = appInfo.NTimes();
    auto* lsm = new double[ntimes];
    auto* ci = new double[ntimes];
    int ilsm = 0, ici = 0, icount = 0;
    double* pt = nullptr;
    for (auto& param : params) {
        if (param.second->ParamName() == "lsm")
            pt = &lsm[ilsm++];
        else if (param.second->ParamName() == "ci")
            pt = &ci[ici++];
        else
            continue;

        // Retrieve the levels info (single dimension)
        LevelMap lmap = param.second->Levels();
        auto jj = lmap.begin();
        *pt = ((*jj).second->XValues())[0];
        icount++;
    }

    // Check consistency
    if (icount / 2 != ntimes) {
        delete[] lsm;
        delete[] ci;
        setError(1, "Vprofile-> Number of lsm/ci fields not consistent with other fields");
        return false;
    }

    auto* values = new double[ntimes];
    // Compute Surface Type field
    for (int i = 0; i < ntimes; i++) {
        if (lsm[i] == 0.) {
            if (ci[i] > 0.8)
                values[i] = 2;
            else
                values[i] = 0;
        }
        else
            values[i] = 1;
    }

    // Create Surface Type variable in the netcdf file
    std::vector<long> values_ndim(1, appInfo.NTimes());
    std::vector<std::string> values_sdim(1, XS_VARTIME);
    MvNcVar* ncx = cdf.addVariable("surftype", ncDouble, values_ndim, values_sdim);

    // Add attributes and values
    ncx->addAttribute("long_name", "Surface Type");
    ncx->addAttribute("units", "-");
    ncx->addAttribute("_FillValue", XMISSING_VALUE);
    ncx->put(values, (long)ntimes);

    // Release memory
    delete[] lsm;
    delete[] ci;
    delete[] values;

    return true;
}

MvRequest Vprofile::createOutputRequest(ApplicationInfo& appInfo, ParamInfo* parInfo)
{
    // Create netCDF data request
    // Get output data verb
    std::string verb;
    std::string actionMode = appInfo.actionMode();
    if (outputMode_ == VP_RTTOV &&
        (actionMode == "analyse" || actionMode == "prepare"))
        verb = "NETCDF_RTTOV_INPUT";
    else
        verb = "NETCDF";

    MvRequest xs(verb.c_str());
    xs("PATH") = ncName_.c_str();
    xs("TEMPORARY") = 1;

    // Create netCDF output request
    MvRequest out1("NETCDF_XY_POINTS");
    out1("NETCDF_DATA") = xs;
    out1("NETCDF_Y_VARIABLE") = XS_VARLEVEL.c_str();
    out1("NETCDF_X_VARIABLE") = parInfo->ParamName().c_str();

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

    // Create View request using some parameters from the vprofile data request
    MvRequest viewReq("MVPROFILEVIEW");
    if ((const char*)origReq_("INPUT_MODE"))
        viewReq("INPUT_MODE") = origReq_("INPUT_MODE");

    if ((const char*)origReq_("AREA")) {
        viewReq.addValue("AREA", (double)origReq_("AREA", 0));
        viewReq.addValue("AREA", (double)origReq_("AREA", 1));
        viewReq.addValue("AREA", (double)origReq_("AREA", 2));
        viewReq.addValue("AREA", (double)origReq_("AREA", 3));
    }

    if ((const char*)origReq_("POINT")) {
        viewReq.addValue("POINT", (double)origReq_("POINT", 0));
        viewReq.addValue("POINT", (double)origReq_("POINT", 1));
    }

    viewReq("_CLASS") = "MVPROFILEVIEW";
    viewReq("_DEFAULT") = true;
    viewReq("_DATAATTACHED") = "YES";
    if (appInfo.levelType() != cML_UKMO_ND &&
        appInfo.levelType() != XS_ML_GHBC)  // UK MetOffice model levels and GHBC fields are 'upside-down'
        viewReq("_Y_AUTOMATIC_REVERSE") = "on";

    // 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 (!isVisualise(appInfo)) {
        xs("_VIEW") = view_.c_str();
        xs("_VISUALISE") = out1;
        xs("_VIEW_REQUEST") = viewReq;
        return xs;
    }

    // Final output request
    MvRequest out = viewReq + out1;
    return out;
}

// Write curve values to file
// The routine ignores min/max_level. Remove thiese parameters later
bool Vprofile::generateData(ApplicationInfo& appInfo, ParamMap& params,
                            MvNetCDF& cdf, MvField&, const std::string& key)
{
    // Get parameter info
    ParamInfo* par = appInfo.getParamInfo(params, key);
    if (!par)
        return false;

    // Add/update Parameter to the netCDF
    std::string varname = this->getNetcdfVarname(par->ParamName());
    MvNcVar* ncx = cdf.getVariable(varname);
    if (!ncx)  // Create a new variable
    {
        ntime_ = 0;
        std::vector<long> values_ndim;
        values_ndim.push_back(appInfo.NTimes());
        std::vector<std::string> values_sdim;
        values_sdim.push_back(XS_VARTIME);
        if (!par->IsSurface() && !appInfo.isLNSP(par->Parameter())) {
            values_ndim.push_back(par->NrLevels());
            values_sdim.push_back(XS_VARLEVEL);
        }

        ncx = cdf.addVariable(varname, ncDouble, values_ndim, values_sdim);

        // Add attributes
        ncx->addAttribute("long_name", par->ParamLongName().c_str());
        ncx->addAttribute("units", par->Units().c_str());
        ncx->addAttribute("_FillValue", XMISSING_VALUE);
        ncx->addAttribute("title", titleVariable(appInfo, par).c_str());
    }

    // Retrieve xvalues
    LevelMap lmap = par->Levels();
    int nlevels = lmap.size();
    auto* x_values = new double[nlevels];
    int i = 0;
    for (auto& jj : lmap)
        x_values[i++] = (jj.second->XValues())[0];

    // Write values to the netCDF
    ncx->setCurrent(ntime_++);
    ncx->put(x_values, 1, nlevels);

    delete[] x_values;

    return true;
}

int Vprofile::computeGeographicalPoints(ApplicationInfo& appInfo, MvField*)
{
    appInfo.NrPoints(1);
    return 1;
}

bool Vprofile::fillValues(ApplicationInfo& appInfo, MvField& field, double* vals)
{
    MvFieldExpander x(field);

    // Get data value
    double X1 = 0., X2 = 0., Y1 = 0., Y2 = 0.;
    appInfo.getAreaLine(X1, X2, Y1, Y2);
    if (inputMode_ == VP_POINT)
        vals[0] = field.interpolateAt(X1, Y1);
    else if (inputMode_ == VP_GRIDPOINT)
        vals[0] = field.nearestGridpoint(X1, Y1, false);
    else if (inputMode_ == VP_AREA)
        vals[0] = field.integrate(Y1, X1, Y2, X2);
    else {
        setError(1, "Vprofile-> Undefined Vertical Profile type");
        return false;
    }

    // Check valid values
    if (vals[0] >= DBL_MAX)
        vals[0] = XMISSING_VALUE;

    return true;
}

bool Vprofile::fieldConsistencyCheck(MvField& field, ApplicationInfo& appInfo)
{
    if (inputMode_ == VP_GRIDPOINT) {
        double X1 = 0., X2 = 0., Y1 = 0., Y2 = 0.;
        appInfo.getAreaLine(X1, X2, Y1, Y2);
        MvLocation nearestGridPoint = field.nearestGridPointLocation(MvLocation(Y1, X1));
        if (nearestGridPoint.ok()) {
            X1 = nearestGridPoint.x();
            Y1 = nearestGridPoint.y();
            appInfo.setAreaLine(X1, X2, Y1, Y2);
        }
    }

    return true;
}

std::string Vprofile::titleVariable(ApplicationInfo& appInfo, ParamInfo* par)
{
    char titlestr[150];
    if (inputMode_ == VP_POINT || inputMode_ == VP_GRIDPOINT)
        sprintf(titlestr, "Vertical profile of %s %d %02d Step %d %s Point (%.1f,%.1f)",
                par->ParamLongName().c_str(), par->Date(), par->Time(), par->Step(),
                par->ExpVerTitle().c_str(), appInfo.Y1(), appInfo.X1());

    else
        sprintf(titlestr, "Vertical Profile of %s %d %02d Step %d %s Area [%.1f,%.1f,%.1f,%.1f]",
                par->ParamLongName().c_str(), par->Date(), par->Time(), par->Step(),
                par->ExpVerTitle().c_str(), appInfo.Y1(), appInfo.X1(), appInfo.Y2(), appInfo.X2());

    return {titlestr};
}


//---------------------------------------------------------------------

// Translate Metview 3 Vertical Profile Data to Metview 4 definition. Call Metview 4
// server to process the job.
void VprofileM3::serve(MvRequest& in, MvRequest& out)
{
    // Send a general warning message
    setError(0, "The Metview 3 VERTICAL PROFILE DATA icon is deprecated. An automatic translation to the Metview 4 VERTICAL PROFILE DATA icon will be performed internally, but may not work for all cases. It is recommended to manually replace the old icons with their new equivalents.");

    // There are input parameters that are no longer available in Metview 4.
    // Remove them and send a warning message.
    setError(0, "The Metview 3 VERTICAL PROFILE DATA icon is deprecated. Parameters PRESSURE_LEVEL_AXIS, BOTTOM_PRESSURE and TOP_PRESSURE will not be translated internally.");
    MvRequest req = in;
    req.unsetParam("PRESSURE_LEVEL_AXIS");
    req.unsetParam("BOTTOM_PRESSURE");
    req.unsetParam("TOP_PRESSURE");

    // Keep the remaining parameters and update the VERB
    req.setVerb("MVPROFILE");

    // Call the Xsection server to process the job
    Xsect::serve(req, out);
}
