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

#ifndef MvFieldSet_DEFINED
#define MvFieldSet_DEFINED

#include "MvRequest.h"
#include "MvFilter.h"
#include "MvGrid.h"

//-- activate next line if you need old ml-to-pressure methods
//for-the-moment//#define INCLUDE_OLD_CODE


const double cValueNotGiven  = DBL_MAX;
const int    cML_UKMO_ND     =  205;     //-- level type for UKMO New Dynamics data
const string cML_UKMO_ND_STR = "205";    //-- level type for UKMO New Dynamics data

#if 0
double interpolateValue( field* Grib, double lat, double lon );
#endif


class MvFieldSet;
class MvFieldState;
class MvGridBase;

//=============================================================================

//! Class to handle field data (stored as a GRIB message)
/*! MvField contains the physical structure of a field in GRIB format
 *  and its description as MvRequest.
 *  The class is based on libMars structure \c field, implemented in files
 *  \c libMars/field.h and \c libMars/field.c.
 */
class MvField {

	friend class MvFieldSet;
	friend class MvFieldState;

	MvRequest   Request;
	MvGridBase* mvgrid_;


public:

	//! Constructor
	MvField();

	//! Destructor
	~MvField();

	//! Copy constructor 1
	/*! Builds a new MvField object by copying the internal pointers
	 *  (GRIB data and request) from argument \c f .\n \n
	 *  Note that both objects will share the same data!
	 */
	MvField(const MvField& f);

	//! Copy constructor 2
	/*! Builds a new MvField object from libMars \c field struct \c f. \n \n
	 *  Note that the created object will share the GRIB data with struct \c f !
	 */
	MvField(field* f);

	//! Assignment operator
	/*! This operator copies the internal pointers (GRIB data and request)
	 *  of object pointed by argument \c f to the other MvField
	 *  in an assignment operation.\n \n
	 *  Note that both objects will share the same data!
	 */
	MvField& operator=(const MvField& f);

	//! Returns a pointer to the internal libMars \c field struct
	field* libmars_field() const { return mvgrid_ ? mvgrid_->field_ : 0; }

	//! Returns a pointer to the internal MvField object
	MvGridBase* mvGrid() const { return mvgrid_; }


	//! Assigns the internal \c field struct pointer to point to \c f
	void   set_libmars_field(field* f);

// Converters
	//! Returns a copy of the RETRIEVE command that describes the field contents.
	/*! Creates the request if the request has not been given in the constructor
	 *  or if not already created.
	 */
	MvRequest& getRequest();           // Create a MvRequest

// Data access, expanding is taken care of
	//! Non-OOP style method to calculate average in NS or WE direction over an area
	bool   averageAlong (double*n, double,double,double,double,int,int,
	                     double res=0.,bool useNearestPoint=true, double* coords=0);

	//! Obsolete method. Internally calls integrate()
	double averageAt(double,double,double,double);

	//! Interpolates a value for the given geographical location
	/*! Uses the internal MvGridBase object (method MvGridBase::interpolatePoint)
	 *  to do the calculation (which depend on the underlying grid
	 *  presentation.\n \n
	 *  Note that arguments are (longitude, latitude).
	 */
	double interpolateAt(double x_lon, double y_lat);

	//! Returns the value from a grid point nearest to the given geographical location
	/*! Uses the internal MvGridBase object (method MvGridBase::nearestGridpoint)
	 *  to do the search (which depends on the underlying grid presentation.\n \n
	 *  Use method nearestGridPointLocation() to get the location of the nearest grid point.\n \n
	 *  Note that arguments are (longitude, latitude).
	 */
	double nearestGridpoint(double x_lon, double y_lat);

	//! (Not used. Obsolete?)
	double cornerGridpoint(double,double,int);

	//! Get a matrix of values around the given geographical location
	/*! Uses the internal MvGridBase object (method MvGridBase::getMatrixNN)
	 *  to extract the requested matrix. Used mainly for satellite images.
	 */
	bool   getMatrixNN(double,double,MvMatrix&);

	//-- new versions --

