#if     !defined(_BASELINESSET_H)
 
#include <alma/ASDM/ASDMEntities.h>
#include <alma/ASDMBinaries/Error.h>
#include <alma/ASDMBinaries/BaselineMetadata.h>
#include <alma/ASDMBinaries/SwitchCyclesList.h>
#include <alma/ASDMBinaries/DataDescriptionsSet.h>

#include <alma/Enumtcl/AxisName.h>
#include <alma/Enumtcl/DataContent.h>


namespace sdmbin {
  
  /** Definition of the second level (the top level) in the tree hierarchy: include the antenna baseline
   *  configuration */ 
  class BaselinesSet : public DataDescriptionsSet
  {
  public:
    BaselinesSet();

    /** Definition of the second level of the tree hierarchy 
     * @pre the lower levels of the tree hierarchy are already known
     * @param v_antennaIdArray  Array of antenna identifiers
     * @param v_feedIdArray     Array of feed identifiers 
     *   \note                    Because with ALMA there is only one feed used at any time this vector has 
     *                            the same size as v_antennaIdArray. With Focal Plane Array this size is
     *                            multiplied by the number of feeds in the FPA
     * @param v_phasedArrayList Place-holder, not yet implemented
     * @param v_antennaUsedArray Array to tell in the antennaIdArray which antenna have been actually used
     *                           for the data being stored. v_antennaUsedArray and v_antennaIdArray must have
     *                           the same size.
     * @param dataDescriptionsSet a dataDescriptionsSet object (<i>i.e.</i> the lower levels of the hierarchy 
     * @post the whole tree hierarchy is now known, transfer identifier available for read and write methods
     * @note v_feedIdArray may be typed vector<vector<int> > in the future to better describe the case of
     *       of focal plane arrays when used with interferometers
     */     
    BaselinesSet( std::vector<asdm::Tag>  v_antennaIdArray, 
		  std::vector<int>  v_feedIdArray,
		  std::vector<int>  v_phasedArrayList,
		  std::vector<bool> v_antennaUsedArray, 
		  DataDescriptionsSet& dataDescriptionsSet
		  );

    /** Copy constructor */
    BaselinesSet(const BaselinesSet &);

    /** Destructor (use the default implementation) */
    ~BaselinesSet();

    /** Transfer identifier for a given node in the tree hierarchy for auto-correlation data
     *  Transfer identifier for a given node in the tree hierarchy for auto-correlation data
     *  @pre WARNING: if the use-case is CROSS_AND_AUTO and the current index ndd corresponds
     *       to the case when crossDataDescriptionId is not associated to an autoDataDescriptionId,
     *       i.e. when v_pairDataDescriptionId[ndd]==false, it is an error to invoke this method. 
     *       This situation is met with DSB front-ends, the sideband separation being possible only 
     *       for the cross data. 
     *  @param  na   Index of the antenna
     *  @param  ndd  Index of the dataDescription
     *  @param  nbin Index for the step in a switchCycle (nbin=0 if no switchCycle mode used)
     *  @return A position (counted in number of PDT values) in the container of auto-correlation 
     *          data where the data starts for the selection
     *  @note Note that the APC axis has a size of 1 for the auto-correlations, even when, in the 
     *        CROSS_AND_AUTO mode, the cross data are stored with an APC axis size of 2. Would TelCal
     *        not like this feature will have to be modified in the method size() of the base class
     *        DataDescriptionsSet and this will have an additional napc input parameter.  
     */ 
    unsigned int transferId(unsigned int na,  unsigned int ndd, unsigned int nbin);

    /** Transfer identifier for a given node in the tree hierarchy for auto-correlation data
     *  @pre WARNING: if the use-case is CROSS_AND_AUTO and the current index ndd corresponds
     *       to the case when crossDataDescriptionId is not associated to a autoDataDescriptionId,
     *       i.e. when v_pairDataDescriptionId[ndd]==false, it is an error to invoke this method. 
     *       This situation is met with DSB front-ends, the sideband separation being possible only 
     *       for the cross data. 
     *  @param  na   Index of the antenna
     *  @param  nfe  Index of the feed (required when using FPAs)
     *  @param  ndd  Index of the dataDescription
     *  @param  nbin Index for the step in a switchCycle (nbin=0 if no switchCycle mode used)
     *  @return A position (counted in number of PDT values) in the container of auto-correlation 
     *          data where the data starts for the selection
     *  @note Note that the APC axis has a size of 1 for the auto-correlations, even when, in the 
     *        CROSS_AND_AUTO mode, the cross data are stored with an APC axis size of 2. Would TelCal
     *        not like this feature will have to be modified in the method size() of the base class
     *        DataDescriptionsSet and this will have an additional napc input parameter.  
     */ 
    unsigned int transferId(unsigned int na,  unsigned int nfe, unsigned int ndd, unsigned int nbin);
    
