#if     !defined(_SDMBINDATA_H)

#include <iostream>
#include <sstream>
#include <string>

#include <alma/ASDM/ASDMEntities.h>  
#include <alma/ASDM/Tag.h>
#include <alma/ASDM/ArrayTime.h>
#include <alma/ASDM/ArrayTimeInterval.h>

// Enumerations used to select data:
#include <alma/Enumtcl/AtmPhaseCorrection.h>
#include <alma/Enumtcl/CorrelationMode.h>
#include <alma/Enumtcl/ProcessorType.h>
#include <alma/Enumtcl/ScanIntent.h>
#include <alma/Enumtcl/SpectralResolutionType.h>
#include <alma/Enumtcl/TimeSampling.h>

#include <alma/Enumtcl/CorrelatorType.h>

// Views:
#include <alma/ASDMBinaries/SDMDataViews.h>

#include <alma/ASDMBinaries/BaselinesSet.h>
#include <alma/ASDMBinaries/Integration.h>


#include <alma/ASDMBinaries/SDMDataObjectReader.h>
#include <alma/ASDMBinaries/SDMDataObjectStreamReader.h>

// shared_ptr not needed by CASA, do not compile this for the WITHOUT_BOOST option
// It may be needed by ALMA code, so do not elimiate it yet.
#ifndef WITHOUT_BOOST
#include <boost/shared_ptr.hpp>
#endif

// #include "Singleton.hpp"
// #include "ExecBlockDir.hpp"

namespace sdmbin{

  /** Accessor to the SDM binary data */
class SDMBinData{
 public:
  /*   SDMBinData(); */
  /** Constructor:
      @pre The SDM dataset must be in memory, this dataset must have the binary data,
      files in a sub-directory named ASDMBinary. 
      @param datasetPtr Pointer to the SDM dataset
      @param execBlockDir SDM dataset directory (it must be readable as well as its 
                          sub-directory ASDMBinary.
      @post All the rows of the Main table will be accepted in the processing. 
      - Would that be inadequate use the methods 'select' to accept only a subset of the SDM
      main table rows. 
      - Use also, if desired, the method 'selectDataSubset' for further
      filtering at the level of the individual BLOBs.
      - After setting this filtering consider using the method 'isComplex()' before 
      scaning the SDM main table rows if the intent is to build a MeasurementSet 
      MAIN table.
  */ 
  SDMBinData( asdm::ASDM* const datasetPtr, std::string execBlockDir);

  ~SDMBinData();

  /** Method to select the main table rows to retrieve data for a subset of
      scan intents.
      @param es_si set of scan intents to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<ScanIntentMod::ScanIntent> es_si);

  /** Method to select the main table rows to retrieve data for a subset of
      processor types.
      @param es_pt set of processor types to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<ProcessorTypeMod::ProcessorType> es_pt);

  /** Method to select the main table rows to retrieve data for a subset of
      correlation modes.
      @param es_cm set of correlation modes to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<CorrelationModeMod::CorrelationMode> es_cm);
  
  /** Method to select the main table rows to retrieve data for a subset of
      spectral resolution types.
      @param es_srt set of spectral resolution types to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<SpectralResolutionTypeMod::SpectralResolutionType> es_srt);

  /** Method to select the main table rows to retrieve data for a subset of
      time samplings.
      @param es_ts set of time samplings to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<TimeSamplingMod::TimeSampling> es_ts);


  /** An alternate method to select the main table rows to retrieve data for a subset of
      correlation modes, spectral resolution types and time sampling.
      @param es_cm set of correlation modes to be accepted
      @param es_srt set of spectral resolution types to be accepted
      @param es_ts set of time samplings to be accepted
      @post All the rows in the Main table which do not satisfy this selection will be ignored.
      @note If the method isComplex() is used subsequently this selection can not be modified 
      after that.
  */
  void select( EnumSet<CorrelationModeMod::CorrelationMode>        es_cm,
	       EnumSet<SpectralResolutionTypeMod::SpectralResolutionType> es_srt, 
	       EnumSet<TimeSamplingMod::TimeSampling>           es_ts);  


