/***************************** 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"),
               windFlag_(false)
{ }

GribThermo::GribThermo(const char*kw) :
               Thermo(kw),
               windFlag_(false)
{ }

GribThermo::~GribThermo() {}

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

   // Retrieve point selection
   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
      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
   string ncFileName = this->ncFileName();
   MvNetCDF netcdf(ncFileName,'w');
   if ( !netcdf.isValid() )
   {
      setError(1,"ERROR: 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( 13, "Thermo: 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 )
      {
         cerr << "Exception caught: " << e.what() << endl;
         setError( 13, 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;
#if 0
   MvRequest cdfreq("NETCDF");
   cdfreq("PATH") = netcdfName.c_str();
   out = out + cdfreq;
   return true;

      // It is a netCDF data and it has been already processed.
      // Build the output request
      //out = this->createOutputRequest( in );
/////
   // Initialize fildset iterator
   MvFieldSet fs(data);
   MvFieldSetIterator iter(fs);
   MvField field = iter();

   // Retrieve initial info
   this->getInitialFieldInfo(field,appInfo,north,south,east,west);

   // First loop: build field keystring and get the Level info.
   // The LNSP matrix data is also computed, but the other matrices
   // are computed in the second loop.
   iter.rewind();
   while( field = iter() )
   {
      // Retrieve info from the field request
      MvRequest rq=field.getRequest();
      iparam = rq("PARAM");
      const char *lev = rq("LEVELIST");
      istep  = rq("STEP");

      // Check that this field's vital statistics match those of the others
      // - this is important only in the Average application
      if ( !fieldConsistencyCheck(field,north,south,east,west) )
         return false;

      // Calculate the data matrix for LNSP field
      generateKey(keystr,iparam,idate,itime,istep,expver);
      if ( iparam == LnPress )
      {
         fillValues( appInfo,field,splin );
         ParamInfo* par = new ParamInfo();
         params.insert(ParamPair(keystr,par) );
      }
      else
      {
         if ( iparam == U_FIELD )
            foundU = true;
         else if ( iparam == V_FIELD )

         ParamInfo* par = new ParamInfo();

         ParamInsertPair inserted = params.insert(ParamPair(keystr,par));
         ParamInfo *pp = (*(inserted.first)).second;
      }
   } //end while (first loop)

   // Check if fieldset is compatible with the application
   if ( this->consistencyCheck( appInfo, params ) == false )
   {
      setError(1,"ERR"); return false;
   }

   // Create a temporary netCDF file
   ...
   // Write netCDF global attributes
   if ( !this->writeGlobalAttributesNetcdf(netcdf,params,appInfo) )
      return false;

   // Write initial variables to the netCDF file: levels, time,
   // geographical coordinates
   iter.rewind();
   field = iter();
   if ( !this->writeLevelInfoNetcdf(netcdf,appInfo,params,&field,splin) )
      return false;
   if ( !this->writeTimeInfoNetcdf(netcdf,params,ntimes) )
      return false;
   if ( !this->writeGeoCoordsNetcdf( netcdf, appInfo ) )
      return false;

   // Initialize variables
   string lastKey        = "FIRSTFIELD";
   int currentGenerated  = 0, lastNrGenerated = -1;
   MvField lastField;

   // Initialize fieldset iterator
   iter.rewind();
   iter.sort("LEVELIST");

   // Second loop: generate data
   ParamIterator paramIter;
   while( field = iter() )
   {
      currentGenerated++;
      MvRequest r = field.getRequest();
      iparam = r("PARAM");

      // Generate key
      generateKey(keystr,iparam,idate,itime,istep,expver);

      // Calculate the data matrix for non LNSP field
      if ( iparam != LnPress ) // Already done for lnsp
         bool ok = fillValues(appInfo,field,xint);

      // Write out the data if we have the end of what can be grouped together.
      if ( lastKey != keystr && lastKey != string("FIRSTFIELD") )
      {
         if ( !generateData(appInfo,params,netcdf,lastField,lastKey) )
         {setError(1,"");return false; }
         lastNrGenerated = currentGenerated;
      }

      lastKey = keystr;
      lastField = field;
   }  // end while (second loop)

   // Write out the last input field data
   if ( lastNrGenerated <=  currentGenerated )
   {
      if ( !generateData(appInfo,params,netcdf,lastField,lastKey) )
         setError(1,"");return false;
   }

   // Write additional fields, if it is needed
   if ( ! this->generateExtraData(netcdf,appInfo,params,iter) )
      setError(1,"C"); return false;

   // Close netCDF file
   netcdf.close();

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

   // Create output request
   out = this->createOutputRequest( appInfo, netcdfName, parInfo );

   // Free memory for parameters
   for (paramIter = params.begin(); paramIter != params.end(); paramIter++)
      delete (*paramIter).second;
#endif
}

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
      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;
}
