/***************************** 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 "MvMiscelaneous.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,X2,Y1,Y2;
   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,"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,"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,"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;
   vector<double> y_values;
   for ( ParamIterator ii = params.begin(); ii != params.end(); ii++ )
   {
      // Retrieve the levels info
      LevelMap lmap = (*ii).second->Levels();
      int nlevel = 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 ( LevelIterator jj = lmap.begin(); jj != lmap.end(); jj++ )
         {
            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 ( !(*ii).second->IsSurface() && nlevelM != nlevel )
         {
            setError(1,"ERROR: Data has more than one LevelList");
            return false;
         }
      }
   }

   // Add the 'levels' variable to the netCDF
   vector<long> levels_dimsize(1,y_values.size());
   vector<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
   vector<string> coord_sdim;
   if ( outputMode_ == VP_RTTOV )
      coord_sdim.push_back(XS_VARTIME);
   else
      coord_sdim.push_back(XS_VARLAT);

   double val = appInfo.Y1();
   vector<long> coord_ndim(1,1);
   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;

   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
   vector<long> values_ndim(1,appInfo.NTimes());
   vector<string> values_sdim(1,XS_VARTIME);
   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,"ERROR: 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,nlevels;
   double* vlevels;
   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 ( LevelIterator jj = lmap.begin(); jj != lmap.end();jj++ )
         vlevels[i++] = (*jj).second->YValue();

      break;
   }

   // Initialise 'pressure' variable in the netcdf file
   vector<long> values_ndim;
   values_ndim.push_back(appInfo.NTimes());
   values_ndim.push_back(nlevels);
   vector<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;
   double* 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();
      LevelIterator jj = lmap.begin();
      double lnsp = ((*jj).second->XValues())[0];
      for ( i = 0; i < nlevels; i++ )
      {
         values[i] = field.meanML_to_Pressure_byLNSP(lnsp,vlevels[i]);
      }

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

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

   // Check consistency
   if ( appInfo.NTimes() != ntimes )
   {
      setError(1,"ERROR: 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();
   double* tmt    = new double[ntimes];
   double* lnsp   = new double[ntimes];
   double* tmdt   = new double[ntimes];
   double* values = new double[ntimes];
   int i2t = 0, ilnsp = 0, i2d = 0, icount = 0;
   double *pt;
   for ( ParamIterator ii = params.begin(); ii != params.end(); ii++ )
   {
      if ( (*ii).second->ParamName() == "2t" )
         pt = &tmt[i2t++];
      else if ( (*ii).second->ParamName() == "lnsp" )
         pt = &lnsp[ilnsp++];
      else if ( (*ii).second->ParamName() == "2d" )
         pt = &tmdt[i2d++];
      else
         continue;

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

   // Check consistency
   if ( icount/3 != ntimes )
   {
      setError(1,"ERROR: 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] = metview::specificHumidityFromTd(tmt[i], exp(lnsp[i]), tmdt[i]);

   // Create Q2 variable in the netcdf file
   vector<long> values_ndim(1,appInfo.NTimes());
   vector<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;    tmt    = 0;
   delete [] lnsp;   lnsp   = 0;
   delete [] tmdt;   tmdt   = 0;
   delete [] values; values = 0;

   return true;
}

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

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

   // Create Orography variable in the netcdf file
   vector<long> values_ndim(1,appInfo.NTimes());
   vector<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 = 0;

   return true;
}

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

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

   // Check consistency
   if ( icount/2 != ntimes )
   {
      setError(1,"ERROR: Number of lsm/ci fields not consistent with other fields");
      return false;
   }

   // 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
   vector<long> values_ndim(1,appInfo.NTimes());
   vector<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;    lsm    = 0;
   delete [] ci;     ci     = 0;
   delete [] values; values = 0;

   return true;
}

MvRequest Vprofile::createOutputRequest( ApplicationInfo& appInfo, ParamInfo* parInfo )
{
   // Create netCDF data request
   // Get output data verb
   string verb;
   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 string& key )
{
   // Get parameter info
   ParamInfo *par = appInfo.getParamInfo(params,key);
   if ( !par )
      return false;

   // Add/update Parameter to the netCDF
   string varname = this->getNetcdfVarname(par->ParamName());
   MvNcVar *ncx = cdf.getVariable(varname);
   if ( !ncx )   // Create a new variable
   {
      ntime_ = 0;
      vector<long> values_ndim;
      values_ndim.push_back(appInfo.NTimes());
      vector<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();
   double *x_values = new double[nlevels];
   int i = 0;
   for (LevelIterator jj = lmap.begin(); jj != lmap.end(); jj++)
      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,X2,Y1,Y2;
   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);
   else if ( inputMode_ == VP_AREA )
      vals[0] = field.integrate( Y1, X1, Y2, X2 );
   else
   {
      setError(1,"ERROR: 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,X2,Y1,Y2;
      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;
}

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 string(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);
}