  /** Method to constrain the getData methods to return a (sub)set of the data which are in the BLOBs
      @param e_qcm   selected correlationMode enumerator;
      @param es_qapc set of AtmPhaseCorrection enumerators;
      @post The getData methods will return the data restricted to that selection. 
      For example if es_qapc is a set of 2 enumerators AP_CORRECTED and AP_UNCORRECTED
      then both type of data will be returned.
      @note Note that the MS filler accepts at most one AtmPhaseCorrection enumerator.
  */ 
  void selectDataSubset( Enum<CorrelationModeMod::CorrelationMode>       e_qcm,
			 EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc );

  /** Method to set priority for DataDescription in the output order
      @post Output sequence forced to be DataDescriptionId/TimeInterval/Ant1/Ant2
      @note There is no unset method.
   */
  void setPriorityDataDescription();

  /** Accessor to the sequence of time centroids.
      @return An indexed sequence of time centroids
      @note The returned sequence has the required informations concerning transpositions to 
      minimize the number of measure conversions when computing the UVWs with the UvwCoords 
      state machine.
  */
  std::vector<std::pair<unsigned int,double> > timeSequence()const;

  /** Predicate to tell if the dataDescriptionId has priority over time in the out
      sequence of MSData objects.
  */
  bool dataDescriptionFirst()const;

  /** Apply system temperature calibration
      @pre  System temperature calibration not applied
      @post System temperature calibration will be applied
      @todo Handle the case when what is needed is missing in the syscal table
  */
  void applySysCal();

  /** Predicate to know if the system temperature calibration is applied
      when accessing data
  */
  bool sysCalApplied() const;

  /**
   * A utility method which returns the type of the processor which produced the data referred to by a row of the Main table.
   *
   * @param mainRowPtr a pointer to a row of the Main table of an ASDM dataset.
   * @return a ProcessorType value.
   */
  ProcessorTypeMod::ProcessorType processorType(asdm::MainRow* const mainRowPtr) const; 

  /** Accessor to the SDM Main table rows
      @param mainRowPtr Pointer to a row of the Main table
      @return true is the SDM row passed the selection criterion, else false
      @post If true is returned the state is adeqaute to respond to a 'getData' 
      method, else the state is adequate to respond to a new call of this
      acceptMainRow for an other SDM Main table row.
  */
  bool acceptMainRow( asdm::MainRow* const mainRowPtr);

  /** Accessor to one SDM Main table row.
   * @param mainrowPtr is a pointer to one row of the SDM Main table,
   * @return true if the SDM row passed the selection criterium , else false,
   * @post a true value returned means that the instance of SDMBinData is in 
   * a state such a call to the getData method can be done, else only another call to openRow (supposedly for another Main row) can be done.
   */
  bool openMainRow(asdm::MainRow* const mainRowPtr);

  /** Filter: rejection of a Main table row if it does not satisfy the criterion
      of selection. 
      @param mainRowPtr Pointer to a row of the Main table
      @return the reason if this row has been rejected, else an empty string
  */ 
  std::string reasonToReject(asdm::MainRow* const mainRowPtr);


  /** Method to know if the data to be retrieved will be of type complex or not.

      @pre 
      - case a: used in the context of the SDM main table as a whole
      - case b: used in the context of a given row
      @return
      - case a: true if there is in the table at least one row with references 
                to data with at least 3 polarization cross-products or if there 
                are data of type visibilities, else it returns false
      - case b: true if for the current row there are references to data with at 
                least 3 polarization cross-products or if there are data of type 
                visibilities, else it returns false
      @post data to be retrieve for the current row will be of type complex if this
            method returns true, else false.
  */
  bool isComplexData();

  std::vector<SDMData*> getData();

  /**
     @todo 
     - apply the polynomials for the field directions
     - apply syscal for the cross data
   */
  std::vector<MSData*>  getData( Enum<CorrelationModeMod::CorrelationMode> e_qcm, EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc); 

  MSData*          getData( unsigned int na, unsigned int nfe, unsigned int ndd, unsigned int nbin);

  MSData*          getCalibratedData( unsigned int na, unsigned int nfe, unsigned int ndd, unsigned int nbin,
				      std::pair<bool,std::vector<std::vector<float> > > p_tsys);

  MSData*          getData( unsigned int na1, unsigned int nfe1, unsigned int na2, unsigned int nfe2, 
			    unsigned int ndd, unsigned int nbin, std::vector<unsigned int> v_napc,
			    float scleFactor);

