/***************************** 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 "HovToolkit.h"
#include "MvRequestUtil.hpp"

// Type of input request to be processed
enum { HOV_DATA_DROPPED, HOV_DATA_MODULE, HOV_DATA_MODULE_DROPPED };

HovToolkit::HovToolkit() : swapAxes_(false),
                           glConvention_("lonlat"),
                           netcdf_(0),
                           xint_(0),
                           expand_(false),
                           netcdfIn_(0)
{
//empty
}

HovToolkit::~HovToolkit()
{
   this->ReleaseAllMemory();
}

void HovToolkit::ReleaseAllMemory()
{
   this->ReleaseMemory();
   this->ReleaseMemoryParam();
}

void HovToolkit::ReleaseMemory()
{
   coord1_.clear();
   coord2_.clear();
   if (xint_)    { delete [] xint_; xint_=0; }
   if (netcdf_)  { delete netcdf_; netcdf_=0; }

   ExpandCloseNcFile();

   return;
}

void HovToolkit::ReleaseMemoryParam()
{
   if (params_.size())
   {
      for ( ParamIterator iter = params_.begin(); iter != params_.end(); iter++)
         delete (*iter).second;

      params_.clear();
   }

   return;
}

// Main function. It is called from the Expand option
bool HovToolkit::Compute(string& netfnameIn,MvRequest& data,string action,MvRequest& out)
{
   // Initialise variables
   expand_ = true;
   netfnameIn_ = netfnameIn;
   nrIndexesIn_ = -1;
   actionMode_ = action;

   // Open previous netcdf file
   ExpandOpenNcFile();

   // Initialize HovToolkit object from a netcdf file
   if ( !GetInputInfo(netcdfIn_) )
      return false;

   // Create Param Info from the fieldset
   if ( !CreateParamInfo(data) )
      return false;

   // Generate data
   if ( !this->GenerateData(data) )
      return false;

   // Create output request
   out = CreateOutputRequest();

   // Close previous netcdf file
   ExpandCloseNcFile();

   return true;
}

// Main function. It is called from the basic option (Line,Area,Height)
bool HovToolkit::Compute(MvRequest& in,MvRequest& out)
{
   nrIndexesIn_ = 0; //option is not Expand

   // Initialize HovToolkit object from the user interface
   if ( !GetInputParameters(in) )
      return false;

   // Process data
   if ( strcmp(dataRequest_.getVerb(),"GRIB") == 0 )
   {
      // Process a grib data and build the output request
      // Create Param Info from the fieldset. Start allocating memory.
      if ( !CreateParamInfo(dataRequest_) )
         return false;

      // Initialize reference time
      SetRefTime(dateMinD_);

      // Generate data. It allocates more memory.
      if ( !this->GenerateData(dataRequest_) )
         return false;

      // Create output request
      out = CreateOutputRequest();
   }
   else
   {
      // It is a netCDF data and it has been already processed.
      // Build the output request
      out = this->CreateOutputRequest( in );
   }

   // Add hidden values to tne output request
   if ( (const char *)in("_NAME") )
       out("_NAME") = in("_NAME");

   return true;
}

MvRequest HovToolkit::CreateOutputRequest()
{
   // Create netCDF data request
   MvRequest req("NETCDF");
   req("PATH") = ncdfPath_.c_str();
   req("TEMPORARY") = 1;

   // Currently, the netcdf file contains only one parameter.
   // Update this code for more than one parameter
   ParamIterator ii = params_.begin();
   ParamInfo *par   = (*ii).second;

   // Create netCDF output request
   MvRequest out1("NETCDF_XY_MATRIX");
   out1("NETCDF_PLOT_TYPE") = "XY_MATRIX";
   out1("NETCDF_DATA") = req;
   const char* secondCoordName = GetSecondCoordName();
   if ( verticalAxis_ == HOV_TIME )  // TIME is vertical axis
   {
      out1("NETCDF_Y_VARIABLE") = HOV_VARTIME.c_str();;
      out1("NETCDF_X_VARIABLE") = secondCoordName;
      if ( SecondAuxiliaryCoord() )
      {
         out1("NETCDF_X_AUXILIARY_VARIABLE") = GetSecondAuxiliaryCoordName();
         out1("NETCDF_X_GEOLINE_CONVENTION") = (const char*)GeolineConvention().c_str();
      }
   }
   else  // TIME is horizontal axis
   {
      out1("NETCDF_X_VARIABLE") = HOV_VARTIME.c_str();
      out1("NETCDF_Y_VARIABLE") = secondCoordName;
      if ( SecondAuxiliaryCoord() )
      {
         out1("NETCDF_Y_AUXILIARY_VARIABLE") = GetSecondAuxiliaryCoordName();
         out1("NETCDF_Y_GEOLINE_CONVENTION") = (const char*)GeolineConvention().c_str();
      }
   }

   out1("NETCDF_VALUE_VARIABLE") = getNetcdfVarname(par->ParamName()).c_str();
   out1("NETCDF_MISSING_ATTRIBUTE") = "_FillValue";
   out1("_VERB") = "NETCDF_XY_MATRIX";

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

   // Create the View request taking values from the input application
   MvRequest viewReq = CreateViewRequest();

   // Save min/max date. This info is important if the dates in the
   // original View are 'automatic'.
   MvDate dd(dateMinD_);
   viewReq("DATE_MIN") = dd.magicsDate().c_str();
   dd = dateMaxD_;
   viewReq("DATE_MAX") = dd.magicsDate().c_str();

   // 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() )
   {
      req("_VIEW") = "MHOVMOELLERVIEW";
      req("_VIEW_REQUEST") = viewReq;
      req("_VISUALISE") = out1;
      return req;
   }

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

   return out;
}

MvRequest HovToolkit::CreateOutputRequest( MvRequest& in )
{
   // Create NetCDF output request
   MvRequest data = in.getSubrequest("DATA");
   MvRequest out1 = data.getSubrequest("_VISUALISE");
   MvRequest viewReq = data.getSubrequest("_VIEW_REQUEST");
   data.unsetParam("_VISUALISE");        // to avoid duplication of info
   data.unsetParam("_VIEW_REQUEST");     // to avoid duplication of info
   data.unsetParam("_ORIGINAL_REQUEST"); // to avoid duplication of info
   out1("NETCDF_DATA") = data;

   // Final output request
   // If an icon was dropped into a view, uPlot will ignore it.
   // Mode-specific options
   MvRequest out = viewReq + out1;
   return out;
}

// Retrieve parameters from the input request and make a consistency check.
// There are 4 types of input request:
// 1. DATA_DROPPED: Grib dropped into a View: MXSECT, DATA(), _CONTEXT(),...
// 2. DATA_MODULE_DROPPED: Data module dropped into a View: MXSECT, DATA(),...
// 3. DATA_MODULE: Process a data module: MXSECT, ...
// 4. netCDF dropped in a View:
//
// Actions for each 4 types:
// 1. Use the original View.
// 2. Use the original View and check if the input HovData parameters are
//    consistent with the View.
// 3. Build a new View.
// 4. Use the original View and check if the netCDF parameters are consistent
//    with the View.
//
bool HovToolkit::GetInputParameters( MvRequest& in )
{
   // Retrieve fieldset
   in.getValue(dataRequest_,"DATA");
   if ( !in.countValues("DATA") ) {
      marslog(LOG_EROR, "No Data files specified");
      return false;
   }

   // Get and save action mode. Default value is "prepare"
   string actionMode = (const char*)in("_ACTION") ? (const char*)in("_ACTION") : "prepare";
   this->ActionMode(actionMode);

   // Retrieve the Cartesian View request if exists, e.g. if a
   // grib data or a HovData module was dropped into a view
   MvRequest viewRequest;
   if ( (const char*)in("_CONTEXT") )
      viewRequest = in("_CONTEXT");

   // Check if a HovData module was dropped
   const char* verb = (const char*)in("_VERB");
   bool moduleDropped = this->IsDataModule(verb);

   // Check the type of input request to be processed
   if ( viewRequest )
      procType_ = moduleDropped ? HOV_DATA_MODULE_DROPPED : HOV_DATA_DROPPED;
   else
      procType_ = HOV_DATA_MODULE;

   // Type of input request is a data dropped into a View
   // Get information from the View request
   if ( procType_ == HOV_DATA_DROPPED )
   {
      // Retrieve the HovmoellerView request from the CartesianView
      MvRequest hovRequest = this->GetAppView( viewRequest );

      // Retrieve parameters
      if ( !this->GetInputInfo(hovRequest) )
         return false;
   }
   // Process a data module
   // Get information from the module icon parameters
   else if ( procType_ == HOV_DATA_MODULE )
   {
      // Retrieve parameters
      if ( !this->GetInputInfo(in) )
         return false;
   }
   // Process a data module dropped into a View.
   // Get information from the Module and View and check if they are compatible.
   else if ( procType_ == HOV_DATA_MODULE_DROPPED )
   {
      // Retrieve the HovmoellerView request from the CartesianView
      MvRequest hovRequest = this->GetAppView( viewRequest );

      // Retrieve 'original' request from the data module
      MvRequest modRequest = dataRequest_.getSubrequest("_ORIGINAL_REQUEST");
      if ( !modRequest )
         modRequest = in;

      // Consistency check, only for non-default View request
      if ( !((const char*)hovRequest("_DEFAULT") && (int)hovRequest("_DEFAULT") == 1) )
         if ( !this->consistencyCheck(hovRequest, modRequest) )
            return false;

      // Retrieve parameters
      if ( !this->GetInputInfo(hovRequest) )
         return false;
   }

   return true;
}

bool HovToolkit::GetInputCommonInfo( MvNetCDF* netcdf )
{
   // Retrieve information from the 'time' variable
   MvNcVar *var = netcdf->getVariable(HOV_VARTIME);
   long *edges = var->edges();
   nrIndexesIn_ = edges[0];
   MvNcAtt *tmpatt = var->getAttribute("reference_date");
   if ( !tmpatt )
   {
      marslog(LOG_EROR, "Netcdf file: attribute reference_date not found");
      return false;
   }
   MvDate d1 (tmpatt->as_string(0));
   refTime_= d1.YyyyMmDd_r();

   // Retrieve information from the second axis variable
   var = netcdf->getVariable(GetSecondCoordName());
   edges = var->edges();
   nrPoints_ = edges[0];
   if ( nrIndexesIn_ <= 0 || nrPoints_ <= 0 )
   {
      marslog(LOG_EROR, "Netcdf file: invalid dimension for variables 'time' and/or 'geographical'");
      return false;
   }

   // Retrieve attributes values
   // Retrieve Grid values
   MvRequest req = netcdf->getRequest();
   const char* cp = (const char*)req("grid");
   if ( cp == NULL )
   {
      marslog(LOG_EROR, "Netcdf file: attribute grid not found");
      return false;
   }
   string str = cp;
   gridEW_ = atof(strtok((char*)str.c_str(),"/"));
   gridNS_ = atof(strtok(NULL,"/"));

   // Retrieve Coordinates values
   cp = (const char*)req("coordinates");
   if ( cp == NULL )
   {
      marslog(LOG_EROR, "Netcdf file: attribute coordinates not found");
      return false;
   }
   str = cp;
   y1_ = atof(strtok((char*)str.c_str(),"/"));
   x1_ = atof(strtok(NULL,"/"));
   y2_ = atof(strtok(NULL,"/"));
   x2_ = atof(strtok(NULL,"/"));

   // Retrieve vertical axis flag
   cp = (const char*)req("vertical_axis");
   verticalAxis_ = (cp == NULL ) ? HOV_TIME : atoi(cp);

   return true;
}

bool HovToolkit::CreateParamInfo(MvRequest& data)
{
   // Clean param info structure
   this->ReleaseMemoryParam();

   // Initialize fieldset
   MvFieldSet fs(data);
   MvFieldSetIterator iter(fs);
   MvField field;

   // Retrieve basic information from the fieldset
   dateMinD_ = 99999999.;
   dateMaxD_ = 0.;
   string keystr,timekey;
   int iparam;
   double level;
   const char* expver = 0;
   iter.rewind();
   iter.sort("LEVELIST", '>');
//   iter.sort("STEP");
//   iter.sort("TIME");
//   iter.sort("DATE");
   iter.sort("EXPVER");
   iter.sort("PARAM");
   while( (field = iter()) )
   {
      // MvFieldExpander	x(field);
      MvRequest rq=field.getRequest();

      iparam = rq("PARAM");
      level  = rq("LEVELIST",0);
      expver = rq("EXPVER");
      string ev = expver ? expver : "_";

      // Save basic information
      GenerateKey(keystr,rq);
      GenerateTimeKey(timekey,rq);
      UpdateLevelList(field,level);

      MvDate dd = GetDate(field);
      double d1 = dd.YyyyMmDd_r();

      // compute min/max time values
      if ( dateMinD_ > d1 ) dateMinD_ = d1;
      if ( dateMaxD_ < d1 ) dateMaxD_ = d1;

      ParamInfo* par = new ParamInfo( iparam,d1,level,ev,
                                      field.getGribKeyValueString("shortName"),
                                      field.getGribKeyValueString("name"),
                                      field.getGribKeyValueString("units")
                                    );

      ParamInsertPair inserted = params_.insert( ParamPair(keystr,par) );
      ParamInfo *pp = (*(inserted.first)).second;
      pp->AddIndex(timekey);
   }

   // Check if basic information was retrieved
   if (params_.size() == 0 )
   {
      marslog(LOG_EROR, "GRIB file invalid, no parameters found.");
      return false;
   }

   // Maybe this restriction should be removed in the future
   if ( params_.size() > 1 )
   {
      marslog(LOG_EROR, "Creation of Hovmoeller diagrams from multiple parameters or vertical levels is not yet supported. This data has %d parameters or levels.", params_.size());
      return false;
   }

   return true;
}

void HovToolkit::GenerateKey(string &str, MvRequest& rq)
{
   int par = rq("PARAM");
   int level  = rq("LEVELIST",0);

   ostringstream oss;
   oss << setfill( '0' )
       << setw( 1 ) << 'p'
       << setw( 3 ) << par
       << setw( 4 ) << level
       << ends;

   str = oss.str();

   return;
}
 
void HovToolkit::GenerateTimeKey(string &str, MvRequest& rq)
{
   int istep  = rq("STEP");
   int itime  = rq("TIME");
   int idate  = rq.getBaseDate();

   ostringstream oss;
   oss << setfill( '0' )
       << setw( 8 ) << idate
       << setw( 4 ) << itime
       << setw( HOV_STEPSIZE ) << istep
       << ends;

   str = oss.str();

   return;
}

double HovToolkit::GetDateDif(string& timekey)
{
   // Extract date information
   double ddate = atof(timekey.substr(0,8).c_str());
   double dhour = atof(timekey.substr(8,2).c_str());
   double dmin  = atof(timekey.substr(10,2).c_str());
   double dstep;
   string strStep = timekey.substr(12,HOV_STEPSIZE);


   // Note that the step can be a negative number. Since it has been padded
   // with leading zeros, it could look like "000-72", which is not
   // correctly interpreted as a number. Therefore we need to remove leading
   // zeros before parsing it.
   std::size_t found = strStep.find_first_not_of("0");

   if (found!=std::string::npos)
   {
       dstep = atof(strStep.substr(found, std::string::npos).c_str());  // parse from the first non-zero character
   }
   else
   {
       dstep = 0;   // nothing but zeros
   }

   // Compute hour+min+step in fraction of days
   double frac = dhour*3600./86400. + dmin*60./86400. + dstep*3600./86400.;

   // If it is a monthly mean parameter, add 1 to original date
   if ( fmod(ddate,100)== 0. ) ddate++;

   // Compute new date
   MvDate ndate(ddate);
   ndate += frac;

   // Compute the difference from the reference date
   MvDate ref(GetRefTime());
   double dif = ref.time_interval_hours(ndate);

   return dif;
}

MvDate HovToolkit::GetDate(MvField& field)
{
   // Compute initial date
   MvDate d1(field.yyyymmddFoh());

   // Add step
   double d2 = field.stepFoh();

   return (d1+d2);
}

bool HovToolkit::GetTimeInfo()
{
   // Empty time variable
   vtime_.clear();

   // Get time series from the netCDF file (expand option)
   if ( !this->ExpandGetTimeInfo() )
      return false;

   // Get time series from the data
   this->GetTimeInfoFromParam();

   // Update number of times
   nrIndexes_ = vtime_.size();

   return true;
}

void HovToolkit::GetTimeInfoFromParam()
{
   // Get time series from the first variable
   double dd;
   string timekey;
   ParamInfo* par = (*params_.begin()).second;
   IndexesMap lmap = par->Indexes();
   for (IndexIterator ii = lmap.begin(); ii != lmap.end(); ii++)
   {
      timekey = (*ii).first;
      dd = this->GetDateDif(timekey);
      vtime_.push_back(dd);
   }

   return;
}

bool HovToolkit::GenerateData(MvRequest& data)
{
   // Write initial info and variables to the netCDF
   if ( !this->Initialiser() )
      return false;

   // Allocate internal variables
   if ( xint_ )
      delete xint_;
   xint_ = new double[nrPoints_];

   // Sort data by level, expver and param
   MvFieldSet fs(data);
   MvFieldSetIterator iter(fs);
   iter.rewind();
   iter.sort("LEVELIST",'>');
   iter.sort("EXPVER");
   iter.sort("PARAM");

   // Generate data
   int currentGenerated = 0, lastNrGenerated = -1;
   string timekey,keystr;
   string lastKey = "FIRSTFIELD";
   MvField field, lastField;
   ParamIterator paramIter;
   while( (field = iter()) )
   {
      currentGenerated++;

      // Create keys
      MvRequest rq = field.getRequest();
      GenerateKey(keystr,rq);
      GenerateTimeKey(timekey,rq);

      // Compute data
      if ( !ComputeValues(field) )
         return false;

      // Save data in the ParamInfo structure
      ParamInfo* par = this->GetParamInfo( keystr );
      if ( !par )
         return false;

      par->FillIndex(xint_,timekey,nrPoints_);

      // Write out data, if it can be grouped together
      if ( lastKey != keystr && lastKey != string("FIRSTFIELD") )
      {
         if ( !NcWriteData(lastField,lastKey ) )
            return false;
         lastNrGenerated = currentGenerated;
      }

      lastKey = keystr;
      lastField = field;
   }

   // Write out the data in the netcdf file
   if ( lastNrGenerated <=  currentGenerated )
   {
      if ( !NcWriteData(lastField,lastKey) )
         return false;
   }
   netcdf_->close();

   return true;
}

bool HovToolkit::Initialiser()
{
   // Create temporary netcdf file name
   ncdfPath_ = marstmp();
   if ( netcdf_ )
      delete netcdf_;
   netcdf_ = new MvNetCDF();
   netcdf_->init(ncdfPath_,'w');

   // Write main attributes to the netCDF
   this->NcWriteGlobalAttributes();

   // Initialise time variable: compute and write to the netCDF
   if ( !this->GetTimeInfo() )
      return false;
   if ( !this->NcWriteTimeInfo() )
      return false;

   // Compute second coordinates: compute and write to the netCDF
   if ( !this->ComputeSecondCoord() )
      return false;
   if ( !this->NcWriteSecondCoord() )
      return false;

   return true;
}

void HovToolkit::NcWriteGlobalAttributes()
{
   // Write main attributes
   netcdf_->addAttribute("_View","MHOVMOELLERVIEW");
   netcdf_->addAttribute("_FillValue",HMISSING_VALUE );
   netcdf_->addAttribute("type", ApplicationType() );

   char buf[32];
   sprintf(buf,"%7.2f/%7.2f/%7.2f/%7.2f",y1_,x1_,y2_,x2_);
   netcdf_->addAttribute("coordinates",buf);

   sprintf(buf,"%5.2f/%5.2f",gridEW_,gridNS_);
   netcdf_->addAttribute("grid",buf);

   netcdf_->addAttribute("vertical_axis", verticalAxis_);

   // Write attributes specific to an application
   this->NcWriteGlobalAttributesApp();

   // Add title to the global attributes.
   // Currently, because Magics only checks the Global Attributes to
   // produce a title and Hovmoeller 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.
   ParamIterator ii = params_.begin();
   ParamInfo *par   = (*ii).second;
   netcdf_->addAttribute("title",this->GetTitle(par).c_str());
}

// Write Time info to the netCDF file
bool HovToolkit::NcWriteTimeInfo()
{
   // Add 'time' variable to netCDF
   vector<long> v_ndim(1,nrIndexes_);
   vector<string> v_sdim(1,HOV_VARTIME);
   MvNcVar *nctime = netcdf_->addVariable(v_sdim[0],ncDouble,v_ndim,v_sdim);

   char buf[128];
   MvDate d1(this->GetRefTime());
   d1.Format(d1.StringFormat(),buf);
   nctime->addAttribute("long_name", "Time");
   nctime->addAttribute("units", "hours");
   nctime->addAttribute("reference_date", buf);
   nctime->put(vtime_,nrIndexes_);

   return true;
}

// We have all the values for one set of data
// Call functions to write the values to netcdf file
bool HovToolkit::NcWriteData(MvField&, const string& key)
{
   // Get parameter info
   ParamInfo* par = this->GetParamInfo( key );
   if ( !par )
      return false;

   // Generate the CDF variable
   vector<long> vdim(2);
   vector<string> vname(2);
   int nrIndexes = this->NrIndexes();
   if ( verticalAxis_ == HOV_TIME )
   {
      vdim[0]  = nrIndexes;
      vdim[1]  = nrPoints_;
      vname[0] = HOV_VARTIME;
      vname[1] = this->GetSecondCoordName();
   }
   else
   {
      vdim[0]  = nrPoints_;
      vdim[1]  = nrIndexes;
      vname[0] = this->GetSecondCoordName();
      vname[1] = HOV_VARTIME;
   }
   MvNcVar* ncvar = netcdf_->addVariable(getNetcdfVarname(par->ParamName()),ncDouble,vdim,vname);

   // Write some attributes in the netcdf file
   this->NcAddMetadata(ncvar,par);

   // Write matrix in the netcdf file
   this->NcFillValues(ncvar,par);

   return true;
}

void HovToolkit::NcAddMetadata(MvNcVar *ncvar, ParamInfo *par)
{
   // Write parameter info
   ncvar->addAttribute("long_name",par->ParamLongName().c_str());
   ncvar->addAttribute("units",par->Units().c_str());
   ncvar->addAttribute("level",par->Level());
   ncvar->addAttribute("expver",par->ExpVer().c_str());
   ncvar->addAttribute("_FillValue",HMISSING_VALUE);

   // Write title
   string title = this->GetTitle(par);
   ncvar->addAttribute("title",title.c_str());

   return;
}

// Internal data organization:
//   .each line contains a set of averaged lat/long values related to
//    an especific time
//   .first line is related to the earliest time
//   .first value in each line is related to X1 (first longitude west) or
//    Y1 (first latitude south) or X1/Y1 (first longitude/latitude point).
// Output data organization:
//   .the matrix coordinates (0,0) corresponds to bottom/left corner
//   .2 output layouts:
//
//      A)           min lng     max lng
//         max time  -------------------
//                   ...................
//         min time  -------------------
//
//
//      B)           min time   max time
//         max lat   -------------------
//                   ...................
//         min lat   -------------------
//
void HovToolkit::NcFillValues(MvNcVar *ncvar, ParamInfo *par)
{
   // Create data matrix according to the input parameters
   // Write matrix to the netcdf file
   IndexesMap lmap = par->Indexes();
   int k;

   if ( verticalAxis_ == HOV_TIME ) //vertical time axis
   {
      // 'Expand' option: write previous values first
      int pos=0;
      this->ExpandFillValues(ncvar,pos);
      IndexIterator ii;
      for ( ii = lmap.begin(), k = 0; ii != lmap.end(); ii++, k++ )
      {
         IndexesInfo *lInfo = (*ii).second;
         NcWriteValues (ncvar, lInfo, k+pos );
      }
   }
   else    //vertical geo/height axis: transpose values
   {
      // Retrieve all values from the internal structure
      int i,j=0;
      int nrInd = lmap.size(); // or nrIndexes_ - nrIndexesIn_
      vector <double> mat(nrInd * nrPoints_);
      IndexIterator ii;
      for ( ii = lmap.begin(); ii != lmap.end(); ii++ )
      {
         IndexesInfo *lInfo = (*ii).second;
         for (i = 0; i < nrPoints_; i++ )
            mat[j++] = lInfo->Values()[i];
      }

      // Write matrix data to the netcdf file
      const char* key = ncvar->name();  //get variable name
      vector <double> cp(nrIndexes_);
      for (i = 0; i < nrPoints_; i++ )
      {
         // 'Expand' option: write previous values
         if ( !this->ExpandFillValues(key,i,cp) )
            return;

         // Add values
         k = nrIndexesIn_;
         for (j = i; j < nrInd*nrPoints_; j+=nrPoints_ )
            cp[k++] = mat[j];

         // Update netcdf file
         ncvar->setCurrent(i);
         ncvar->put(cp,1,(long)nrIndexes_);
      }
   }
}

void HovToolkit::NcWriteValues (MvNcVar *ncvar, IndexesInfo *lInfo, int pos )
{
   // Get values from the structure
   vector <double> cp(nrPoints_);
   for (int i = 0; i < nrPoints_; i++ )
      cp[i] = lInfo->Values()[i];

   // Write values to a netcdf file
   ncvar->setCurrent(pos);
   ncvar->put(cp,1,(long)nrPoints_);
}

ParamInfo* HovToolkit::GetParamInfo( const string& key )
{
   // Check if field is valid
   ParamIterator ii = params_.find(key);
   if ( ii == params_.end() )
   {
      //setError(1,"ERROR: Could not find the requested field (getParamInfo)");
      marslog(LOG_EROR,"Could not find the requested field (getParamInfo)");
      return 0;
   }

   // Get parameter info
   return (*ii).second;
}

MvRequest HovToolkit::GetAppView ( MvRequest& viewRequest )
{
   // Check if the original view request has been already processed,
   // e.g. it has been already replaced by the CartesianView
   if ( (const char*)viewRequest("_ORIGINAL_REQUEST") )
      return viewRequest("_ORIGINAL_REQUEST");
   else
     return viewRequest;
}

bool HovToolkit::IsVisualise()
{
   // If the output is not 'visualisation' related: actions
   // "not visualise" or "prepare" (in certain cases), then
   // send back the "netCDF" request
    if ( actionMode_ != "visualise" &&
        !(actionMode_ == "prepare" && procType_ == HOV_DATA_MODULE_DROPPED) &&
        !(actionMode_ == "prepare" && procType_ == HOV_DATA_DROPPED) )
      return false;
   else
      return true;
}
bool HovToolkit::IsDataModule( const char* verb )
{
   return (verb && (strcmp(verb,"LINE_HOVM")     == 0 ||
                    strcmp(verb,"AREA_HOVM")     == 0 ||
                    strcmp(verb,"VERTICAL_HOVM") == 0 ||
                    strcmp(verb,"LINE_HOV" )     == 0 ||
                    strcmp(verb,"AREA_HOV")      == 0 ||
                    strcmp(verb,"HEIGHT_HOV")    == 0 ));
}

// Variable name in netCDF has some restrictions, e.g. can not
// start with a non-alphabetic letter.
string HovToolkit::getNetcdfVarname(string name)
{
   // Netcdf only accepts variable name starting with a alphabetic letter
   if ( !isalpha(name[0]) )
   {
      string newname = "a_" + name;
      return newname;
   }

   return name;
}

void HovToolkit::ExpandOpenNcFile()
{
   // Return if option is not Expand
   if ( !expand_ )
      return;

   // Initialize the input netcdf file
   if ( netcdfIn_ )
      delete netcdfIn_;

   netcdfIn_ = new MvNetCDF();
   netcdfIn_->init(netfnameIn_,'r');

   return;
}

void HovToolkit::ExpandCloseNcFile()
{
   if ( netcdfIn_ )
   {
      delete netcdfIn_;
      netcdfIn_ = 0;
   }

   return;
}

void HovToolkit::ExpandFillValues(MvNcVar *ncvar,int& pos)
{
   // Return if option is not Expand
   if ( !expand_ )
      return;

   // Copy values from the input netcdf file
   vector<double> vals;
   string key = ncvar->name();  //get variable name
   MvNcVar *ncvarIn = netcdfIn_->getVariable(key.c_str());
   long* edges = ncvarIn->edges();
   ncvarIn->get(vals,edges);
   ncvar->setCurrent(pos);
   ncvar->put(vals,edges[0],edges[1]);
   pos += edges[0];

   return;
}


bool HovToolkit::ExpandFillValues( const char* key, int pos, vector<double>& cp )
{
   // Return if option is not Expand
   if ( !expand_ )
      return true;

   // Get values from the input netcdf file
   vector<double> vals;
   MvNcVar *ncvarIn = netcdfIn_->getVariable(key);
   long* edges = ncvarIn->edges();
   ncvarIn->setCurrent(pos);
   ncvarIn->get(vals,1,edges[1]);

   // Copy values
   for (unsigned int i = 0; i < vals.size(); i++)
      cp[i] = vals[i];

   return true;
}

bool HovToolkit::ExpandGetTimeInfo()
{
   // Return if option is not Expand
   if ( !expand_ )
      return true;

   // Check input file
   if ( !netcdfIn_ )
   {
      marslog(LOG_EROR, "Input NetCDF file not accessible");
      return false;
   }

   // Copy 'time' values from the input file
   vector<double> vals;
   MvNcVar *tvar = netcdfIn_->getVariable(HOV_VARTIME);
   long *edges = tvar->edges();
   nrIndexesIn_ = edges[0];
   tvar->get(vals,edges);
   for ( unsigned int i = 0; i < vals.size(); i++)
      vtime_.push_back(vals[i]);

   return true;
}

MvRequest HovToolkit::buildMacroConverterRequest()
{
   const char* caux = origReq_.getVerb();
   MvRequest oreq( caux );
   oreq("_MACRO_DECODE_TAG") = 1;  // extra info for the Macro Converter
   oreq("_CLASS") = (const char*)origReq_("_CLASS") ? (const char*)origReq_("_CLASS") : caux;
   oreq("_VERB") = (const char*)origReq_("_VERB") ? (const char*)origReq_("_VERB") : caux;
   if ( strcmp((const char*)oreq("_CLASS"),"RETRIEVE") == 0  ||
        strcmp((const char*)oreq("_CLASS"),"GRIB") == 0 )
   {
      MvRequest retReq = origReq_("DATA");
      if ( (const char*)retReq("_NAME") )
         oreq("_NAME") = (const char*)retReq("_NAME");
   }
   else if ( (const char*)origReq_("_NAME") )
      oreq("_NAME") = (const char*)origReq_("_NAME");

   return oreq;
}
