/***************************** 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 *************************************/

// Header file for Xsect application. This is really 3 applications : Xsection,
// Average and Vertical Profile, depending on which request it receives.


#ifndef MVXSECTFRAME_H
#define MVXSECTFRAME_H

#include "Metview.h"
#include "MvNetCDF.h"

const double XMISSING_VALUE = 1.0E22;

// Used in the interpolation procedure to create more 'valid' points near 
// the orography. This is to avoid having empty areas in the plot.
const double THRESHOLD_INTERPOLATION = 3000.; // 30hPa

// Offset used to add extra levels when converting model to pressure levels
const float OFFSET_LEVELINFO = 1000.;  // 10hPa

// Level type data combined with specific fields
// Values are: model level, pressure level, model level + lnsp, model level + GHBC
// cML_UKMO_ND =  n;       // UKMO New Dynamics - defined elsewhere
enum { XS_ML, XS_PL, XS_ML_LNSP, XS_ML_GHBC };

const double cCDR = atan(1.0)/45.0; //-- conversion factor Degrees->Radians
                                    //-- atan(1)/45 = (PI/4)/45 = PI/180

const string XS_VARLEVEL   = "nlev";
const string XS_VARTIME    = "time";
const string XS_VARLAT     = "lat";
const string XS_VARLON     = "lon";
const string XS_VARLATPLOT = "lat_for_plot";
const string XS_VARLONPLOT = "lon_for_plot";

//-- Q&D trick: still use global vars
//-- but let them be non-const!
#ifdef INITIALISE_HERE
 int U_FIELD    = 131;
 int V_FIELD    = 132;
 int W_FIELD    = 135;
 int LNSP_FIELD = 152;
#else
 extern int	U_FIELD;
 extern int	V_FIELD;
 extern int	W_FIELD;
 extern int	LNSP_FIELD;
#endif

// Type of input request to be processed
enum { XS_DATA_DROPPED, XS_DATA_MODULE, XS_DATA_MODULE_DROPPED };

// Holds info about the values for 1 level.
class LevelInfo
{
public:

  double* XValues() { return xvalues_; }
  double   YValue() { return yvalue_; }

private:

  friend class ParamInfo;

  void UpdateLevel(double lev, bool modlev)
    { yvalue_ = modlev ? lev : lev * 100.; }

  LevelInfo(double lev, bool modlev) : xvalues_(0)
    { yvalue_ = modlev ? lev : lev * 100.; }

  LevelInfo() : xvalues_(0),yvalue_(DBL_MAX) {}

  double *xvalues_,yvalue_;
};

// Do a numeric comparison for the level map, instead
// of the normal alphabetic one.
class LevelCompare
{
public:

  bool operator() ( const string& s1,const string& s2 ) const
  {
      return ( atof(s1.c_str() ) < atof(s2.c_str() ) );
  }
};

typedef map<string,LevelInfo*, LevelCompare > LevelMap;
typedef LevelMap::iterator LevelIterator;


// Info about a parameter at one date, time and step.
// Contains  a LevelMap with info about the levels.
class ParamInfo
{
public:

  ParamInfo() : param_(0), date_(0), step_(0), time_(0),
                paramName_(" "), paramLongName_(" "), expver_("_"),
                units_(" "), levType_(" ") { }

  ParamInfo(int i1,int i2,int i3,int i4,string sn,string ln,string u,string lt) :
            param_(i1), date_(i2), step_(i4), time_(i3),
            paramName_(sn), paramLongName_(ln), expver_("_"),
            units_(u), levType_(lt) {}

  ~ParamInfo();

  int Date() { return date_;}
  int Time() { return time_;}
  int Step() { return step_;}
  int Parameter()  { return param_; }
  string ParamName() { return paramName_; }
  string ParamLongName() { return paramLongName_; }
  string Units() { return units_; }
  string LevelType() { return levType_; }
  bool IsSurface();
  const char *ExpVer() { return expver_.c_str(); }
  string ExpVerTitle();
  void ExpVer(const char *xx) { expver_ = xx; }
  void Level(double*, string, int, bool);
  LevelMap& Levels() { return levels_; }
  MvDate ReferenceDate();
  MvDate VerificationDate();

  void AddLevel(string);
  void UpdateLevels(bool);