  MSData*          getCalibratedData( unsigned int na1, unsigned int nfe1, unsigned int na2, unsigned int nfe2, 
				      unsigned int ndd, unsigned int nbin, std::vector<unsigned int> v_napc,
				      float scleFactor,
				      std::pair<bool,std::vector<std::vector<float> > > p_tsys);

  MSData*          getData( asdm::Tag antId, int feedId,
			    asdm::Tag dataDescId, 
			    AtmPhaseCorrectionMod::AtmPhaseCorrection apc, 
			    unsigned int  binNum); 

  MSData*          getData( asdm::Tag antId1, int feedId1,
			    asdm::Tag antId2, int feedId2,
			    asdm::Tag dataDescId, 
			    std::vector<AtmPhaseCorrectionMod::AtmPhaseCorrection> v_apc, 
			    unsigned int  binNum); 

  const VMSData* getDataCols();

  const VMSData* getDataCols( Enum<CorrelationModeMod::CorrelationMode> e_qcm, EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc );

  /**
   * Returns a pointer to a VMSData structure containing the values require to populate the MS Main table columns
   * from what it has got from the reading of at most n SDMDataSubsets in the current BDF file. 
   * <ul>
   * <li> This method call be called iteratively as long as there are SDMDataSubsets.  </li>
   * <li> A returned VMSData having any of its array empty means that all the SDMDataSubsets have been read.</li>
   * </ul>
   * @param n gives the maximum number of SDMDataSubsets ([sub]integrations) that one wants to read in the current BDF.
   * @return a pointer to a VMSData containing the values to populate MS rows.
   */
  const VMSData* getNextMSMainCols(unsigned int n);
  const VMSData* getNextMSMainCols(Enum<CorrelationModeMod::CorrelationMode> e_qcm, EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc, unsigned int n);

  // shared_ptr not needed by CASA, do not compile this for the WITHOUT_BOOST option
  // It may be needed by ALMA code, so do not elimiate it yet.
#ifndef WITHOUT_BOOST
  void getNextMSMainCols(unsigned int n, boost::shared_ptr<VMSDataWithSharedPtr>& vmsData_p_sp);
  void getNextMSMainCols(Enum<CorrelationModeMod::CorrelationMode> e_qcm, EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc, unsigned int n, boost::shared_ptr<VMSDataWithSharedPtr>& vmsData_p_sp);
#endif

  std::vector<MSData*> getMSDataFromBDFData(Enum<CorrelationModeMod::CorrelationMode> e_qcm, EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection> es_qapc, unsigned int n);

  /**
   * Populates the vector v_dataDump after having read at most n DataSubsets in the BDF.
   */
  void getNextDataDumps(unsigned int n);

  /** To know easily in which order the baselines are returned in a sequence of MSData objects or in a VMSData object.
      @note Let assume the sequence of antennas {1,2,3,4} in the ConfigDescription table. There are two cases: 
      - Return true if the resulting order is (1,2) (1,3) (2,3) (1,4) (2,4) and (3,4)
      - Return false if the resulting order is (1,2) (1,3) (1,4) (2,3) (2,4) and (3,4)

      Note that this order may be different compared to the order used in the BDF.  
  */
  static bool baselineReverse();

  /** To know easily the position of the auto data and metadata relative to the cross data and metadata in the
      sequence of MSData objects or in a VMSDATA object

      Note that this order may be different compared to the order used in the BDF.  
  */
  static bool autoTrailing();

  /** The two parameters defining the order in the output sequences, baslineReverse and autoTrailing */
  static std::pair<bool,bool> dataOrder();

 protected:

  /** Do the actual binary data binding
      @param dataOID The data object identifier
   */
  int  attachDataObject(std::string dataOID);

  /** Do the actual binary data binding.
      The BDF is traversed as a stream.
      @param dataOID The data object identifier
   */
  int  attachStreamDataObject(const std::string& dataOID);

  /** Initialize the sequential reading of the SDMDataSubsets (i.e. [sub]integrations) contained in the BDF referred 
   * to by dataOID.
   * <ul>
   * <li>The BDF file referred to by dataOID is opened and the informations global to the whole BDF are read </li>
   * <li>The sequential reading of the SDMDataSubsets can be started. </li>
   * </ul>
   *
   * @param dataOID the BDF identifier.
   * @return an int value. 0 means a problem, 1 means Ok.
   */
  int openStreamDataObject(const std::string& dataOID);