    /** Transfer identifier for a given node in the tree hierarchy for cross-correlation data
     *  @param  na1  Index of the antenna 1
     *  @param  na2  Index of the antenna 2
     *  @param  ndd  Index of the dataDescription
     *  @param  nbin index for the step in a switchCycle (nbin=0 if no switchCycle mode used)
     *  @return A position (counted in number of PDT values) in the container of cross-correlation 
     *          data where the data starts for the selection
     *  @note A baseline corresponds to a pair of indices. The convention is that the first index is 
     *        smaller than the second one (i.e. na1<na2). In case na2<na1, this method will consider 
     *        na2 as the first index and na1 as the second one.
     */ 
    unsigned int transferId(unsigned int na1, unsigned int na2, unsigned int ndd, unsigned int nbin, unsigned int napc);

    /** Transfer identifier for a given node in the tree hierarchy for cross-correlation data
     *  @param  na1  Index of the antenna 1
     *  @param  na2  Index of the antenna 2
     *  @param  nfe  Index of the feed (required when using FPAs)
     *  @param  ndd  Index of the dataDescription
     *  @param  nbin Index for the step in a switchCycle (0nbin=0 if no switchCycle mode used)
     *  @return A position (counted in number of PDT values) in the container of cross-correlation 
     *          data where the data starts for the selection
     *  @note A baseline corresponds to a pair of indices. The convention is that the first index is 
     *        smaller than the second one (i.e. na1<na2). In case na2<na1, this method will consider 
     *        na2 as the first index and na1 as the second one.
     */ 
    unsigned int transferId(unsigned int na1, unsigned int na2, unsigned int nfe, unsigned int ndd,  unsigned int nbin, unsigned int napc);

    /** Baseline number for a given antenna identifier. This number is equal to i+1 where i is the antenna index in the
     * list of antennas effectively producing data. In this context this is the 'zero-baseline' number. This number
     * never exceed the number of effectively used antennas (given by the method getNumEffAntennas()).
     * @param  antennaId Antenna identifier
     * @exception antennaId does not correspond to any of the antenna effectively producing data. 
     * @return The baseline number (one-based)
     * @exception Error if there is no antenna effectively 
     * producing data with this antennaId.
     */
    unsigned int baselineIndex( asdm::Tag antennaId)  ;

    /** Baseline number for a given pair of antenna identifiers
     * @param  antennaId1 Antenna identifier of one of the antenna in a pair of a non-zero baseline
     * @exception antennaId1 does not correspond to any of the antenna effectively producing data. 
     * @param  antennaId2 Antenna identifier of the other antenna in a pair to define the non-zero baseline
     * @exception antennaId2 does not correspond to any of the antenna effectively producing data. 
     * @return The baseline number (one-based)
     * @exception Error if antennaId1 or (and) antennaId2 is (are) not associated to an antenna producing 
     * effectively data. 
     * \note Order of the baselines:\n
     *      Example 1: Consider an antenna array with the list of identifiers (1,2,3,5) the pairs of identifier
     *      defining the baselines are in the order (1,2 1,3 2,3 1,5 2,5 3,5).  
     *      Hence, if antennaId1=2 and antennaId2=3 the returned value is 3. \n
     *      Example 2: Consider an antenna array with the list of identifiers (5,2,3,1) the pairs of identifier
     *      defining the baselines are in the order (5,2 5,3 2,3 5,1 2,1 3,1).  
     *      If antennaId1=2 and antennaId2=3 the returned value is 3. If antennaId1=5 and antennaId2=1 the returned 
     *      value is 4. Note that if antennaId1=1 and antennaId2=5 the returned value will be again 4 due to the
     *      convention that baselines are defined on the basis of a pair onf indices (see the note in the documentation
     *      for the transferId(int, int, int, int, int) method.
     */ 
    unsigned int baselineIndex( asdm::Tag antennaId1, asdm::Tag antennaId2) ;