	//! Evaluates the corresponding pressure level for the given model level \c ml
	/*! This method is for the vertical discretisation used at ECMWF, based on
	 *  model level layers.
	 *  The pressure level is evaluated using \c sp and the coefficient pairs ( \c C1
	 *  and \c C2 ) for the two surrounding "half-levels" ( \c ml-0.5 and \c ml+0.5 ),
	 *  stored in the GRIB header, using the layer mean.\n \n
	 *  If the GRIB header contains only one pair of coefficients \c C1 and \c C2
	 *  then method ML_to_Pressure_bySP() is called internally.
	 */
	double meanML_to_Pressure_bySP(double sp, int ml);

	//! Evaluates the corresponding pressure level for a given model level \c ml
	/*! Internally converts \c lnsp to \c sp and calls method meanML_to_Pressure_bySP()
	 */
	double meanML_to_Pressure_byLNSP(double lnsp, int ml);

	//! Evaluates the corresponding pressure level for the given model level \c ml
	/*! The pressure level is evaluated using \c sp and the coefficient pair \c C1
	 *  and \c C2 stored in the GRIB header.
	 */
	double ML_to_Pressure_bySP(double sp, int ml);

	//! Evaluates the corresponding pressure level for a given model level \c ml
	/*! Internally converts \c lnsp to \c sp and calls method ML_to_Pressure_bySP()
	 */
	double ML_to_Pressure_byLNSP(double lnsp, int ml);

	//! Computes the pressure level for a single model level
	/*! Arguments \c C1 and \c C2 are the vertical coordinate coefficient pair
	 *  and \c sp is the surface pressure.
	 */
	double pressureLevel(double C1, double C2, double sp)
	                        { return C1 + C2*sp; }

	//! Computes the pressure level for a model level layer
	double meanPressureLevel(double C1a, double C1b,
	                         double C2a, double C2b, double sp)
	                        { return (C1a+C1b + (C2a+C2b)*sp)/2.0; }

	//! Extracts vertical coordinate coefficient pair ( \c C1 , \c C2 ) from the GRIB header
	bool vertCoordCoefs( double& C1, double& C2 );

	//! Gets a vertical coordinate coefficient pair
	/*! Vertical coordinate coefficient pair ( \c C1 , \c C2 )
	 *  for level \c level is extracted from the GRIB header
	 */
	bool vertCoordCoefs( int level, double& C1, double& C2 );

	//! Returns the number of vertical coordinate coefficient pairs stored in the GRIB header
	int  vertCoordCoefPairCount();

	//! Grid point weighted integration over the given area
	/*! Weighting is done according to the geographical area that
	 *  each grid point represents, e.g. in cylindrical projection
	 *  grid points closer to the Poles represent smaller areas
	 *  than those closer to the Equator
	 */
	double integrate( double north, double west, double south, double east);

	//! Grid point weighted standard deviation calculated over the given area
	/*! About the weighting used, see integrate().
	 */
	double stdev( double north, double west, double south, double east);

	//! Grid point weighted covariance over the given area
	/*! About the weighting used, see integrate().
	 */
	double covar( MvField& other, double north, double west, double south, double east);

	//! Returns the offset of the GRIB message
	/*! This is the offset from the beginning of the input file, in bytes
	 */
	long getOffset() {  if (libmars_field()) return (long)libmars_field()->offset; else return -1; }

	//! Returns the length of the GRIB message, in octets (bytes)
	long getLength() {  if (libmars_field()) return libmars_field()->length; else return -1; }

// Data access, field should be expanded
// inline for speed
	//! Returns the number of grid points (or spectral coefficients) in the field
	int        countValues()     { return libmars_field()->value_count;  }

	//! Returns the value of the \c n'th element in the field
	/*! Element type depends on the field presentation:
	 *  it can be a value of a grid point or of a spectral coefficient.\n
	 *  For the first element \c n = 0 ( \c n = 0, 1, 2, ...)
	 */
	double& operator[](int n) { return libmars_field()->values[n]; }

	//! Returns \c true if the GRIB message contains a missing field
	bool isMissing() { return libmars_field()->missing; }

