#ifndef CSCDCCFormatStatusDigi_CSCDCCFormatStatusDigi_h
#define CSCDCCFormatStatusDigi_CSCDCCFormatStatusDigi_h

/*
 * =====================================================================================
 *
 *       Filename:  CSCDCCFormatStatusDigi.h
 *
 *    Description:  CSC DCC Format error, status and payload flags for a single DCC
 *
 *        Version:  1.0
 *        Created:  02/12/2009 03:22:34 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Valdas Rapsevicius (VR), valdas.rapsevicius@cern.ch,
 *                  Victor Barashko (VB), victor.barashko@cern.ch
 *        Company:  CERN, CH
 *
 * =====================================================================================
 */

#include "DataFormats/MuonDetId/interface/CSCDetId.h"
#include <set>
#include <map>
#include <algorithm>
#include <iosfwd>

/** DCC identifier type */
typedef int32_t DCCIdType;

/** DDU identifier type */
typedef int16_t DDUIdType;

/** CSC identifier type */
typedef int32_t CSCIdType;

/** Examiner status and mask type */
typedef uint32_t ExaminerMaskType;
typedef uint32_t ExaminerStatusType;

/** Format Error individual named flags */
enum FormatErrorFlag {
  ANY_ERRORS = 0,
  DDU_TRAILER_MISSING = 1,
  DDU_HEADER_MISSING = 2,
  DDU_CRC_ERROR = 3,
  DDU_WORD_COUNT_ERROR = 4,
  DMB_TRAILER_MISSING = 5,
  DMB_HEADER_MISSING = 6,
  ALCT_TRAILER_MISSING = 7,
  ALCT_HEADER_MISSING = 8,
  ALCT_WORD_COUNT_ERROR = 9,
  ALCT_CRC_ERROR = 10,
  ALCT_TRAILER_BIT_ERROR = 11,
  TMB_TRAILER_MISSING = 12,
  TMB_HEADER_MISSING = 13,
  TMB_WORD_COUNT_ERROR = 14,
  TMB_CRC_ERROR = 15,
  CFEB_WORD_COUNT_PER_SAMPLE_ERROR = 16,
  CFEB_SAMPLE_COUNT_ERROR = 17,
  CFEB_CRC_ERROR = 18,
  DDU_EVENT_SIZE_LIMIT_ERROR = 19,
  C_WORDS = 20,
  ALCT_DAV_ERROR = 21,
  TMB_DAV_ERROR = 22,
  CFEB_DAV_ERROR = 23,
  DMB_ACTIVE_ERROR = 24,
  DCC_TRAILER_MISSING = 25,
  DCC_HEADER_MISSING = 26,
  DMB_DAV_VS_DMB_ACTIVE_MISMATCH_ERROR = 27,
  EXTRA_WORDS_BETWEEN_DDU_HEADER_AND_FIRST_DMB_HEADER = 28
};

/** CSC Payload individual named flags */
enum CSCPayloadFlag {
  CFEB1_ACTIVE = 0,
  CFEB2_ACTIVE = 1,
  CFEB3_ACTIVE = 2,
  CFEB4_ACTIVE = 3,
  CFEB5_ACTIVE = 4,
  ALCT_DAV = 5,
  TMB_DAV = 6,
  CFEB1_DAV = 7,
  CFEB2_DAV = 8,
  CFEB3_DAV = 9,
  CFEB4_DAV = 10,
  CFEB5_DAV = 11
};

/** CSC Status individual named flags */
enum CSCStatusFlag {
  ALCT_FIFO_FULL = 0,
  TMB_FIFO_FULL = 1,
  CFEB1_FIFO_FULL = 2,
  CFEB2_FIFO_FULL = 3,
  CFEB3_FIFO_FULL = 4,
  CFEB4_FIFO_FULL = 5,
  CFEB5_FIFO_FULL = 6,
  ALCT_START_TIMEOUT = 7,
  TMB_START_TIMEOUT = 8,
  CFEB1_START_TIMEOUT = 9,
  CFEB2_START_TIMEOUT = 10,
  CFEB3_START_TIMEOUT = 11,
  CFEB4_START_TIMEOUT = 12,
  CFEB5_START_TIMEOUT = 13,
  ALCT_END_TIMEOUT = 14,
  TMB_END_TIMEOUT = 15,
  CFEB1_END_TIMEOUT = 16,
  CFEB2_END_TIMEOUT = 17,
  CFEB3_END_TIMEOUT = 18,
  CFEB4_END_TIMEOUT = 19,
  CFEB5_END_TIMEOUT = 20,
  CFEB_ACTIVE_DAV_MISMATCH = 21,
  B_WORDS_FOUND = 22
};

