text: /**
   * @file Cuts.h
   * @brief Handle data selections and DSS keywords for packages outside of
   * dataSubselector.
   * @author J. Chiang
   *
   * $Header: /nfs/slac/g/glast/ground/cvs/ScienceTools-scons/dataSubselector/dataSubselector/Cuts.h,v 1.46 2015/01/16 05:35:41 jchiang Exp $
   */
  
  #ifndef dataSubselector_Cuts_h
  #define dataSubselector_Cuts_h
  
  #include <iostream>
  #include <map>
  #include <string>
  #include <vector>
  
  #include "dataSubselector/GtiCut.h"
  #include "dataSubselector/RangeCut.h"
  
  namespace tip {
     class ConstTableRecord;
     class Header;
     class Table;
  }
  
  namespace dataSubselector {
  
  class BitMaskCut;
  class Gti;
  class GtiCuts;
  
  /**
   * @class Cuts
   * @brief Handle data selections and Data Subspace (DSS) keywords for
   * packages outside of dataSubselector.
   * @author J. Chiang
   *
   */
  
  class Cuts {
  
  public: 
  
     Cuts() {}
  
     /// @brief This constructor reads the data selections from the event 
     ///        extension header.
     /// @param eventFile FITS file name.
     /// @param extension Name of the HDU containing the cuts.
     /// @param check_columns DSS range cuts may be specified to 
     ///        have a functional dependence on other column values,
     ///        e.g., to specify an energy dependent zenith angle cut,
     ///        the DSTYPn keyword value would be
     ///        "ZENITH_ANGLE(ENERGY)".  Currently, these classes only
     ///        support simple range cuts that do not have these
     ///        functional dependences.  If check_columns is true, an
     ///        exception will be thrown if a non-bare column name is
     ///        encountered as the DSS type.  If false, the range cut
     ///        will be stored, but if applied to a tip::TableRecord,
     ///        tip will throw an exception.
     /// @param skipTimeRangeCuts If true, time range cuts in the DSS
     ///        keywords will not be read in.  Since the GTI extension
     ///        should already include any time range cuts, explicit
     ///        time range cuts are not needed.
     Cuts(const std::string & eventFile, const std::string & extension,
          bool check_columns=true, bool skipTimeRangeCuts=false,
          bool skipEventClassCuts=false);
  
     /// @brief This constructor reads in a vector of eventFiles, verifying
     ///        that the non-GTI cuts are the same in all files, and merging
     ///        the GTIs from the various files into a single Gti object.
     Cuts(const std::vector<std::string> & eventFiles,
          const std::string & extension,
          bool check_columns=true,
          bool skipTimeRangeCuts=false,
          bool skipEventClassCuts=false);
  
     /// A copy constructor is needed since there are pointer data members.
     Cuts(const Cuts & rhs);
  
     /// Deletes the CutBase pointers.
     ~Cuts();
  
     /// Copy assignment operator
     Cuts & operator=(const Cuts & rhs);
  
  #ifndef SWIG
     /// @brief True if the data in the row passes all of the cuts.
     /// @param row A row of FITS binary table.
     bool accept(tip::ConstTableRecord & row) const;
  
     /// @brief True if the data in the map passes all of the cuts.
     /// @param params A map of keyword-value pairs for use in contexts
     ///        where filtering is desired but the data do not exist in
     ///        FITS format.
     bool accept(const std::map<std::string, double> & params) const;
  
     /// @brief This method will add a cut if it is not equal to
     ///        and if it is not superceded by an existing cut.
     ///        If the added cut supercedes an existing cut, that cut
     ///        will be replaced.
     /// @return The current number of cuts stored.
     /// @param colname The column name of the value to be selected on.
     /// @param unit The units of this quantity, e.g., 's', 'deg', 'MeV'
     /// @param minVal The minimum value to be accepted by this cut.
     /// @param maxVal The maximum value to be accepted by this cut.
     /// @param type Specifies whether the cut range is open (bounded 
     ///        from below or above) or closed.
     /// @param indx If the column is a vector, indx is the index of the
     ///        array to be cut on.  FITS follows the FORTRAN convention
     ///        that the first element is indx=1.
     unsigned int addRangeCut(const std::string & colname, 
                              const std::string & unit,
                              double minVal, double maxVal,
                              RangeCut::IntervalType type=RangeCut::CLOSED,
                              unsigned int indx=0);
  
     /// @brief Add a GTI cut.  Here an existing GTI extension is 
     ///        read from a FITS file as a tip::Table.
     /// @return The current number of cuts stored.
     /// @param gtiTable The GTI extension read using tip.
     unsigned int addGtiCut(const tip::Table & gtiTable);
  
     /// @brief Add a GTI cut using a Gti object.
     /// @return The current number of cuts stored.
     /// @param gti A dataSubselector::Gti object (NB: This class derives
     ///        from evtbin::Gti).
     unsigned int addGtiCut(const Gti & gti);
  
     /// @brief An acceptance cone on the sky.
     /// @return The current number of cuts stored.
     /// @param ra Right Ascension of cone center in (J2000 degrees).
     /// @param dec Declination of cone center (J2000 degrees).
     /// @param radius Cone half-opening angle (degrees)
     unsigned int addSkyConeCut(double ra, double dec, double radius);
  
     /// @brief (single) Bit mask cut, usually used to make event
     /// selection based on bit map in the EVENT_CLASS column
     /// @return The current number of cuts stored.
     /// @param colname Name of column to which the bit-mask is applied
     /// @param bitPosition Position of the bit (corresponding to the
     /// desired event class)
     /// @param pass_ver Pass version of IRFs, e.g., "P7V6".
     ///        This should be left blank for pre-Pass 7 IRFs.
     unsigned int addBitMaskCut(const std::string & colname,
                                unsigned int bitPosition,
                                const std::string & pass_ver="");
  
     /// @brief Not a real cut since it does not apply to any columns
     /// in FT1.  This cut is used to keep track of the IRF version
     /// information so that it is automatically propagated to derived
     /// data products such as filtered FT1 files, counts maps, etc..
     /// @return The current number of cuts stored.
     /// @param colname Name of IRF property that is versioned.
     /// @param version The version string.
     unsigned int addVersionCut(const std::string & colname,
                                const std::string & version);
  
     unsigned int addCut(const CutBase & newCut) {
        m_cuts.push_back(newCut.clone());
        return m_cuts.size();
     }
  
     /// @brief Merge RangeCuts, taking care of intersecting intervals, 
     /// open-ended ranges, etc.
     unsigned int mergeRangeCuts();
  
     /// @brief Remove all RangeCuts with a given column name.
     /// @param colname The name of the column corresponding to the
     ///        RangeCuts to be removed.
     /// @param removedCuts Vector of removed RangeCuts.  The calling routine
     ///        is responsible for deleting these cuts.
     unsigned int removeRangeCuts(const std::string & colname,
                                  std::vector<RangeCut *> & removedCuts);
  
     /// @brief Remove specified VersionCut
     /// @param colname Column name of VersionCut to be removed.
     unsigned int removeVersionCut(const std::string & colname);
  
     /// @brief Add the DSS keywords to a write-enabled tip::Header.
     /// @param header The reference returned from the tip::Table::getHeader
     ///        method where the table has been opened using the
     ///        tip::IFileSvc::editTable method.
     void writeDssKeywords(tip::Header & header) const;
  
     /// @brief Add the time-related DSS keywords to a write-enabled
     ///        tip::Header.
     /// @param header The reference returned from the tip::Table::getHeader
     ///        method where the table has been opened using the
     ///        tip::IFileSvc::editTable method.
     void writeDssTimeKeywords(tip::Header & header) const;
  
     /// @brief Add the first GTI extension in the m_cuts vector to
     ///        the specified FITS file.  It is tacitly assumed that
     ///        there is only one GTI extension.  If a GTI extension
     ///        already exists in the FITS file, then nothing is done.
     /// @param filename The FITS file name.
     /// @todo This method should check if the existing extension is
     /// compatible with the one to be written. If it is, then the old
     /// one is should be replaced.  If it is not, then a runtime
     /// exception should be thrown.
     void writeGtiExtension(const std::string & filename) const;
  
     /// @brief The number of cuts contained herein.
     unsigned int size() const {return m_cuts.size();}
  
     /// @brief Do a member-wise comparison of each cut in m_cuts.
     /// The ordering of cuts must be the same.
     bool operator==(const Cuts & rhs) const;
  
     bool operator!=(const Cuts & rhs) const {
        return !operator==(rhs);
     }
  
     /// @brief Do a member-wise comparison of each cut, but skip GTIs
     bool compareWithoutGtis(const Cuts & rhs) const;
  
     /// @brief Write a summary of the cuts to the output stream.  This
     /// summary contains the same information as the DSS keywords.
     /// @param stream Where the output should be directed.
     /// @param suppressGtis Suppress printing of the GTI data if it exists.
     void writeCuts(std::ostream & stream, bool suppressGtis=false) const;
  
     /// @return A reference to the i-th cut.
     const CutBase & operator[](unsigned int i) const {return *(m_cuts.at(i));}
  
     std::string filterString() const;
  
     /// @return A vector of pointers to the GtiCuts that are present.
     void getGtiCuts(std::vector<const GtiCut *> & gtiCuts);
  #endif
  
     /// @return The irfs named in the DSS keywords.
     const std::string & irfName() const;
  
     /// @return IRFs implied by the bit position and Pass version in
     /// the BIT_MASK DSS keywords, using (as specified by the FSSC),
     /// the most recent version of the corresponding IRFs in
     /// $CALDB/bcf/irf_index.fits.
     std::string CALDB_implied_irfs() const;
  
     /// @brief Append the event type partition to the irfs_name.  The
     /// event type partition is determined from the EVENT_TYPE
     /// BitMaskCut value. For FB, the result is just the generic irfs
     /// name, for PSF and EDISP, " (PSF)" and " (EDISP)" are appended.
     /// The addition of the event type partition info is needed to
     /// index the desired irfs from irfLoader.
     /// @param irfs_name The generic irfs name, e.g., P8R2_SOURCE_V6.
     void append_event_type_partition(std::string & irfs_name) const;
  
  #ifndef SWIG
     /// This will set the BitMaskCut and m_pass_ver value based 
     /// on the IRF name (assuming it is Pass 7 or later).
     /// If BitMaskCut is already set, this will throw an exception.
     void setIrfs(const std::string & irfName);
  
     /// @return A new Cuts object. This static function checks that all
     /// of the non-GtiCuts are the same in each element of the input
     /// vector, copies those, then merges the GtiCuts.  The returned
     /// object is then equivalent to the composition of all of the Cuts
     /// in the vector.
     static Cuts mergeGtis(std::vector<Cuts> & cuts_vector);
  
     /// @return True if the cut is a time range or GTI cut.
     static bool isTimeCut(const CutBase & cut);
  
     static void checkIrfs(const std::string & infile,
                           const std::string & extname,
                           const std::string & irfs);
  
     RangeCut * conversionTypeCut() const;
  
  #endif
  
     BitMaskCut * bitMaskCut(const std::string & colname="EVENT_CLASS") const;
  
     std::vector<BitMaskCut *> bitMaskCuts() const;
  
     const std::string & pass_ver() const {
        return m_pass_ver;
     }
  
     const bool post_P7() const {
        return m_post_P7;
     }
  
     /// Replace or add an existing BitMaskCut applied to the same column.
     void setBitMaskCut(BitMaskCut * bitMaskCut);
  
     static void extract_irf_versions(const std::string & irf_name,
                                      std::string & pass_ver,
                                      std::string & irf_ver);
  
     static void read_bitmask_mapping(std::map<std::string,
                                      unsigned int> & irfs);
  
  private:
  
     std::vector<CutBase *> m_cuts;
  
     std::string m_irfName;
  
     std::string m_pass_ver;
     bool m_post_P7;
  
     unsigned int parseColname(const std::string & colname,
                               std::string & col) const;
  
     bool hasCut(const CutBase * newCut) const;
  
     unsigned int find(const CutBase * cut) const;
  
     /// @brief Add a cut. The passed cut will not be added if an 
     ///        existing cut supercedes it, but it will be deleted.  If
     ///        added, this cut will be deleted by the destructor ~Cut().
     unsigned int addCut(CutBase * newCut);
  
     /// @brief Remove all DSS keywords (and NDSKEYS) from
     ///        the header.
     void removeDssKeywords(tip::Header & header) const;
  
     void read_pass_ver(const std::string & infile, const std::string & ext);
  
     void set_irfName(const std::string & infile, const std::string & ext);
  };
  
  } // namespace dataSubselector
  
  #endif // dataSubselector_Cuts_h
  