	//! Returns \c true if the grid presentation is cylindrical latitude-longitude grid
	bool isLatLon();

	//! Returns the number of grid points in latitude (j) direction
	int numberOfLat();

	//! Returns the number of grid points in longitude (i) direction
	int numberOfLon();

	//! Returns the Northern coordinate of the grid area
	double north();

	//! Returns the Southern coordinate of the grid area
	double south();

	//! Returns the Eastern coordinate of the grid area
	double east();

	//! Returns the Western coordinate of the grid area
	double west();

	//! Returns the grid interval in j direction (North-South)
	double gridNS();

	//! Returns the grid interval in i direction (West-East)
	double gridWE();

	//! Returns \c true if the field is derived, \c false if retrieved
	/*! Metview and MARS mark derived (computed) fields with a special
	 *  value in generatingProcess() octet.\n \n
	 *  In Metview visualisation derived fields
	 *  are indicated by an extra asterisk in the parameter name
	 *  in the plot title, and some fields get different unit conversions.
	 *  For instance retrieved temperature field values (stored in Kelvin
	 *  in GRIB message) are converted (by default) to Celsius for
	 *  plotting, but a computed temperature difference field is plotted
	 *  (by default) as is, without conversion.
	 */
	bool isDerived();

	//! Returns the GRIB Edition number of the current GRIB message
	int     edition(){ return mvgrid_->getLong("editionNumber"); }

	//! Returns the code number for 'creating centre' (e.g. 98 is ECMWF)
	int     centre();

	//! Returns the code number for the parameter; the actual parameter depends also on table()
	double  parameter();

	//! Returns the parameter name used by MARS
	string  parameterName();

	//! Assigns a different parameter code number
	void    setParameter(int v){mvgrid_->setLong("indicatorOfParameter",v);}

	//! Returns (GRIB 1) Table 2 version number
	int     table();

	//! Assigns a different Table 2 version number
	void    setTable(int v){ mvgrid_->setLong("gribTablesVersionNo",v);}

	//! Returns the code number for the 'generating process'
	/*! This code is also used by Metview/MARS to mark a field
	 *  as 'computed', see isDerived().
	 */
	int     generatingProcess();

	//! Returns the primary level value (octet 11, or combined octets 11&12 in GRIB 1)
	double  level();

	//! Returns the second level value (octet 12 in GRIB 1)
	double  level_L2();

	//! Returns the type of the level as a string
	string  levelTypeString();

	//! Returns the code number for the level type
	int     levelType();

	//! Returns the code number for the underlying geographical grid
	int     dataRepres();

	//! Returns the parameter name used by MARS
	Cached  marsName();   //-- from Emoslib Table 2 versions
	//Cached  fullName_DeleteMe();   //-- from Emoslib Table 2 versions

	//! Returns the parameter name used by Magics 6
	Cached  magicsName(); //-- from Magics Table 2 versions


	//OBSOLETE: Remove later
	//! Returns the pairing parameter for vector parameters, as a string
	/*! Method searches Magics 6 Extended GRIB Table 2 files for the current
	 *  parameter, to see if it is part of a vector pair (u/v, speed/direction,
	 *  vorticity/divergence). If this is a part of a vector, then returns
	 *  the other parameter code in format \c "ppp.tt" , where \c ppp is the code
	 *  for the parameter in Table 2 version \c tt. \n \n
	 *  Returns \c "no_pair" if this is a scalar parameter.
	 */
//	Cached  vectorPair(); //-- from Magics Table 2 versions

	//! [Obsolete!]] Returns \c true if the GRIB message has been fully expanded into memory
	/*! Expanded state was a requirement for the obsolete GRIBEX based implementation.
	 *  With the current GRIB_API implementation messages do not need to be
	 *  expanded in order to have access to data values, thus calling this
	 *  calling this function is obsolete.
	 */
	bool isExpanded(){ return mvgrid_->field_->shape == expand_mem; }

	//! This method reads the GRIB message into memory, in case it is not already there
	void    ensurePackedMem(); //-- if only on disk, get into memory