/**
 * @brief  Map iterator template.
 * @param  it Iterator from 0 to ... (auto inc).
 * @param  key Next key to return.
 * @param  m Map to iterate.
 * @return true if key found, false - otherwise
 */
template <class TKey, class TVal>
bool nextInMap(uint32_t& it, TKey& key, const std::map<TKey, TVal>& m) {
  uint32_t c = 0;
  typename std::map<TKey, TVal>::const_iterator itr = m.begin();
  while (itr != m.end()) {
    if (c == it) {
      it++;
      key = itr->first;
      return true;
    }
    itr++;
    c++;
  }
  return false;
}

/**
 * @brief  List of Map keys template
 * @param  m Map to iterate.
 * @return std::set ok Keys
 */
template <class TKey, class TVal>
std::set<TKey> getKeysList(const std::map<TKey, TVal>& m) {
  std::set<TKey> keys;
  typename std::map<TKey, TVal>::const_iterator itr;
  for (itr = m.begin(); itr != m.end(); ++itr) {
    keys.insert(itr->first);
  }
  return keys;
}

/**
 * @class CSCDCCFormatStatusDigi
 * @brief CSC Format Status Object
 */
class CSCDCCFormatStatusDigi {
private:
  /**
   * Internal mask storage variables and containers.
   */

  /** DCC Examiner mask used */
  ExaminerMaskType fDCC_MASK;

  /** CSC Examiner mask used */
  ExaminerMaskType fCSC_MASK;

  /** FED/DCC Id */
  DCCIdType DCCId;

  /** DCC Level summary errors */
  ExaminerStatusType fDDU_SUMMARY_ERRORS;

  std::map<DDUIdType, ExaminerStatusType> mDDU_ERRORS;
  std::map<CSCIdType, ExaminerStatusType> mCSC_ERRORS;
  std::map<CSCIdType, ExaminerStatusType> mCSC_PAYLOADS;
  std::map<CSCIdType, ExaminerStatusType> mCSC_STATUS;

protected:
  /// Make CSCIdType from Crate and DMB IDs
  CSCIdType makeCSCId(const uint16_t crateId, const uint16_t dmbId) const {
    return ((CSCIdType(crateId & 0xFF) << 4) | (dmbId & 0xF));
  }

  /// Init internal data stuctures
  void init() {
    fDDU_SUMMARY_ERRORS = 0;
    fCSC_MASK = 0;
    fDCC_MASK = 0;
    mDDU_ERRORS.clear();
    mCSC_ERRORS.clear();
    mCSC_PAYLOADS.clear();
    mCSC_STATUS.clear();
  }

public:
  /**
   * @brief  Constructor
   * @param  fDCC_MASK_ DCC Examiner mask used (for information purposes).
   * @param  fCSC_MASK_ Examiner mask per chamber
   * @param  fDDU_SUMMARY_ERRORS_ Cumulative DDUs errors status
   * @param  mDDU_ERRORS_ List of errors per DDU
   * @param  mCSC_ERRORS_ List of errors per CSC
   * @param  mCSC_PAYLOADS_ List of payloads per CSC
   * @param  mCSC_STATUS_ List of statuses per CSC
   */
  CSCDCCFormatStatusDigi(const DCCIdType DCCId_,
                         const ExaminerMaskType fDCC_MASK_,
                         const ExaminerMaskType fCSC_MASK_,
                         const ExaminerStatusType fDDU_SUMMARY_ERRORS_,
                         const std::map<DDUIdType, ExaminerStatusType>& mDDU_ERRORS_,
                         const std::map<CSCIdType, ExaminerStatusType>& mCSC_ERRORS_,
                         const std::map<CSCIdType, ExaminerStatusType>& mCSC_PAYLOADS_,
                         const std::map<CSCIdType, ExaminerStatusType>& mCSC_STATUS_)
      : DCCId(DCCId_) {
    init();
    setDCCExaminerInfo(
        fDCC_MASK_, fCSC_MASK_, fDDU_SUMMARY_ERRORS_, mDDU_ERRORS_, mCSC_ERRORS_, mCSC_PAYLOADS_, mCSC_STATUS_);
  }

