// MvObs.h,   vk july94
//        rev vk 010724

#ifndef MvBufrObs_DEFINED
#define MvBufrObs_DEFINED

#include <string>
#include "inc_iostream.h"
//#include <fstream.h>
//#include <strstream.h>
#include "MvBufr.h"
#include "MvLocation.h"
#include "fmettim.h"

#ifdef METVIEW
//#   include "Metview.h"
#    include "MvDate.h"
#    include "MvRequest.h"
#endif

//---------------------------------------------------
// Function definitions for the FORTRAN BUFR routines
//---------------------------------------------------

#ifdef FORTRAN_NO_UNDERSCORE
#define  BUS012   bus012
#define  BUPRS0   buprs0
#define  BUPRS1   buprs1
#define  BUPRS2   buprs2
#define  BUFREX   bufrex
#define  BUFREN   bufren
#define  BUSEL    busel
#define  BUSEL2   busel2
#define  BUUKEY   buukey
#define  BUPRS3   buprs3
#define  BUBOX    bubox
#define  BUPRTBOX buprtbox
#define  FT_OP6   ft_op6
#define  FT_CLO   ft_clo
#else
#define  BUS012   bus012_
#define  BUPRS0   buprs0_
#define  BUPRS1   buprs1_
#define  BUPRS2   buprs2_
#define  BUFREX   bufrex_
#define  BUFREN   bufren_
#define  BUSEL    busel_
#define  BUSEL2   busel2_
#define  BUUKEY   buukey_
#define  BUPRS3   buprs3_
#define  BUBOX    bubox_
#define  BUPRTBOX buprtbox_
#define  FT_OP6   ft_op6_
#define  FT_CLO   ft_clo_
#endif

//--------------------------------------------------------------- MvObs
//! A class to handle one observation report stored in a BUFR message
/*! This class is used to request data and metadata from a single
 *  BUFR message (Observation Report). Access to BUFR messages in
 *  files is done using classes MvObsSet and MvObsSetIterator. \n
 *
 * <B>Descriptors</B> \n
 * Elements within the message body are referenced by BUFR descriptors.
 * These descriptors are of the form 'ZXXYYY', where Z=0, XX defines
 * an element class and YYY defines an element within a class.
 * Descriptor values can be found in "BUFR, User Guide and Reference Manual"
 * by Milan Dragosavac (ECMWF). \n
 * A Word of Warning: leave out leading zeroes when writing constants
 * (such as descriptors) into your code, unless you intend to use
 * octal constants! \n
 *
 * <B>Current Descriptor</B> \n
 * Some member functions need no descriptor, because they will use
 * the internal Current Descriptor set by a previously called member
 * function which had a descriptor parameter. \n
 *
 * <B>Missing Values</B> \n
 * Functions returning values from the message will return const kBufrMissingValue
 * if the requested value is not available. Another const, called
 * kBufrMissingIntValue is used for missing integer values.
 */
class MvObs
{
 friend class MvBufrOut;
 friend class MvObsSet;
 friend class MvObsSetIterator;

	void _copy( MvBufr* b );
	void _copy( const MvObs& b );
	void _clean();

public:
//! Constructor
/*! Arguments are mainly used by MvObsSet and MvObsSetIterator.
 *  Applications normally call this constructor without arguments.
 */
	MvObs( MvBufr* b = NULL, int i = 1 );

//! Copy constructor
	MvObs( const MvObs&  b );

	~MvObs();

//! Assignment operator
	MvObs& operator= ( const MvObs& b );

//! Operator to test the validity of a new MvObs
/*! In this example operator void*() is used implicitly
 *  in testing the validity of MvObs object returned by
 *  MvObsSetIterator object:
 * <PRE>
 *      MvObsSet mySet("/path/to/my/file");
 *      MvObsSetIterator myIter(mySet);
 *      ...
 *      while(myObs=myIter()) //-- void*() is called here
 *      {
 *         ... //-- do the stuff
 *      }
 * </PRE>
 */
    operator void*();

//! Returns the number of subsets available in this BUFR message
/*! Use method Advance() to get to the next subset (in a multisubset BUFR message).
 */
        int  msgSubsetCount(){ return _bufrIn->subsetCount(); }

//! Returns the current subset number (in a multisubset BUFR message)
        int  subsetNumber() const { return _subsetNr; }

//! Advances to the next subset in a multisubset BUFR message
/*! Returns 'false' if current subset is the last (or the only)
 *  one. Returns 'true' on success.
 */
	bool   Advance();

//! Checks whether this MvObs contains a BUFR message or is empty
    bool  operator! ();

//! Checks that this MvObs contains a valid decodable BUFR message
    bool  msg_ok() const; // { return _bufr_id == _bufrIn->currentBufrId(); }