	//! This method sets the 'shape' (see libMars) of the field
	void    setShape(field_state newState) {set_field_state(mvgrid_->field_, newState);}

	//! Interface to GRIB_API to request a \c long valued keyword
	long    getGribKeyValueLong   (const string &key)
	                              {return mvgrid_->getLong  (key.c_str());}

	//! Interface to GRIB_API to request a \c double valued keyword
	double  getGribKeyValueDouble (const string &key)
	                              {return mvgrid_->getDouble(key.c_str());}

	//! Interface to GRIB_API to request a \c string valued keyword
	string  getGribKeyValueString (const string &key)
	                              {return mvgrid_->getString(key.c_str());}

	//! Interface to GRIB_API to request a \c long valued array keyword
	long    getGribKeyArrayLong   (const string &key, long   **lvals)
	                              {return mvgrid_->getLongArray   (key.c_str(), lvals);}

	//! Interface to GRIB_API to request a \c double valued array keyword
	long    getGribKeyArrayDouble (const string &key, double **dvals)
	                              {return mvgrid_->getDoubleArray (key.c_str(), dvals);}

	//! Interface to GRIB_API to set a \c long valued keyword
	bool    setGribKeyValueLong   (const string &key, long   value)
	                              {return mvgrid_->setLong  (key.c_str(), value);}

	//! Interface to GRIB_API to set a \c double valued keyword
	bool    setGribKeyValueDouble (const string &key, double value)
	                              {return mvgrid_->setDouble(key.c_str(), value);}

	//! Interface to GRIB_API to set a \c string valued keyword
	bool    setGribKeyValueString (const string &key, string &value)
	                              {return mvgrid_->setString(key.c_str(), value);}

	//! Returns \c true if the level type is model level
	bool isModelLevel();

	//! Returns \c true if the level type is surface level
	bool isSurface();

	//! Returns \c true if the level type is pressure level
	bool isPressureLevel();

	//! Returns \c true if the level type is depth level (e.g. ocean, soil)
	bool isDepth();

// Compare with NULL
	//! Operator for testing the validity of MvField object
	/*! This operator returns the pointer of the internal libMars
	 *  GRIB structure as a \c void* .
	 *  It is useful to compare the contents of a MvField to NULL
	 *  in a \c while or \c if statements.\n \n
	 *  Usage:
	 * <PRE>
	 *      MvField f;
	 *      if ( f ) ...
	 * </PRE>
	 */
	operator void*();

	//! Operator for testing the validity of MvField object
	/*! This operator compares the pointer of the internal libMars
	 *  GRIB structure to NULL and returns the result.\n \n
	 *  Usage:
	 * <PRE>
	 *       MvField f;
	 *       if ( !f ) ...
	 * </PRE>
	 */
	int operator !();

// Clone the data...

//	void clone(); //-- 080724/vk: this method was never implemented?


//	fortfloat* coordinateLevelsA();
//		{ return &Grib->rsec2[10]; }
//
//	fortfloat* coordinateLevelsB()
//		{ return coordinateLevelsA() + coordinateLevelsCount(); }

//	int coordinateLevelsCount();
//		{ return Grib->ksec2[11]/2;}

	//! Returns the location of the nearest grid point for the given geographical location
	/*! Use method nearestGridpoint() to get the value of the grid point
	 */
	MvLocation nearestGridPointLocation( const MvLocation& l );

	//! Returns the date as a double number
	/*! Integer part shows \c yyyymmdd and the decimal part shows the hours
	 *  as a fraction of a day. For instance 20080724.5 represents 12:00 UTC
	 *  on 24 July 2008, and 20070620.75 represents 18:00 UTC on 20 June 2007.
	 */
	double yyyymmddFoh();  // return date: yyyymmdd.f, f=fraction of hour

	//! Returns the forecast step in fractions of a day.
	/*! For instance 0.25 represents a forecast of 6 hours,
	 *  and 2.5 represents a forecast of 60 hours.
	 */
	double stepFoh();      // return step in fraction of hour