    /** Baseline number for a given pair of antenna indices in the list of antennas effectively producing data
     * @param  na1 index (zero-based) of antenna 1
     * @exception na1 exceeds the maximum limit imposed by the number of antennas effectively producing data
     * @param  na2 index  (zero-based) of antenna 2
     * @exception na2 exceeds the maximum limit imposed by the number of antennas effectively producing data
     * @return the corresponding baseline number (one-based)
     * @exception Error this methods returns 0 in lax mode if na1 or na2 exceeds this maximum limit
     * @note 
     * -# Order of the baselines:\n
     *      Example for an antenna array consisting of 4 antennas the indices describing this array range
     *      from 0 to 3. The pairs of indices for the baselines are in the order (0,1 0,2 1,2 0,3 1,3 2,3). Hence, if
     *              na1=1 and na2=3 the returned value is 5.
     * -# By definition the first index in any pair being always smaller than the second index, would in the input na1>na2,
     * the method swaps these indices. 
     */ 
    unsigned int baselineIndex( unsigned int na1, unsigned int na2) ;

    /** Antenna index of the first antenna of a pair defining a baseline number 
     * @param baselineIndex A baseline number (zero-based)
     * @exception baselineIndex not zero-based
     * @return the index for the first antenna of the pair in the list of antennas effectively producing data
     * @exception Error if there is no such baselineIndex in the configuration.
     * @note See the note in the documentation of the baselineIndex() method to know
     *       how baselines are ordered and how baselines correspond to pairs of 
     *       antenna indices. 
     */
    unsigned int antenna1(unsigned int baselineIndex)  ;

    /** Antenna index of the second antenna of a pair defining a baseline number
     * @param baselineIndex A baseline number (one-based)
     * @exception baselineIndex not one-based
     * @return the index for the second antenna of the pair
     * @exception Error if there is no such baselineIndex in the configuration.
     * @note See the note in the documentation of the baselineIndex() method to know
     *       how baselines are ordered and how baselines correspond to pairs of 
     *       antenna indices. 
     */
    unsigned int antenna2(unsigned int baselineIndex)  ;

    /** Accessor to the feed index of a feed identifier given an antenna identifier
     * @param antennaId Antenna identifier of an antenna
     * @param feedId    Feed identifier
     * @return The feed index (zero-based)
     * @exception Error
     * - No antenna with this antenna identifier. In this case, in lax mode, this method return -1.
     * - No feed with this feed identifier given the antenna identifier. In this case, in lax mode, this method return -2.
     */
    unsigned int feedIndex(asdm::Tag antennaId, int feedId) ;

    /** Antenna identifier from its index in the sequence of antenna restricted to those effectively producing data
     * @param na The antenna index in the list of \f$ N'_{ant}\f$ antennas effectively producing data
     * @exception na exceeds \f$ N'_{ant}-1\f$
     * @return The antenna identifier
     */
    asdm::Tag         getEffAntennaId(unsigned int na) ;

    /** Number of antenna involved in the data if none would have been dropped
     * @return this number \f$ N_{ant} \f$ which is also the number of antenna scheduled for the observations
     */
    unsigned int getNumAntennas();


    /** Accessor to the feed identifier given a feed index and an antenna index
     * @param na The antenna index in the list of \f$ N'_{ant}\f$ antennas effectively producing data
     * @exception na exceeds \f$ N'_{ant}-1\f$
     * @param nfe The feed index
     * @exception nfe exceeds the upper limit considering the number of feeds used in the configuration.
     * @return a feed identifier
     * @note In general for telescopes with no focal plane arrays (FPA), this identifier should have always
     *       the value 0. Although ALMA does not have FPAs, a feed identifier may also have the value 1. That
     *       would happen in datasets with spectral windows located in the overlap region of two receiver bands,
     *       e.g. the ALMA bands 2 and 3, some of the data obtained with band 2 and the other
     *       data with band 3 for identical spectral windows.
     */   
    int         getFeedId(unsigned int na, unsigned int nfe) ;

