/***************************** 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, "Spectra-> 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")) {
        string serr = "Spectra-> ";
        serr += penv;
        setError(1, serr.c_str());
        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();
      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,"Spectra-> 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,"Spectra-> 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,"Spectra-> 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, "Spectra-> 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();
}