	//! Returns the step number from a step string.
	/*! Normally the two are basically the same (e.g. '72' -> 72), but
	 *  in some cases the step is a range (e.g. '96-120').
	 *  When we used grib_api <= 1.7.0, mars.step (string) was returning the 'endStep'
	 *  parameter (in this case 120). However, with grib_api 1.8.0, mars.step
	 *  returns the whole range. In this case, Metview was 'unthinkingly' converting
	 *  to a number, and the result was the first in the range (in this case 96).
	 *  The solution, it would appear, is to scan for a range and if so, then take
	 * the second number. This will at least give the same results as previous versions.
	 */
	static int stepRangeDecoder(const char *stepString);

	//! Returns the embedded grib_handle
	/*! This method is for "lower" level GRIB access, for cases
	 *  not available in MvField class
	 */
	grib_handle* getGribHandle(){ return mvgrid_->field_->handle; }

   //! Returns a list of locations (lat/long pairs), where the values of the input field
   //! are within the interval [a, b]. This function is similar to Macro function Find.
   int find( double min, double max, vector<double>& lat, vector<double>& lon );

protected:
	Cached getLineFromTable2( int line );
	Cached table2FileName();
	Cached MagicsTable2FileName();
        bool   MagicsTable2FileCandidate( const char* pref, int form, Cached& path );
};

// Stack based objects to change the state of gribs ....
// This is a meta-class
//! Base class for MvFieldLoader and MvFieldExpander
/*! These classes are now obsolete!
 *  They were needed when GRIB handling was done with the
 *  obsolete \c GRIBEX routine. The porting of Metview code
 *  to use \c GRIB_API has removed the requirement
 *  to expand GRIB messages as \c GRIB_API provides
 *  access to data and meta-data without the need to
 *  explicitly expand the message.
 */
class MvFieldState {
	MvField     *Field;
	field_state  OldState;
    bool         readonly;
protected:
	MvFieldState(const MvField&, field_state, field_state state_to_revert_to = unknown, bool readonlyflag = true);
	virtual ~MvFieldState();
};

// ... this one load a field from file into memory, still in grib

//! Loads a field (a GRIB message) from file to memory
/*! This class is now semi-obsolete (see MvFieldState).
 *  It is currently (July 2008) used in one Metview module
 */
class MvFieldLoader : public MvFieldState{
public:
	MvFieldLoader(const MvField& f, bool readonlyflag = true) : MvFieldState(f, packed_mem, unknown, readonlyflag) {}
};

// ... this one expand the grib

//! Expands (decodes) a field (a GRIB message)
/*! This class is now semi-obsolete (see MvFieldState), although
 *  it is still (July 2008) used in several Metview modules
 */
class MvFieldExpander : public MvFieldState{
public:
	MvFieldExpander(const MvField& f, bool readonlyflag = true) : MvFieldState(f, expand_mem, unknown, readonlyflag) {}
};


// ... this one expands the grib and then frees it when destroyed

//! Expands (decodes) a field (a GRIB message) and frees is when this pbject is destroyed
/*! This class is now semi-obsolete (see MvFieldState), although
 *  it is still (July 2008) used in several Metview modules
 */
class MvFieldExpandThenFree : public MvFieldState{
public:
	MvFieldExpandThenFree(const MvField& f, bool readonlyflag = true) : MvFieldState(f, expand_mem, packed_file, readonlyflag) {}
};

//===========================================================================

//! MvFieldSet contains a set of MvField objects
/*! This class is based on libMars structure \c fieldset, found
 *  in files \c field.h and \c field.c in directory src/libMars.
 */
class MvFieldSet {

	bool      Writable;
	fieldset* Fs;
	MvField** Fields;

// Private stuff

	void _init();
	void _clean();
	void _copy(const MvFieldSet&);

// No copy allowed

	void operator=(const MvFieldSet&);
	MvFieldSet(const MvFieldSet&);

public:

	//! Constructor, creates an empty fieldset
	MvFieldSet();                              // Create an empty fs