    // ----  A P I   f u n c t i o n s :  ---- //

    //-- APIs for requesting parameter values --//

//! Returns the value of an element defined by 'aDescriptor'
/*! Also sets Current Descriptor.
 *  Missing value indicator is returned if the observation report
 *  does not contain elements of type 'aDescriptor', or if the element
 *  does not have value.
 */
      float  value( long aDescriptor );

//! Returns the value of an element defined by 'aDescriptor' as 'long'
/*! Also sets Current Descriptor.
 */
       long  intValue( long aDescriptor );

//! Returns the value of an element defined by 'aDescriptor' as 'string'
/*! Also sets Current Descriptor.
 */
      string stringValue( long aDescriptor );

//! Returns the value of Current Descriptor as a string
      string stringValue();

//! Returns the value of the next element with the same descriptor
/*! Uses Current Descriptor to start looking for the next occurrence
 *  of the same descriptor later in the message.
 *  Updates Current Descriptor.
 *  Returns 'kFortranBufrMissingValue' if no such element is found
 *  or if such an element has no value.
 */
      float  nextValue();

//! Returns the value corresponding to the 'n'th occurrence of descriptor 'descr'
/*! Method searches the current subset from the beginning looking
 *  for descriptors 'descr'. If BUFR message contains 'n' (or more)
 *  occurrences of descriptor 'descr', the corresponding data value
 *  is returned, otherwise 'kFortranBufrMissingValue' is returned.
 */
      float  valueByOccurrence( int n, long descr );

//! Index access operator returns the 'n'th data value
/*! This operator treats BUFR message as an array (as 'bufrdc' does)
 *  and returns the value of the 'n'th element as 'double'.
 *  Index 'n' starts from '1', i.e. n=1,2,3,...
 */
     double  operator[] ( int n );  //-- n starts from 1: 1,2,...,n

//! Returns the value from the 'col'th feedback column for Current Descriptor
/*! Uses 'bufrdc' subroutine 'BUBOX()' to arrange feedback values into
 *  a two dimensional array. Argument 'col' refers to a column in this array.
 */
     double  feedbackValue( int col ){ return _bufrIn->feedbackValue( col, _subsetNr ); }

//! Returns the value of the 'row'th element in the 'col'th feedback column
/*! Uses 'bufrdc' subroutine 'BUBOX()' to arrange feedback values into
 *  a two dimensional array. Arguments 'row' and 'col' are indices into
 *  this array.
 */
     double  feedbackValue( int row, int col ){ return _bufrIn->feedbackValue( row, col, _subsetNr ); }

     string  feedbackItemName( int row){ return _bufrIn->feedbackItemName( row, _subsetNr ); }
     string  feedbackItemUnit( int row){ return _bufrIn->feedbackItemUnit( row, _subsetNr ); }

    //-- APIs for requesting parameter metadata --//

//! Returns the name of the data referenced by 'aDescriptor' (from BUFR Table B)
     string  name( long aDescriptor );

//! Returns the name of the data referenced by Current Descriptor
     string  name();

//! Returns the unit of the data referenced by 'aDescriptor' (from BUFR Table B)
     string  unit( long aDescriptor );

//! Returns the unit of the data referenced by Current Descriptor
     string  unit();