  int NrLevels() { return levels_.size(); }
  void Print() {
    printf("P: %d %d %d %d %s %s %s\n",param_,date_,step_,time_,paramName_.c_str(),units_.c_str(),levType_.c_str());
  };

  friend int operator<(const ParamInfo &p1, const ParamInfo &p2)
  {
     return ( p1.param_ < p2.param_ || p1.date_ < p2.date_ || p1.time_ < p2.time_ || p1.step_ < p2.step_ );
  }

  double* getOneLevelValues( const string& );

private:

  int param_, date_, step_, time_;
  string paramName_;
  string paramLongName_;
  string expver_;
  string units_;
  string levType_;
  LevelMap levels_;
};

typedef map<string,ParamInfo * > ParamMap;
typedef ParamMap::iterator ParamIterator;
typedef pair<ParamIterator,bool> ParamInsertPair;
typedef pair<const string,ParamInfo*> ParamPair;


//------------------------------ General info for application.

class ApplicationInfo
{
public:

  // Constructor
  ApplicationInfo();

  double topLevel() { return topLevel_; }
  double bottomLevel() { return bottomLevel_; }
  void topLevel( double d ) { topLevel_ = d; }
  void bottomLevel( double d ) { bottomLevel_ = d; }

  // Handle geographical coordinates
  void computeLine( int );
  void computeLine( int&, MvField* );
  const vector<double>& getLongitude() const { return lon_; }
  const vector<double>& getLatitude()  const { return lat_; }
  const vector<double>& getLatitudeForPlotting()  const { return latForPlotting_; }
  const vector<double>& getLongitudeForPlotting()  const { return lonForPlotting_; }

  void setAreaLine( double, double, double, double );
  void getAreaLine( double&, double&, double&, double& );
  void getAreaLine( double* );
  double X1() { return x1_; }
  double Y1() { return y1_; }
  double X2() { return x2_; }
  double Y2() { return y2_; }

  void   Grid( double ns, double ew );
  double GridNS() { return gridNS_; }
  double GridEW() { return gridEW_; }

  double Ancos();
  double Ansin();

  void actionMode( string action ) { actionMode_ = action; }
  string actionMode() { return actionMode_; }

  void processType( int type ) { procType_ = type; }
  int  processType( ) { return procType_; }

  int NrPoints() { return nrPoints_; }
  void NrPoints( int np ) { nrPoints_ = np; }

  int NTimes() { return ntimes_; }
  void NTimes( int ntimes ) { ntimes_ = ntimes; }

  // Handle level info
  void levelType( int lt ) { levType_ = lt; }
  int  levelType() { return levType_; }
  int levelType( bool, bool );
  bool levelTypeFlag();
  void updateLevels( ParamMap& );
  void setMinMaxLevels( double, double, int=0 );
  void getMinMaxLevels( double&, double&, int& );
  void outputLevels( int n ) { nOutputLevels_ = n; }
  int outputLevels() { return nOutputLevels_; }

  // Get level values
  void getLevelValues( ParamInfo*, vector<double>& );

  // Compute indexes levels (vertical values)
  int computeLevelInfoUI( ParamInfo*, vector<double>&, double*, MvField& ); // from UI
  int computeLevelInfo ( ParamInfo*, vector<double>& );  // model/pressure level
  int computeLevelInfo ( ParamInfo*, vector<double>&, double*, MvField& ); // model level & LNSP
  int computeLevelInfo ( vector<double>& );  // compute equal separated levels
  int computeLevelInfoGHBC ( ParamInfo*, vector<double>&  ); // model level & GHBC

  bool computeLevelMatrixValues ( ParamMap&, const string&, vector<double>&, vector<double>&, MvField& );
  
  void scaleVelocity(ParamInfo*);
  void scaleVelocityPB(ParamInfo*);
  void scaleVelocity1(ParamInfo*,double);

  void InterpolateVerticala(vector<double>&,ParamInfo*,vector<double>&);
  void InterpolateVerticalb(MvField&,vector<double>&,ParamInfo*,double*,vector<double>&);
  void InterpolateVerticalGHBC(vector<double>&,ParamInfo*,ParamInfo*,vector<double>&);

  bool findModelPressure(LevelMap&, double, double, MvField&, LevelInfo*&, LevelInfo*&, double&, double&);