	//! Constructor, reads fields from a file
	/*! Reads the GRIB data file pointed by argument \c filename
	 *  and builds the fieldset that contains all the fields in the file.
	 */
	MvFieldSet(const char* filename);	           // Contruction from a file name

	//! Constructor, creates a fieldset from a request
	/*! Builds a fieldset from a GRIB command.
	 *  This command specifies the file name in parameter PATH.
	 *  The order of the fields within the file may be specified
	 *  by parameter INDEX or by parameters OFFSET and LENGTH.
	 */
	MvFieldSet(const MvRequest& req); // Contruction from a request

	//! Destructor
	~MvFieldSet();


// Utility methods

	//! Returns the number of fields in the fieldset
	int countFields(void);               // number of fields

	//! Returns one field from the fieldset
	/*! Argument \c n defines which field to return.
	 *  First field is returned when \c n = 0 (\c n = 0, 1, 2,...).
	 */
	MvField& operator[](int);            // Return a single field

	//! Returns a request describing the fieldset
	/*! Returns a GRIB command that describes the position of the fields
	 *  within the file. The GRIB command contains the parameters:
	 * <PRE>
	 *      PATH - file name.
	 *      TEMPORARY - 0 or 1. When 1, file may be deleted
	 *      OFFSET - byte offset of field within file.
	 *      LENGTH - size of field in bytes.
	 * </PRE>
	 */
	MvRequest getRequest(); // Create a MvRequest
//	MvRequest getRequest(bool clone=false); // original had an argument which is never used

	//! Appends field \c f to the fieldset
	void operator+=(const MvField& f);     // Append a field

};

//=============================================================================

//! Allows changing the accessing order of MvField objects in MvFieldSet
/*! This class can be used to change the accessing order of fields in MvFieldSet.
 *  Fields can be sorted and - using one MvFilter object - filtered.\n \n
 *  Sorting and filtering are done inside this class (MvFieldSet object
 *  itself is not changed). Fields are accessed using the iterator operator()().
 */
class MvFieldSetIterator {

	int         Count;
	int         Current;
	int        *Order;

	MvFieldSet *FieldSet;
	MvField    *Field;
	MvFilter    Filter;

public:

	//! Constructor that associates fieldset \c fs to the iterator
	MvFieldSetIterator(MvFieldSet& fs);

	//! Destructor
	~MvFieldSetIterator();

	//! Method that associates MvFilter \c f to the iterator.
	/*! Only fields that satisfy the filter conditions will be
	 *  returned by the iterator
	 */
	void setFilter(const MvFilter& f);

	//! This method specifies the sorting criteria to be used when accessing the fieldset
	/*! Argument \c name defines which parameter of the field description command
	 *  will be used as a sorting key.
	 *  Argument \c ord specifies if fields will be sorted in
	 *  ascending ('<') or descending ('>') order.
	 *  When more than one parameter are defined as sorting key,
	 *  the first one varies more quickly
	 */
	void sort(const char* name, char ord = '<'); //  '<' = ascending, '>' = descending

	//! Returns the next iterated field
	/*! This operator sequentially returns a MvField from a MvFieldSet
	 *  in the order specified by method sort() and - if used - MvFilter object.\n \n
	 *  Usage:\n
	 * <PRE>
	 *  MvRequest  grib;             // a GRIB command
	 *  MvFieldSet fs(grib);         // construct field set
	 *
	 *  MvFieldSetIterator iter(fs); // define an iterator
	 *  iter.sort("STEP,'>'");       // sort STEP in descending order
	 *  iter.sort("DATE");           // sort DATE in ascending order
	 *
	 *  MvFilter level("LEVEL");
	 *  MvFilter param("PARAM");
	 *
	 *  iter.setFilter(param==129 && level>5 && level<=20);
	 *
	 *  MvField field;
	 *
	 *  while(field = iter())
	 *  {
	 *  	// fields that have PARAM=129 and LEVEL in
	 *  	// the range [6...20] may be accessed here
	 *  }
	 * </PRE>
	 */
	MvField& operator()();            // returns NULL at end of list

	//! Rewinds the iterator
	void rewind()                 { Current = 0; }

};
#endif