    //-- APIs for iterating through all parameters --//

//! Sets the first expanded descriptor in the message as Current Descriptor
/*! In case of decoding problems, returns 'false'.
 */
       bool  setFirstDescriptor(){ return _bufrIn->SetFirstDescriptor(); }

//! Advances Current Descriptor to the next expanded descriptor in the message
/*! Returns 'false' if message contains no more data (no more descriptors).
 */
       bool  setNextDescriptor(){ return _bufrIn->SetNextDescriptor(); }

//! Returns the data value related to Current Descriptor
      float  currentValue(){ return _bufrIn->CurrentValue( _subsetNr ); }

//! Returns Current Descriptor
       long  currentDescriptor(){ return _bufrIn->CurrentDescriptor(); }

    //-- APIs for requesting parameter types --//

//! Returns the type of data related to descriptor 'aDescriptor'
 EElementValueType  elementValueType( long aDescriptor );

//! Returns the type of data related to Current Descriptor
 EElementValueType  elementValueType();

    //-- APIs for requesting time and location data --//

//! Returns the time from the observation report (from the data: BUFR section 4)
        TDynamicTime  obsTime(){ return _bufrIn->obsTime( _subsetNr ); }

//! Returns the time from the message metadata (from BUFR section 1)
        TDynamicTime  msgTime(){ return _bufrIn->msgTime(); }

//! Returns the location from the observation report (from the data: BUFR section 4)
      MvLocation  location();

    //-- APIs for message type, subtype, originating centre, etc --//

//! Returns the Message Type code from BUFR section 1
        int  messageType();

//! Returns the locally defined Message Subtype code from BUFR section 1
        int  messageSubtypeLocal();

//! Returns the international Message Subtype code (WMO defined) from BUFR section 1
/*! Note that this code is available only in BUFR Edition 4 messages. For Edition 3
 *  messages a value of 255 is returned (255 corresponds to an octet with all bits '1').
 */
        int  messageSubtypeInternational();

//! Returns either the local or the international message subtype
/*! In BUFR Edition 3 only the local subtype was available.
 *  In BUFR Edition 4 the WMO defined international subtype was
 *  added. Local subtype was left for backwards compatibility.
 *
 *  For BUFR Edition 3 messages this function always returns the local subtype.
 *
 *  For later Editions this function returns the international subtype if
 *  it has been set. If not set (has a value 255) then the local subtype
 *  is returned
 */
        int  messageSubtype();

//! Returns the Originating Centre code from BUFR section 1
        int  originatingCentre();

//! Returns the Originating Subcentre code from BUFR section 1
        int  originatingSubCentre();

//! Returns the Edition number from BUFR section 0
        int editionNumber();

//! Returns the Master Table code from BUFR section 1
/*! Master Table 0 is for Meteorology, 10 is for Oceanography
 */
	int masterTable();

//! Returns the Master Table version from BUFR section 1
	int masterTableVersion();

//! Returns the Local Table version from BUFR section 1
	int localTableVersion();

    //-- APIs for  --//
//! Returns the total length of the message from BUFR section 0
	int messageTotalLen();

    //-- APIs for requesting weather station values --//
//! Returns the 5 digit WMO station identifier
/*! Looks for WMO block number 'BB' and station identifier 'SSS'
 *  and returns the value of 'BBSSS' if found. Returns zero if
 * 'BB' or 'SSS' is not found.
 */
       long  WmoIdentNumber();

//! Returns the 2 digit WMO block identifier 'BB'
/*! Returns zero if 'BB' not found.
 */
        int  WmoBlockNumber();

//! Returns the 3 digit WMO station identifier 'SSS'
/*! Returns zero if 'SSS' not found.
 */
        int  WmoStationNumber();

//! Looks for an ident from the BUFR message
/*! Looks for the following message identifiers: \n
 *  - WMO Station Identifier 'BBSSS' \n
 *  - Ship or mobile land station identifier (001011) \n
 *  - Buoy/platform identifier (001005) \n
 *  - Aircraft flight number (001006) \n
 *  - Satellite identifier (001007) \n
 *  - Aircraft registration number (001008) \n
 *  - Stationary buoy platform identifier (001010) \n
 *  - Storm identifier (001025)
 *  - WMO storm name (001026)
 *  - WMO long storm name (001027)
 *
 *  and returns the first one found, as a string. If none
 *  of the above is found, returns "id???".
 */
     string  findSomeIdent();