  CSCDCCFormatStatusDigi(const DCCIdType DCCId_) : DCCId(DCCId_) { init(); }

  /// Default constructor.
  CSCDCCFormatStatusDigi() : DCCId(0) { init(); }

  /// Fill internal data structures using Examiner object
  void setDCCExaminerInfo(const ExaminerMaskType fDCC_MASK_,
                          const ExaminerMaskType fCSC_MASK_,
                          const ExaminerStatusType fDDU_SUMMARY_ERRORS_,
                          const std::map<DDUIdType, ExaminerStatusType>& mDDU_ERRORS_,
                          const std::map<CSCIdType, ExaminerStatusType>& mCSC_ERRORS_,
                          const std::map<CSCIdType, ExaminerStatusType>& mCSC_PAYLOADS_,
                          const std::map<CSCIdType, ExaminerStatusType>& mCSC_STATUS_);

#ifdef DEBUG
  /**
   * Manipulate internal data structures for debug purposes
   */
  void setDCCId(DCCIdType id) { DCCId = id; }
  void setDCCMask(ExaminerMaskType mask) { fDCC_MASK = mask; }
  void setCSCMask(ExaminerMaskType mask) { fCSC_MASK = mask; }
  void setDDUSummaryErrors(ExaminerStatusType status) { fDDU_SUMMARY_ERRORS = status; }
  void setDDUErrors(DDUIdType DDUId, ExaminerStatusType status) {
    std::map<DDUIdType, ExaminerStatusType>::const_iterator item = mDDU_ERRORS.find(DDUId);
    if (item != mDDU_ERRORS.end())
      mDDU_ERRORS[DDUId] = status;
    else
      mDDU_ERRORS.insert(std::make_pair(DDUId, status));
  }
  void setCSCErrors(CSCIdType CSCId, ExaminerStatusType status) {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_ERRORS.find(CSCId);
    if (item != mCSC_ERRORS.end())
      mCSC_ERRORS[CSCId] = status;
    else
      mCSC_ERRORS.insert(std::make_pair(CSCId, status));
  }
  void setCSCPayload(CSCIdType CSCId, ExaminerStatusType status) {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_PAYLOADS.find(CSCId);
    if (item != mCSC_PAYLOADS.end())
      mCSC_PAYLOADS[CSCId] = status;
    else
      mCSC_PAYLOADS.insert(std::make_pair(CSCId, status));
  }
  void setCSCStatus(CSCIdType CSCId, ExaminerStatusType status) {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_STATUS.find(CSCId);
    if (item != mCSC_STATUS.end())
      mCSC_STATUS[CSCId] = status;
    else
      mCSC_STATUS.insert(std::make_pair(CSCId, status));
  }

#endif

  /**
   * Get lists of DDUs and CSCs 
   * Loop iterators for CSCs
   */

  std::set<DDUIdType> getListOfDDUs() const { return getKeysList(mDDU_ERRORS); }

  std::set<CSCIdType> getListOfCSCs() const { return getKeysList(mCSC_PAYLOADS); }

  std::set<CSCIdType> getListOfCSCsWithErrors() const { return getKeysList(mCSC_ERRORS); }

  /**
   * @brief  CSC with error iteration procedure.  
   * Usage:
   *   unsigned int i = 0;
   *   CSCIdType cscId;
   *   while (c.nextCSCWithError(i, cscId)) {
   *     // do stuff
   *   }
   * @param  iterator Integer iterator (incremented automatically)
   * @param  CSCId CSC id to return
   * @return true if CSC id found and returned, false - otherwise
   */

  bool nextCSCWithError(uint32_t& iterator, CSCIdType& CSCId) const { return nextInMap(iterator, CSCId, mCSC_ERRORS); }

  /**
   * @brief  CSC with status iteration procedure.  
   * @see    bool nextCSCWithError(uint32_t&, CSCIdType&) const
   * @param  iterator Integer iterator (incremented automatically)
   * @param  CSCId CSC id to return
   * @return true if CSC id found and returned, false - otherwise
   */
  bool nextCSCWithStatus(uint32_t& iterator, CSCIdType& CSCId) const { return nextInMap(iterator, CSCId, mCSC_STATUS); }

