////////////////////////////////////////////////////////////////////////
/// \class RAT::DS::DataQCFlags
///
/// \brief  This class contains the data cleaning or data quality flags
///         for an event.
///
/// \author R Bonventre <rbonv@hep.upenn.edu>
/// \author Phil G Jones <p.g.jones@qmul.ac.uk>
/// \author Ashley R. Back <a.r.back@qmul.ac.uk> -- contact person
///
/// REVISION HISTORY:\n
///     2013-10-16: P G Jones - New ds refactor. \n
///     2014-08-31 : P G Jones - Add new `Set` method.\n
///
/// \details The flags are stored alongside an applied mask, both of which
/// are indexed by pass number.
///
////////////////////////////////////////////////////////////////////////

#ifndef __RAT_DS_DataQCFlags__
#define __RAT_DS_DataQCFlags__

#include <RAT/DS/BitMask.hh>

#include <TObject.h>

#include <map>
#include <vector>

namespace RAT
{
namespace DS
{

class DataQCFlags : public TObject
{
public:
  /// Construct the DataQCFlags with pass zero information
  DataQCFlags() : TObject() { flags[0] = BitMask(); applied[0] = BitMask(); }

  /// Check on data cleaning cuts
  ///
  /// @param[in] pass data cleaning bit to check
  /// @return boolean true if pass is present
  bool ExistFlags( const UInt_t pass ) const { return flags.count( pass ) == 1; }

  /// Return the latest pass number on which data cleaning was run
  inline Int_t GetLatestPass();
  /// @copydoc GetLatestPass()
  inline Int_t GetLatestPass() const;

  /// Get data cleaning flags for pass
  ///
  /// @param[in] pass to return
  /// @return data cleaning flag corresponding to pass (returns NULL if not present)
  /// @throws out_of_range if pass is not present
  BitMask& GetFlags( const UInt_t pass ) { return flags.at( pass ); }
  /// @copydoc GetFlags(UInt_t)
  const BitMask& GetFlags( const UInt_t pass ) const { return flags.at( pass ); }

  /// Get data quality/cleaning applied for pass
  ///
  /// @param[in] pass to return
  /// @return data cleaning flag corresponding to pass (returns NULL if not present)
  /// @throws out_of_range if pass is not present
  BitMask& GetApplied( const UInt_t pass ) { return applied.at( pass ); }
  /// @copydoc GetApplied(UInt_t)
  const BitMask& GetApplied( const UInt_t pass ) const { return applied.at( pass ); }

  /// Helper method to set the applied flag to true and the flag to value
  ///
  /// @param[in] pass to return
  /// @param[in] flag position to set
  /// @param[in] value of the flag to set
  /// @throws out_of_range if pass is not present
  inline void Set( const UInt_t pass, const size_t flag, const bool value );

  /// Set the data quality/cleaning flags for pass
  ///
  /// Will override existing flags!
  ///
  /// @param[in] pass to set
  /// @param[in] mask flags to set, BitMask type
  void SetFlags( const UInt_t pass, const BitMask& mask ) { flags[pass] = mask; }

  /// Set the data quality/cleaning applied for pass
  ///
  /// Will override existing applied!
  ///
  /// @param[in] pass to set
  /// @param[in] mask applied to set, BitMask type
  void SetApplied( const UInt_t pass, const BitMask& mask ) { applied[pass] = mask; }

  // This ROOT macro adds dictionary methods to this class.
  // The number should be incremented whenever this class's members are changed.
  // It assumes this class has no virtual methods, use ClassDef if change this.
  ClassDefNV( DataQCFlags, 2 );
protected:
  std::map<UInt_t, BitMask> flags; ///< The flags indexed by pass
  std::map<UInt_t, BitMask> applied; ///< The applied flags indexed by pass
};

inline void
DataQCFlags::Set( const UInt_t pass, const size_t flag, const bool value )
{
  applied.at( pass ).Set( flag, true );
  flags.at( pass ).Set( flag, value );
}

inline Int_t
DataQCFlags::GetLatestPass()
{
  for( std::map<UInt_t, BitMask>::reverse_iterator iter = applied.rbegin();
       iter != applied.rend(); ++iter )
    if( (iter->second).GetULong64_t(0) != 0 ) // Then flags have been applied
      return iter->first;
  // No flags have been applied in any runs. return -1
  return -1;
}

inline Int_t
DataQCFlags::GetLatestPass() const
{
  for( std::map<UInt_t, BitMask>::const_reverse_iterator iter = applied.rbegin();
       iter != applied.rend(); ++iter )
    if( (iter->second).GetULong64_t(0) != 0 ) // Then flags have been applied
      return iter->first;
  // No flags have been applied in any runs. return -1
  return -1;
}

} // namespace DS

} // namespace RAT

#endif