    //-- APIs for accessing replicated parameters --//

//! Returns the number of times level coordinator descriptor 'levelDescriptor' is found
	int  numberOfLevels( long levelDescriptor );

//! Returns the first value related to 'levelDescriptor', i.e. value of the first level
      float  firstLevel( long levelDescriptor );

//! Returns the value of the next level related to Current Level Descriptor
      float  nextLevel();

//! Returns the value of the specified data for the specified level
/*! First looks for a data block related to level 'aLevel' as the
 *  value of the data element related to level descriptor 'aLevelDescriptor',
 *  and then looks for a data element related to descriptor 'aDescriptor'
 *  within this level.
 */
      float  valueByLevel( long  aLevelDescriptor
                         , float aLevel
			 , long  aDescriptor );

//! Returns the number of pressure levels found in the observation report
/*! Pressure level descriptor is 007004, so this is an alias for member function
 *  numberOfLevels(7004).
 */
	int  numberOfPressureLevels();

//! Returns the value of the first pressure level in hPa
/*! Original pressure values are stored in Pa, so this is
 *  the same as 100.0*firstLevel(7004).
 */
      float  firstPressureLevel();

//! Returns the value of the next pressure level in hPa
/*! Original pressure values are stored in Pa, so this is
 *  the same as 100.0*nextLevel(7004).
 */
      float  nextPressureLevel();

//! Returns the value of the data corresponding to 'aDescriptor' on level 'aLevel'
/*! Here 'aLevel' is given in hPa. Original pressure values are stored in Pa,
 *  so this is the same as \n
 * <PRE>
 *      valueByLevel( 7004, 100.0*aLevel, aDescriptor )
 * </PRE>
 */
      float  valueByPressureLevel( float aLevel, long aDescriptor );

//! Returns the value of the data corresponding to 'aDescriptor' in a layer
/*! Level values are for the top and the bottom pressure values of a layer
 *  and they are given in hPa. \n \n
 *  This method looks for two consecutive pressure coordinate descriptors
 *  007004 with the given values (hPa is first converted to Pa). If such
 *  a layer is found and the layer contains 'aDescriptor' then the corresponding
 *  data value is returned, otherwise 'kBufrMissingValue' is returned.
 */
      float  valueByLayer( float firstLevel
		         , float secondLevel
		         , long aDescriptor );

    //-- APIs for printing obs --//
    // Section 0,1,2 and 3 just delegated to bufr class.

//! Prints BUFR section 0 to output stream 'aStream'
    boolean  printSection0(ostream &aStream = cout)
      { return _bufrIn->printSection_012(aStream,0); }

//! Prints BUFR section 1 to output stream 'aStream'
    boolean  printSection1(ostream &aStream = cout)
      { return _bufrIn->printSection_012(aStream,1); }

//! Prints BUFR section 2 to output stream 'aStream'
    boolean  printSection2(ostream &aStream = cout)
      { return _bufrIn->printSection_012(aStream,2); }

//! Prints BUFR section 3 to output stream 'aStream'
    boolean  printSection3(ostream &aStream = cout)
      { return _bufrIn->printSection(aStream,3); }

//! Decode BUFR section 2 and place the result into an std::map
    bool getDataFromSection2(map<string,string> &data)
	{ return _bufrIn->getDataFromSection2(data); }

//! Prints all data values into standard output
/*! For output format see method 'writeAllValues' below
 */
    boolean  printAllValues();

//! Writes all data values into output stream 'aStream'
/*! Writes the data, one data value per line. Each line consists of:
 *  - index of the data
 *  - data value
 *  - name of the data
 *  - unit of the data, in square brackets
 *  - the corresponding BUFR descriptor, in parenthesis
 *
 *  Example output (an extract from a SYNOP report): \n
 * <PRE>
 *       1.      10 Wmo Block Number [NUMERIC] (01001)
 *       2.     500 Wmo Station Number [NUMERIC] (01002)
 *       3.       1 Type Of Station [CODE TABLE 002001] (02001)
 *       4.    2007 Year [YEAR] (04001)
 *       5.      11 Month [MONTH] (04002)
 *       6.       6 Day [DAY] (04003)
 *       7.      22 Hour [HOUR] (04004)
 *       8.       0 Minute [MINUTE] (04005)
 *       9.   50.97 Latitude (High Accuracy) [DEGREE] (05001)
 *      10.    6.05 Longitude (High Accuracy) [DEGREE] (06001)
 *      11.      98 Height Of Station [M] (07001)
 *     ...
 * </PRE>
 */
    boolean  writeAllValues( ostream& aStream );

//! Writes all data values into file 'aPathName'
/*! For output format see the version of method 'writeAllValues' above
 */
    boolean  writeAllValues( const char* aPathName );

//! Calls 'bufren' routine BUPRTBOX to write feedback data into stream 'aStream'
    boolean  writeBufrBox( ostream& aStream = cout );