  /**
   * @brief  CSC with payload iteration procedure.  
   * @see    bool nextCSCWithError(uint32_t&, CSCIdType&) const
   * @param  iterator Integer iterator (incremented automatically)
   * @param  CSCId CSC id to return
   * @return true if CSC id found and returned, false - otherwise
   */
  bool nextCSCWithPayload(uint32_t& iterator, CSCIdType& CSCId) const {
    return nextInMap(iterator, CSCId, mCSC_PAYLOADS);
  }

  /**
   * Getters for complete mask by using internal identifiers.
   * Mostly to be used by examiner and old/current code.
   */

  /**
   * Return DCC/DDU level Error Status
   */

  ExaminerStatusType getDDUSummaryErrors() const { return fDDU_SUMMARY_ERRORS; }

  ExaminerStatusType getDDUErrors(const DDUIdType DDUId) const {
    std::map<DDUIdType, ExaminerStatusType>::const_iterator item = mDDU_ERRORS.find(DDUId);
    if (item != mDDU_ERRORS.end())
      return item->second;
    else
      return 0;
  }

  ExaminerStatusType getCSCErrors(const CSCIdType CSCId) const {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_ERRORS.find(CSCId);
    if (item != mCSC_ERRORS.end())
      return item->second;
    else
      return 0;
  }

  ExaminerStatusType getCSCErrors(const uint16_t crateId, const uint16_t dmbId) const {
    return getCSCErrors(makeCSCId(crateId, dmbId));
  }

  ExaminerStatusType getCSCPayload(const CSCIdType CSCId) const {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_PAYLOADS.find(CSCId);
    if (item != mCSC_PAYLOADS.end())
      return item->second;
    else
      return 0;
  }

  ExaminerStatusType getCSCPayload(const uint16_t crateId, const uint16_t dmbId) const {
    return getCSCPayload(makeCSCId(crateId, dmbId));
  }

  ExaminerStatusType getCSCStatus(const CSCIdType CSCId) const {
    std::map<CSCIdType, ExaminerStatusType>::const_iterator item = mCSC_STATUS.find(CSCId);
    if (item != mCSC_STATUS.end())
      return item->second;
    else
      return 0;
  }

  ExaminerStatusType getCSCStatus(const uint16_t crateId, const uint16_t dmbId) const {
    return getCSCStatus(makeCSCId(crateId, dmbId));
  }

  /* 
   * Return FED/DCC Id
   */
  DCCIdType getDCCId() const { return DCCId; }

  /**
   * Return DCC/DDU level Errors Mask 
   */
  ExaminerMaskType getDCCMask() const { return fDCC_MASK; }

  /**
   * Return CSC level Errors Mask 
   */
  ExaminerMaskType getCSCMask() const { return fCSC_MASK; }

  /**
   * Flag Getters for individual named masks.
   */

  bool getDDUSummaryFlag(const FormatErrorFlag flag) const {
    return ((fDDU_SUMMARY_ERRORS & ExaminerStatusType(0x1 << flag)) != 0);
  }
  bool getDDUErrorFlag(const DDUIdType DDUId, const FormatErrorFlag flag) const {
    return ((getDDUErrors(DDUId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCErrorFlag(const CSCIdType CSCId, const FormatErrorFlag flag) const {
    return ((getCSCErrors(CSCId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCErrorFlag(const uint16_t crateId, const uint16_t dmbId, const FormatErrorFlag flag) const {
    return ((getCSCErrors(crateId, dmbId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCPayloadFlag(const CSCIdType CSCId, const CSCPayloadFlag flag) const {
    return ((getCSCPayload(CSCId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCPayloadFlag(const uint16_t crateId, const uint16_t dmbId, const CSCPayloadFlag flag) const {
    return ((getCSCPayload(crateId, dmbId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCStatusFlag(const CSCIdType CSCId, const CSCStatusFlag flag) const {
    return ((getCSCStatus(CSCId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  bool getCSCStatusFlag(const uint16_t crateId, const uint16_t dmbId, const CSCStatusFlag flag) const {
    return ((getCSCStatus(crateId, dmbId) & ExaminerStatusType(0x1 << flag)) != 0);
  }

  void print() const;
};

std::ostream& operator<<(std::ostream& o, const CSCDCCFormatStatusDigi& digi);

#endif