  /** Unset the current data binding
   */
  void detachDataObject();

  /** Determine the view of a MS STATE
    @param subscanNum Subscan number (the semantic of this attribute is assumed to be identical
                      in the SDM and the MS
    @param v_stateId  The list of states identifiers in the row of SDM Main table
    @param v_antennaId The corresponding list of antenna identifier
    @param v_feedId The corresponding list of collection of feed identifier
    @param v_ddId The sequence of data description identifiers for the current config description
    @param na Antenna index for a given antenna in this antenna list
    @param nfe Feed index
    @param nspw DataDescription index
    @param timeOfDump The epoch to be considered to determine this MS STATE view
  */
  MSState          getMSState( unsigned int subscanNum,
			       std::vector<asdm::Tag>  v_stateId, 
			       std::vector<asdm::Tag>  v_antennaId, std::vector<int> v_feedId, std::vector<asdm::Tag> v_ddId,
			       unsigned int na, unsigned int nfe, unsigned int nspw, asdm::ArrayTime timeOfDump);

 private:
  static asdm::ASDM*                     datasetPtr_;
  static std::string                  execBlockDir_;
  static bool                       canSelect_;
  static bool                    forceComplex_;  // true ==> autoData will be transformed into complex data
  EnumSet<ScanIntentMod::ScanIntent>                   es_si_;  // set of scan intents selected
  EnumSet<ProcessorTypeMod::ProcessorType>                es_pt_;  // set of processor type selected
  EnumSet<CorrelationModeMod::CorrelationMode>              es_cm_;  // set of correlation mode selected
  EnumSet<SpectralResolutionTypeMod::SpectralResolutionType>      es_srt_;  // set of spectral resolution types selected
  EnumSet<TimeSamplingMod::TimeSampling>                 es_ts_;  // set of time sampling selected

  Enum<CorrelationModeMod::CorrelationMode>                 e_qcm_;  // query to select a subset of data in a BLOB
  EnumSet<AtmPhaseCorrectionMod::AtmPhaseCorrection>         es_qapc_;  // query to select a subset of data in a BLOB

  bool                                ddfirst_;  // true ==> output sequences of time for every dataDescription

  asdm::MainRow*                         mainRowPtr_;
  std::string                              dataOID_;
  asdmbinaries::SDMDataObjectReader                  blob_r_;  // current read-only BLOB
  asdmbinaries::SDMDataObjectStreamReader           sdmdosr ;  // current SDMDataObjectStreamReader
  bool                         bdfMemoryMapped;  // will the BDF mapped in memory (true) or read sequentially (false) ?
  std::vector<DataDump*>                v_dataDump_;

  const  float*                  floatDataPtr_;  // mutable attribute; autocorrelation data of a single dump
  const  short*                  shortDataPtr_;  // mutable attribute; visiblity data of a single dump
  const  int*                     longDataPtr_;  // mutable attribute; visiblity data of a single dump
  static MSData*                    msDataPtr_;  // mutable attribute; one MS-MAIN row given
  static SDMData*                  sdmDataPtr_;  // mutable attribute; one SDM-Main (v2) row 
  static BaselinesSet*           baselinesSet_;  // mutable attribute
  static std::vector<MSData*>          v_msDataPtr_;  // mutable attribute
  static VMSData*                  vmsDataPtr_;
  static std::vector<SDMData*>        v_sdmDataPtr_;
  std::map<asdm::Tag,BaselinesSet*>  m_cdId_baselinesSet_;
  std::set<asdm::Tag>                             s_cdId_; // the keys present in  m_cdId_baselinesSet_ (used for optimization)
  bool                            complexData_;
  static bool                  coutDeleteInfo_; // utility for debugging
  static bool                 baselineReverse_; // order in which the data are in the returning VMSData structure.
  static bool                    autoTrailing_; // position of the auto data relative cross data in the returning VMSData structure.
  static bool                          syscal_; // true ==> apply system temperature calibration.

  const float*               floatDataDumpPtr_;

  std::vector<std::pair<unsigned int,double> >    v_tci_; // Indexed time centroid sequence

  void   deleteMsData(MSData* msDataPtr);

  bool verbose_ ;  // A variable to turn on/off the verbosity of the code, traditionnally based on the existence of env var "ASDM_DEBUG"
};

}

#define _SDMBINDATA_H
#endif