/***************************** 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 "Spectra.h"
#include "MvFortran.h"


#ifdef FORTRAN_UPPERCASE
#define spectra_ SPECTRA
#endif

#ifdef FORTRAN_NO_UNDERSCORE
#define spectra_ spectra
#endif

extern "C" void spectra_(void);

#define SPECGRAPH	1
#define SPECCONTOUR 	0

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

   // Create a temporary netCDF file name
   ncFileName_ = marstmp();

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

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

   // Compute Spectra
   MvRequest spec;
   if ( !computeSpectra(spec) )
      return;

   // Build the output request
   if ( !this->createOutputRequest(spec,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;
}

bool SpectraF :: getDataGen(MvRequest& in)
{
   // Get fields
   MvRequest grib = in.getSubrequest("DATA");
   if( grib == (request*)NULL )
   {
      setError(1,"Grib data not supplied ...");
      return FALSE;
   }

   // Organise fields to send to the Fortran routine
   MvFieldSet fset(grib);           // field set
   MvFieldSetIterator fsiter(fset); // field set iterator
   MvField field;                   // auxiliary field

   fsiter.sort("STEP");
   while( field = fsiter() )
      Sfset_ += field;

   // Get Diurnal Cycle, Mtrunc and plot title
   Sdc_ = ( (const char*)in("DIURNAL_CYCLE") && strcmp(in("DIURNAL_CYCLE"),"YES") == 0 ) ? TRUE : FALSE;

   Smtrunc_ = (const char*)in("TRUNCATION") ? (double)in("TRUNCATION") : 213.;

   return TRUE;
}

bool SpectraF :: computeSpectra(MvRequest& spec)
{
   MvFortran spectraf("Spectra");  // fortran subroutine

   // Call the fortran routines
   spectraf.addParameter(Sfset_);   // setup fortran params
   spectraf.addParameter(Smtrunc_);
   spectraf.addParameter(Stype_);
   spectraf.addParameter(Sdc_);
   spectraf.addParameter(Snl24int_);
   spectraf.addParameter(Sntmin_);
   spectraf.addParameter(Sntlim_);

   putenv("SPECTRA_ENV=OK");
   spectra_();
   char* penv = getenv("SPECTRA_ENV");
   if( strcmp(penv,"OK") )
   {
      setError(1,penv);
      return FALSE;
   }

   // Get the result
   spec = spectraf.getResult();
   return TRUE;
}

string SpectraF :: getTitle(MvRequest& spec)
{
   string title;        // title text
   int    nfldr;        // field code
   int    levelType;    // field level type
   int    level;        // field level
   int    time;         // field time
   long   date;         // field date (YYMMDD)
#if 1
   levelType = (int)spec("VALUE");
   spec.advance();
   level = (int)spec("VALUE");
   spec.advance();
   time = (int)spec("VALUE");
   spec.advance();
   date = (long)spec("VALUE");
   spec.advance();
   nfldr = spec("VALUE");
   spec.advance();
   return string("TEST");
#else
   char   clev[100];    // buffer for holding level string
   char   caux[100];    // auxiliary variable
//   MvFieldSet fset(grib);           // field set
//   MvFieldSetIterator fsiter(fset); // field set iterator

//      MvFieldSetIterator iter(Sfset_);
//   MvField field = iter();
   MvField field = Sfset_[0];
MvFieldExpander expa( field );
   cout << field.getGribKeyValueString("shortName") << endl;
   cout << field.getGribKeyValueString("name") << endl;
   cout << field.getGribKeyValueString("units") << endl;
   cout << field.getGribKeyValueString("levelType") << endl;
   MvField ff = Sfset_[0];
      MvRequest rq=field.getRequest();
      rq.print();
      int iparam = rq("PARAM");
      const char *lev = rq("LEVELIST");
      int istep  = rq("STEP");
      int idate  = rq("DATE");
      int itime  = rq("TIME");
      const char* expver = rq("EXPVER");


   MvGribParStruct* parlist = NULL;  // list of parameters table
   MvGribTEntryStruct* pardesc;      // parameter description

   // Get field level,time,date and code
   levelType = (int)spec("VALUE");
   spec.advance();
   level = (int)spec("VALUE");
   spec.advance();
   time = (int)spec("VALUE");
   spec.advance();
   date = (long)spec("VALUE");
   spec.advance();
   nfldr = spec("VALUE");
   spec.advance();

   if (levelType == 100)
      sprintf(clev, "%d mb", level);
   else
      sprintf(clev, "level %d", level);

   sprintf(caux,", %s. %dZ  %06ld verifying date  ",clev,time,date%1000000);

   // Get field name
   if ( !parlist )
      parlist = MvGribParOpen();

   if ( !parlist )
   {
      setError(1,"Read error at list of parameters table ...");
      title = caux;
      return title;
   }
   pardesc = MvGribParEntry (parlist,128, nfldr);

   // Set title
   title = pardesc->shortName;        // pardesc->longName;
   if( title == "" || title == "?" )  // field code not found
   {
      char caux1[100];
      sprintf(caux1,"Spectra : Warning - Parameter code %d not found in grib table 2, version 128",nfldr);
      setError(0,caux1);
      sprintf(caux1,"%d",nfldr);
      title = caux1;
   }
   title = title + caux;

   return title;
#endif
}

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

bool Specgraph :: getInputParameters(MvRequest& in)
{
   // Get general data
   Stype_ = SPECGRAPH;
   if ( !getDataGen(in) )
      return FALSE;

   // Set horizontal axis min/max values
   Sntmin_ = 1;
   Sntlim_ = (int)Smtrunc_;

   // Get Smoothing and Tension
   Ssmooth_ = (strcmp(in("SMOOTHING"),"YES") == 0) ? TRUE : FALSE;
   Stension_ = (const char*)in("TENSION");
   Snl24int_ = FALSE;   // default value

   return TRUE;
}

bool Specgraph :: createOutputRequest(MvRequest& spec, MvRequest& out)
{
   int instep;    // initial step value
   int ncurves;   // number of curves
   int npts;      // number of points
   int i,j;       // auxiliary variables

   out.setVerb("MGRAPH");        // output graph request
   MvRequest curveset;           // set of curves
   MvRequest haxis("MAXIS");     // X-axis
   MvRequest vaxis("MAXIS");     // Y-axis

   // Set title
   out("TITLE") = getTitle(spec).c_str();

   // Set horizontal and vertical axis
   BuildAxis(vaxis,haxis,spec);
   out("HORIZONTAL_AXIS") = haxis;
   out("VERTICAL_AXIS")   = vaxis;

   // Get number of curves
   ncurves = (int)spec("VALUE");
   spec.advance();

   // Build all curves
   for(i = 0; i < ncurves; i++)
   {
      MvRequest curve("INPUT_XY_POINTS");
      MvRequest mgraph("MGRAPH");

      // Get initial step value and number of spectra values
      instep = (int)spec("VALUE");
      spec.advance();
      npts = (int)spec("VALUE");
      spec.advance();

      // Loop for all points
      for (j = 0; j < npts; j++)
      {
         curve("INPUT_Y_VALUES") += (double)spec("VALUE");
         spec.advance();
         curve("INPUT_X_VALUES") += (double)spec("VALUE");
         spec.advance();
      }

      // Build line parameters
      //BuildCurveLineParam(pgraph,i+1,instep);

      // Set curve
curve("_VERB") = "INPUT_XY_POINTS";
out = curve + mgraph;
      curveset = curveset + curve + haxis + vaxis + mgraph;
   }

   //F plotspec("CURVES") = curveset;
   return TRUE;
}

void Specgraph :: BuildAxis(MvRequest& vaxis,MvRequest& haxis,MvRequest& spec)
{
   float ymin,ymax;  // vertical axis limits

   // Get vertical axis limits
   ymin = (double)spec("VALUE");
   spec.advance();
   ymax = (double)spec("VALUE");
   spec.advance();

   #if 0
   if (Svaxis_)   // user supplied a vertical axis
   {
      vaxis = Svaxis_.justOneRequest();
      if(strcmp(vaxis("AXIS_ORIENTATION"),"VERTICAL") != 0)
      {
         setError(1," Axis orientation supplied by user is not correct ...");
         return;
      }

      if(!(const char*)(vaxis("AXIS_MIN_VALUE")))
         vaxis("AXIS_MIN_VALUE") = ymin;
      if(!(const char*)(vaxis("AXIS_MAX_VALUE")))
         vaxis("AXIS_MAX_VALUE") = ymax;
      if(!(const char*)vaxis("AXIS_TICK_LABEL_FORMAT"))
         vaxis("AXIS_TICK_LABEL_FORMAT") = "(G7.1)";
   }
   else          // user not supplied a vertical axis
#endif
   {
      vaxis("AXIS_ORIENTATION") = "VERTICAL";
      vaxis("AXIS_TICK_POSITIONING") = "REGULAR";
      vaxis("AXIS_MIN_VALUE") = ymin;
      vaxis("AXIS_MAX_VALUE") = ymax;
      vaxis("AXIS_TICK_LABEL_ORIENTATION") = "HORIZONTAL";
      vaxis("AXIS_TICK_LABEL_FORMAT") = "(G7.1)";
      vaxis("AXIS_TITLE_TEXT") = "Amplitude";
   }

#if 0
   if (Shaxis_)     // user supplied a horizontal axis
   {
      haxis = Shaxis_.justOneRequest();
      if((const char*)haxis("AXIS_ORIENTATION"))
      {
         setError(1," Axis orientation supplied by user is not correct ...");
         return;
      }
   }
   else             // user not supplied a horizontal axis
#endif
   {
      haxis("AXIS_ORIENTATION") = "HORIZONTAL";
      haxis("AXIS_TICK_POSITIONING") = "REGULAR";
      haxis("AXIS_MIN_VALUE") = float(Sntmin_);
      haxis("AXIS_MAX_VALUE") = float(Sntlim_);
      haxis("AXIS_TICK_LABEL_FREQUENCY") = 2;
      haxis("AXIS_TICK_LABEL_FORMAT") = "(F4.0)";
      haxis("AXIS_TITLE_TEXT") = "Legendre polynomial order (n)";
   }
}

void Specgraph :: BuildCurveLineParam(MvRequest& pgraph,int nc,int istep)
{
   char caux[20];    // auxiliary variable

   pgraph("GRAPH_TYPE") = "CURVE";
   pgraph("GRAPH_LINE_THICKNESS") = 2;
   if(Ssmooth_)
   {
      pgraph("GRAPH_CURVE_METHOD") = "ROUNDED";
      pgraph("GRAPH_CURVE_INTERPOLATION") = Stension_.c_str();
   }
   else
      pgraph("GRAPH_CURVE_METHOD") = "STRAIGHT";

   pgraph("LEGEND") = "ON";
   if(istep == 0)
      pgraph("LEGEND_USER_TEXT") = "ANALYSIS";
   else
   {
      sprintf(caux,"%d HOUR FORECAST",istep);
      pgraph("LEGEND_USER_TEXT") = caux;
   }

   int iaux = nc;
   if(iaux > 5) iaux = (nc%5) + 1;
   switch(iaux)
   {
      case 1 :
         pgraph("GRAPH_LINE_COLOUR") = "BLUE";
         pgraph("GRAPH_LINE_STYLE")  = "SOLID";
         break;
      case 2 :
         pgraph("GRAPH_LINE_COLOUR") = "RED";
         pgraph("GRAPH_LINE_STYLE")  = "DASH";
         break;
      case 3 :
         pgraph("GRAPH_LINE_COLOUR") = "GREEN";
         pgraph("GRAPH_LINE_STYLE")  = "DOT";
         break;
      case 4 :
         pgraph("GRAPH_LINE_COLOUR") = "CYAN";
         pgraph("GRAPH_LINE_STYLE")  = "CHAIN_DOT";
         break;
      case 5 :
         pgraph("GRAPH_LINE_COLOUR") = "PINK";
         pgraph("GRAPH_LINE_STYLE")  = "CHAIN_DASH";
         break;
   }
}

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

bool Speccontour :: getInputParameters(MvRequest& in)
{
   // Get general data
   Stype_ = SPECCONTOUR;
   if ( !getDataGen(in) )
      return FALSE;

   // Set vertical axis min/max values
   Sntmin_ = 1;
   Sntlim_ = (int)Smtrunc_;

   // Get Nl24int and Hilorad
   Snl24int_ = (strcmp(in("NL24INT"),"YES") == 0) ? TRUE : FALSE;
   Shilorad_ = (const char*)in("HILORAD") ? (double)in("HILORAD") : 1.;

   return TRUE;
}

bool Speccontour :: createOutputRequest(MvRequest& spec, MvRequest& out)
{
   FILE*  fp;                       // pointer to temporary file
   char   caux[50];                 // auxiliary variable
   int    dim1,dim2;                // input_field limits
   int    i,j;                      // auxiliary variables
   int    bmag;                     // field scale
   string title;                    // title
   string pname = marstmp();        // temporary path file
   string fname = mbasename(pname.c_str()); // temporary file name

   MvRequest matrix("MATRIX");      // output matrix request
   MvRequest pcont("MCONT");        // output contour request
   MvRequest haxis("MAXIS");        // X-axis
   MvRequest vaxis("MAXIS");        // Y-axis

   // Set title
   title = getTitle(spec);
   bmag = (int)spec("VALUE");
   spec.advance();
   if(bmag != 0)
      sprintf(caux,"    Field scaled by 10**%d",bmag);
   else
      strcpy(caux," ");

   title = title + caux;

   // Set horizontal and vertical axis
   BuildAxis(vaxis,haxis,spec);

   // Create temporary file
   if(!(fp = fopen(pname.c_str(),"w")))
   {
      setError(1," Temporary file open error ...");
      return FALSE;
   }

   dim1 = (int)spec("VALUE");	// get matrix dimensions
   spec.advance();
   dim2 = (int)spec("VALUE");
   spec.advance();
   for (i = 0; i < dim1; i++)
   {
      for (j = 0; j < dim2; j++)
      {
         fprintf (fp,"%e ",(double)spec("VALUE"));
         spec.advance();
      }
   }
   fclose(fp);

   // Create Matrix request
   matrix("_NAME") = fname.c_str();
   matrix("PATH")= pname.c_str();
   matrix("FORMAT") = "ASCII";
   matrix("DIMENSION") = dim2;
   matrix("DIMENSION") += dim1;
   matrix("TITLE") = title.c_str();
   matrix("OFFSET") = 0;
   matrix("TEMPORARY") = 1;

   pcont("INPUT_FIELD_ORGANIZATION") = "FITTED";
   pcont("CONTOUR_HILO_SUPPRESS_RADIUS") = Shilorad_;
   pcont("CONTOUR_LEVEL_SELECTION_TYPE") = "COUNT";
   pcont("CONTOUR_LEVEL_COUNT") = 10;

   out = haxis + vaxis + matrix + pcont;

   return TRUE;
}

void Speccontour :: BuildAxis(MvRequest& vaxis,MvRequest& haxis,MvRequest& spec)
{
   int   i;               // auxiliary variable
   int   iilev;           // number of steps
   float ymin,ymax,yinc;  // vertical axis limits

   // Get vertical axis limits
   ymin = (double)spec("VALUE");
   spec.advance();
   ymax = (double)spec("VALUE");
   spec.advance();
   yinc = (double)spec("VALUE");
   spec.advance();

   vaxis("AXIS_ORIENTATION") = "VERTICAL";
   vaxis("AXIS_TICK_POSITIONING") = "REGULAR";
   vaxis("AXIS_MIN_VALUE") = ymin;
   vaxis("AXIS_MAX_VALUE") = ymax;
   vaxis("AXIS_TICK_INTERVAL") = yinc;

   // Get horizontal axis values
   iilev = (int)spec("VALUE");
   spec.advance();
   haxis("AXIS_ORIENTATION") = "HORIZONTAL";
   haxis("AXIS_MIN_VALUE") = 1.;
   haxis("AXIS_MAX_VALUE") = float(iilev);
   haxis("AXIS_TICK_INTERVAL") = 1.;
   haxis("AXIS_TICK_LABEL_TYPE") = "LABEL_LIST";
   for(i = 0; i < iilev; i++)
   {
      haxis("AXIS_TICK_LABEL_LIST") += (int)spec("VALUE");
      spec.advance();
   }
}

int main(int argc,char **argv)
{
   MvApplication theApp(argc,argv);
   Specgraph specgraph("SPEC_GRAPH");
   Speccontour  speccont("SPEC_CONTOUR");

   theApp.run();
}