    /** Number of antenna actualy involved in the data
     * @return the actual number \f$ N'_{ant} \f$ producing data
     * @note The constraint is that this number can not exceed the number of antenna scheduled
     *       for the observations: \f$ N'_{ant} \le N_{ant} \f$. \n
     *       This number \f$ N'_{ant}\f$ is equal to the number of items in the vector antennaUsedArray
     *       which have the value "true", this vector being a parameter required by the constructors.
     *
     * See the BaselineFlagsMetadata class for details to define or find when a given antenna produce
     * or not data. 
     */
    unsigned int getNumEffAntennas();

    /** Get the number of antenna pairs if all the antennas in  v_AntennaIdArray_ were used 
     * @return The number of baselines actually scheduled for the observations.
     * @note  This is simply given by \f$ N_{bl}=N_{ant}(N_{ant}-1)/2 \f$
     */
    unsigned int getNumBaselines();

    /** Get the effective number of antenna pairs producing data
     * @return The number of antenna actually producing data
     * @note  This is simply given by \f$ N'_{bl}=N'_{ant}(N'_{ant}-1)/2 \f$\n
     * where N'_{ant} is the value returned by the method getNumEffAntennas().
     */
    unsigned int getNumEffBaselines();
    
    /** Get the number of primitive data values (i.e. when a complex number counts for
     *  two values) for a block of data.
     * @param e_dc the nature of the data content
     * @param es_an the sequence of axes associated to the structure for this block of data
     * @param effective a boolean to tell if one consider only the antennas which actualy
     *        produce data or all those scheduled to do the observations. 
     * @note  Note that ALMA has dropped this concept of restricting to the antennas that 
     *        have effectively produced data. Hence in that context "effective" must be set
     *        to false and the data involving the antennas with problem will be considered
     *        as blanked on the basis of the FLAGS information.
     */
    unsigned int getNumPDTvalue(Enum<DataContentMod::DataContent> e_dc, EnumSet<AxisNameMod::AxisName> es_an, bool effective);

  private:
    std::vector<asdm::Tag>  v_AntennaIdArray_;   //!< Input array set of antenna identifiers (size numAntennas_)
    std::vector<int>  v_FeedIdArray_;      //!< Input array list of feed identifiers (size numFeed*numAntennas_)
    std::vector<int>  v_PhasedArrayList_;  //!< Input phasedArray list (not yet implemented) 
    unsigned int numFeeds_;           //!< Input number of feeds
    unsigned int numAntennas_;        //!< Input number of antennas
    unsigned int numEffAntennas_;     //!< Effective number of antennas with data
    unsigned int numBaselines_;       //!< number of antenna pairs
    unsigned int numEffBaselines_;    //!< Effective number of antenna pairs
    std::vector<asdm::Tag>  v_effAntennaIdArray_;//!< Effective array of antenna identifiers

  };


  inline unsigned int BaselinesSet::transferId(unsigned int na1,unsigned int na2,unsigned int nfe, unsigned int ndd,unsigned int nbin,unsigned int napc){

    unsigned int baselineidx = baselineIndex(na1,na2);                   //cout << "baselineidx=" << baselineidx << endl;

    unsigned int v_cumulCrossSize_ndd=0; if(v_cumulCrossSize_.size()!=0)v_cumulCrossSize_ndd = v_cumulCrossSize_[ndd];
    unsigned int v_crossSize_ndd=0;      if(v_crossSize_.size()!=0)     v_crossSize_ndd      = v_crossSize_[ndd];

    unsigned int n = 
      baselineidx*numFeeds_*sumCrossSize_ +                           // /baseline
      nfe*sumCrossSize_ +                                             // /baseline/feed     
      v_cumulCrossSize_ndd +                                          // /baseline/feed/datadesc
      nbin*(v_crossSize_ndd/numBin(ndd)) +                            // /baseline/feed/datadesc/bin
      napc*(v_crossSize_ndd/(numBin(ndd)*numApc_));                   // /baseline/feed/datadesc/bin/napc

    return n;
  }

  inline unsigned int BaselinesSet::baselineIndex( unsigned int na1, unsigned int na2) {
    unsigned int iIdx = std::min (na1, na2);
    unsigned int jIdx = std::max (na1, na2);
    return jIdx * (jIdx - 1) / 2 + iIdx;
  }
}
#define _BASELINESSET_H
#endif