/***************************** 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 "Thermo.h"
#include "BufrThermo.h"
#include "GribThermo.h"
#include "MvException.h"

// Thermo data types
const static int N_DATATYPE = 3;
const static string DATATYPE[N_DATATYPE] = {"GRIB_THERMO","BUFR_THERMO","ODB_THERMO"};

Thermo::Thermo (const char* kw) :
             MvService(kw),
             actionMode_("prepare"),
             procType_(THERMO_DATA_MODULE)
{
}

void Thermo::serve(MvRequest& in,MvRequest& out)
{
cout << "request IN" << endl;
in.print();

   // Get Input parameters
   origReq_ = in;
   if ( !this->getInputParameters(in) )
      return;

   // It is a netCDF data and it has been already processed.
   // Build the output request
   if ( dataRequest_.getVerb() == string("NETCDF") )
      this->createProcessedOutputRequest( out );
   else
   {
      // Create a temporary netCDF file name
      ncFileName_ = marstmp();

      // Process data
      if ( !this->processData() )
         return;

      // Build the output request
      if ( !this->createOutputRequest(out) )
         return;
   }

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

cout << "request OUT" << endl;
out.print();

   return;
}

// 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
// 2. DATA_MODULE_DROPPED: Data module dropped into a View
// 3. DATA_MODULE: Process a data module
// 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 ThermoData 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 Thermo::getInputParameters( MvRequest& in )
{
   // Retrieve data request
   if ( !in.countValues("DATA") )
      throw MvException("Thermo: No Data files specified!");

   in.getValue(dataRequest_,"DATA");

   // Get and save action mode. Default value is "prepare"
   actionMode_ = (const char*)in("_ACTION") ? (const char*)in("_ACTION") : "prepare";

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

   // Check if a data module was dropped into the view
   const char* verb = (const char*)in("_VERB");
   bool moduleDropped = this->isDataModule(verb);

   // Check the type of input request to be processed
   MvRequest commonRequest;
   if ( viewRequest )
      procType_ = moduleDropped ? THERMO_DATA_MODULE_DROPPED : THERMO_DATA_DROPPED;
   else
      procType_ = THERMO_DATA_MODULE;

   // Type of input request is a DATA_DROPPED into a View
   // Get information from the View request
   if ( procType_ == THERMO_DATA_DROPPED )
   {
      // Retrieve 'original' request from the View
      commonRequest = this->getAppView( viewRequest );

      // Retrieve parameters
      if ( !getAppParameters(commonRequest) )
         return false;
   }
   // Process a data module
   // Get information from the module icon parameters
   else if ( procType_ == THERMO_DATA_MODULE )
   {
      // Retrieve parameters
      if ( !this->getAppParameters(in) )
         return false;
   }
   // Process a data module dropped into a view
   // Get information from the module and view. Check if they are compatible.
   else if ( procType_ == THERMO_DATA_MODULE_DROPPED )
   {
      // Retrieve 'original' request from the View
      commonRequest = this->getAppView( viewRequest );

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

      // Retrieve application specific parametes
      if ( !this->getAppParameters( modRequest ) )
         return false;

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

   return true;
}

bool Thermo::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_ == THERMO_DATA_MODULE_DROPPED) &&
        !(actionMode_ == "prepare" && procType_ == THERMO_DATA_DROPPED)
      )
         return false;

   return true;
}

bool Thermo::isDataModule ( const char* verb )
{
   if ( !verb )
      return false;

   for ( int i = 0; i < N_DATATYPE; i++)
   {
      if ( verb == DATATYPE[i] )
         return true;
   }

   return false;
}

MvRequest Thermo::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;
}

void Thermo::createProcessedOutputRequest( MvRequest& out )
{
   // Create NetCDF output request
   MvRequest data = origReq_.getSubrequest("DATA");
   MvRequest out1 = data.getSubrequest("_VISUALISE");
   MvRequest viewReq = data.getSubrequest("_CARTESIANVIEW");
   data.unsetParam("_VISUALISE");        // to avoid duplication of info
   data.unsetParam("_CARTESIANVIEW");    // 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
   out = viewReq + out1;
}

MvRequest Thermo::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;

   // Check if the input data is a MARS retrieval (RETRIEVE) 
   // or a file (GRIB/BUFR)
   if ( strcmp((const char*)oreq("_CLASS"),"RETRIEVE") == 0 ||
        strcmp((const char*)oreq("_CLASS"),"GRIB") == 0     ||
        strcmp((const char*)oreq("_CLASS"),"BUFR") == 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;
}

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

int main(int argc,char **argv)
{
   MvApplication theApp(argc,argv);

   GribThermo gribThermo;
   BufrThermo bufrThermo;

   // The applications don't try to read or write from pool, this
   // should not be done with the new PlotMod.
   gribThermo.saveToPool(false);
   bufrThermo.saveToPool(false);

   theApp.run();
}