    //-- APIs for accessing original section 1 and 2 headers --//

//	const unsigned char*
//	         section1Ptr(){ return (unsigned char*)(_bufrIn->Sec1->start()); }

//! Returns 'true' if BUFR message contains local section 2, 'false' if not
	bool     hasSection2(){ return _bufrIn->fSec2 != NULL; }

//! Returns a pointer to the beginning of local section 2 in BUFR message
/*! Returns 0 (NULL) if message has no local section 2
 */
	const unsigned char*
	         section2Ptr(){ return _bufrIn->fTotalSec2; }

    //-- APIs for accessing confidence values --//

//! Checks whether the BUFR message contains confidence values or not
/*! Returns 'true' if operator descriptor 222000 is found in the
 *  message, otherwise 'false'.
 */
       bool  hasConfidences();

//! Returns the confidence value for the current data, if exists
/*! Otherwise returns -1.
 */
        int  confidence();

//! Writes confidence values into stream 'aStream'
       bool  writeConfidenceValues( ostream& aStream );

 bool  getBufrBoxSize( int& rows, int& cols )
                 { return _bufrIn->getBufrBoxSize( rows, cols, _subsetNr ); }

     //-- Q&D: valueBySpecifier & specifierIndex made public
     //--      so that ObsPicker can use them freely (vk/Jul)
     double  valueBySpecifier( long   aSpecifierDescriptor
			     , double aSpecifierValue
		             , long   aDescriptor
			     , int   firstIndexValue = 0 );
     int     specifierIndex( long   aSpecifierDescriptor
		           , double aSpecifierValue
		           , int    firstIndexValue = 0 );

 private:
 //protected:
    float  pressureLevel( int firstIndexValue );
    float  level( long levelDescriptor, int firstIndexValue );
    boolean writeValues(ostream &aStream,int first, int last);

 private:
        int  subsetOffset();

	const unsigned char*
	         section1Ptr(){ return (unsigned char*)(_bufrIn->Sec1->start()); }

 protected:
 	int     _subsetNr;
	MvBufr* _bufrIn;
	long    _bufr_id;
 	int     _lastSpecifierIndex; //required???? used by who?????
	long    _currentLevelCoordinate;
	int     _currentLevelIndex;
	MvBufrConfidence* _confidence;
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS
//--------------------------------------------------------------- MvBufrConfidence
// Q&D hack for QC Feed Back using descr operator 222000; vk Apr-95

//! \enum ESelectFilterState Used internally by MvBufrConfidence
enum EBufrConfState
{
   kBCS_unknown
  ,kBCS_missing
  ,kBCS_exists
};

//------------------------------------------------------------------------------
//! Auxiliary class to handle quality control feedback
/*! Warning: This is Q&C ("quick&dirty") hack to access
 *  QC FB data behind operator descriptor 222000.
 */
class MvBufrConfidence
{
 public:
          MvBufrConfidence( MvBufr* aBufr, int aSubsetNr );
          ~MvBufrConfidence();

      bool  hasConfidences();
       int  confidence( long aDescriptor );
       int  confidenceByIndex( int anIndex );
       int  lastDataIndex();

 protected:
       int  startOfDataPresent();
       int  startOfConfidences();
       int  delta( int anIndex );

 private:
   MvBufr*  _bufr;
   int      _subsetNr;
   int      _startOfDataPresent;
   int      _startOfConfidences;
   EBufrConfState _state;
};
#endif


#endif
// MvBufrObs_DEFINED