  bool findModelPressureGHBC(LevelMap&, LevelMap&, double, int, LevelInfo*&, LevelInfo*&, double&, double& );

  bool findPressure(LevelMap&, double, LevelInfo*&, LevelInfo*&);

  bool generateAverageData(ParamMap&, MvNetCDF&, const string&, MvField&, double*, double* , double*);

  // Handle vertical interpolation settings
  // Two ways to set a vertical interpolation flag: 
  // a) model to pressure level (setVerticalInterpolationFlag)
  // b) wind plotting (setVerticalInterpolationFlag(flag). This
  //    is because Magics is not doing the interpolation for winds.
  void setVerticalInterpolationFlag();
  void setVerticalInterpolationFlag( bool );
  bool isInterpolate() { return interpolate_; }

  bool viaPole()  {return viaPole_;}
  bool haveLatsForPlotting()       {return haveLatsForPlotting_;}
  void haveLatsForPlotting(bool b) {haveLatsForPlotting_ = b;}
  bool haveLonsForPlotting()       {return haveLonsForPlotting_;}
  void haveLonsForPlotting(bool b) {haveLonsForPlotting_ = b;}

  // Handle LNSP field
  bool haveLNSP() {return haveLNSP_;}
  void haveLNSP(bool flag) {haveLNSP_ = flag;}
  bool isLNSP(int iparam) { return iparam == LNSP_FIELD; }
  int  LNSP() { return LNSP_FIELD; }

  // Handle height-based coordinates
  bool haveGHBC() { return haveGHBC_; }
  void haveGHBC(bool flag) { haveGHBC_ = flag; }
  void setGHBC( int iparam) { paramGHBC_ = iparam; }
  bool isGHBC(int iparam) { return iparam == paramGHBC_; }
  int  GHBC() { return paramGHBC_; }

  // Handle horizontal point mode
  void horPointMode( string mode ) { hor_point_ = mode; }
  string horPointMode() { return hor_point_; }
  bool isHorInterpolate() { return hor_point_ == "INTERPOLATE"; }

  // Retrieve information about one parameter
  ParamInfo* getParamInfo( ParamMap&, const string& );
  ParamInfo* getParamInfo( ParamMap&, int, const string& );

  // Handle parameters retrieved from the User Interface
  string levelSelectionType() { return uiLSType_; }
  void levelSelectionType( const string& type ) { uiLSType_ = type; }

  int levelCount() { return uiLSCount_; }
  void levelCount( int count ) { uiLSCount_ = count; }

  vector<double> levelList() { return uiLSList_; }
  void levelList( vector<double>& );

  bool vLinearScaling() { return uiVLinearScaling_; }
  void vLinearScaling( bool bvs ) { uiVLinearScaling_ = bvs; }

private:

  double GetInterpolatedYValue(int);

  double x1_, x2_, y1_, y2_;
  double gridNS_, gridEW_;
  double topLevel_, bottomLevel_;

  int nrPoints_;             // number of horizontal points
  int nInputLevels_;         // number of input data model/pressure levels
  int nOutputLevels_;        // number of output levels (may be different than nInputLevels_)
  int ntimes_;               // number of times
  int levType_;              // level type data, e.g., XS_ML/XS_PL/XS_ML_LNSP/XS_ML_GHBC
  bool viaPole_;
  bool haveLatsForPlotting_;
  bool haveLonsForPlotting_;
  bool haveLNSP_;
  bool haveGHBC_;            // vertical coordinates flag
  int paramGHBC_;            // vertical coordinate parameter
  string actionMode_;        // e.g. "examine"/"save"/"execute"/...
  int procType_;             // processing type

  string hor_point_;         // horizontal point mode
  vector<double> lat_;       // geographical coordinates
  vector<double> lon_;
  vector<double> latForPlotting_;  // used when line is over a pole and Magics cannot plot the lat co-ordinates
  vector<double> lonForPlotting_;  // used when line is over a pole and Magics cannot plot the lon co-ordinates

  bool interpolate_;         // vertical interpolation flag

  // Parameters from the User Interface
  string uiLSType_;          // level selection type: FROM_DATA, COUNT, LEVEL_LIST
  int uiLSCount_;            // level selection count
  vector<double> uiLSList_;  // level list
  bool uiVLinearScaling_;    // vertical scaling: [true=LINEAR, false=LOG]

};

#endif
