#ifndef __JTRIGGER__JPMTMASK__
#define __JTRIGGER__JPMTMASK__

#include <istream>
#include <ostream>
#include <string>

#include "JIO/JSerialisable.hh"

#include "JDetector/JPMTReadoutAddress.hh"

/**
 * \author rbruijn
 */

namespace JTRIGGER {}
namespace JPP { using namespace JTRIGGER; }

namespace JTRIGGER {

  using JIO::JReader;
  using JIO::JWriter;
  using JDETECTOR::JPMTReadoutAddress;


  /**
   * Type definition of PMT mask.
   */
  typedef unsigned int                            JPMTMask_t;


  /**
   * Number of PMT bits.
   */
  static const unsigned int NUMBER_OF_PMT_BITS = 31;
 

  /**
   * Convert PMT bit to PMT mask.
   *
   * \param  bit        PMT bit
   * \return            PMT mask
   */
  inline JPMTMask_t getPMTMask(const unsigned int bit)
  {
    if (bit < NUMBER_OF_PMT_BITS)
      return JPMTMask_t(1) << bit;
    else
      return 0;
  }


  /**
   * Auxiliary class for PMT mask.
   */
  class JPMTMask {
  public:
    /**
     * Default constructor.
     */
    JPMTMask() :
      pmt_mask(0)
    {}


    /**
     * Constructor.
     *
     * \param  mask           PMT mask
     */
    JPMTMask(const JPMTMask_t mask) :
      pmt_mask(mask)
    {}


    /**
     * Constructor.
     *
     * \param  buffer         PMT bit pattern (i.e.\ sequence of '0' and '1').
     */
    JPMTMask(const std::string& buffer) :
      pmt_mask(valueOf(buffer).getPMTMask())
    {}


    /**
     * Get PMT bit pattern.
     *
     * \return                PMT mask
     */
    JPMTMask_t getPMTMask() const
    {
      return pmt_mask;
    }


    /**
     * Add PMT mask.
     *
     * \param  mask           PMT mask
     * \return                PMT mask
     */
    JPMTMask& addPMTMask(const JPMTMask_t mask)
    {
      pmt_mask |= mask;

      return *this;
    }


    /** 
     * Add PMT mask.
     *
     * \param  mask           PMTed mask
     * \return                PMTed mask
     */
    JPMTMask& addPMTMask(const JPMTMask& mask)
    {
      return addPMTMask(mask.getPMTMask());
    }
   
    /**
     * Add PMT bit.
     *
     * \param  bit            PMT bit
     * \return                PMT mask
     */
    inline JPMTMask& addPMTBit(const unsigned int bit)
    {
      pmt_mask |= JTRIGGER::getPMTMask(bit);

      return *this;
    }


    /**
     * Add PMT channel
     * 
     * \param channel     PMT readout channel
     * \return            PMT mask
     */
    inline JPMTMask& addPMT(const JPMTReadoutAddress& channel)
    {
      return addPMTBit(channel.getTDC());
    }
    

    /**
     * Check PMT bit.
     *
     * \param  bit            PMT bit
     * \return                true if bit is set; else false
     */
    inline bool hasPMTBit(const unsigned int bit) const
    {
      return pmt_mask & JTRIGGER::getPMTMask(bit);
    }

    /** 
     * Check PMT.
     * 
     * \param  channel       PMT channel
     * \return               true if PMT is selected; else false
     */
    inline bool hasPMT(const JPMTReadoutAddress& channel) const
    {
      return hasPMTBit(channel.getTDC());
    }
       

    /**
     * Extract PMT mask.
     *
     * \param  buffer         PMT bit pattern (i.e.\ sequence of '0' and '1').
     * \return                PMT mask
     */
    static JPMTMask valueOf(const std::string& buffer)
    {
      JPMTMask pmt_mask;

      unsigned int bit = 0;

      for (std::string::const_reverse_iterator i = buffer.rbegin(); i != buffer.rend() && bit != NUMBER_OF_PMT_BITS; ++i, ++bit) {
	if (*i == '1') {
	  pmt_mask.addPMTBit(bit);
	}
      }

      return pmt_mask;
    }


    /**
     * Convert PMT mask.
     *
     * \return                PMT bit pattern (i.e.\ sequence of '0' and '1').
     */
    std::string toString() const
    {
      std::string buffer(NUMBER_OF_PMT_BITS, '0');

      unsigned int bit = 0;

      for (std::string::reverse_iterator i = buffer.rbegin(); i != buffer.rend() && bit != NUMBER_OF_PMT_BITS; ++i, ++bit) {
	if (hasPMTBit(bit)) {
	  *i = '1';
	}
      }

      return buffer;
    }


    /**
     * Read PMT mask from input.
     *
     * \param  in         input stream
     * \param  mask       PMT mask
     * \return            input stream
     */
    friend inline std::istream& operator>>(std::istream& in, JPMTMask& mask)
    {
      return in >> mask.pmt_mask;
    }


    /**
     * Write PMT mask to output.
     *
     * \param  out        output stream
     * \param  mask       PMT mask
     * \return            output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const JPMTMask& mask)
    {
      return out << mask.pmt_mask;
    }

    
    /**
     * Read PMT mask from input.
     *
     * \param  in             reader
     * \param  mask           PMT mask
     * \return                reader
     */
    friend inline JReader& operator>>(JReader& in, JPMTMask& mask)
    {
      in >> mask.pmt_mask;
 
      return in;
    }


    /**
     * Write PMT mask to output.
     *
     * \param  out           writer
     * \param  mask          PMT mask
     * \return               writer
     */
    friend inline JWriter& operator<<(JWriter& out, const JPMTMask& mask)
    {
      out << mask.pmt_mask;

      return out;
    }


    /**
     * Get size of object.
     *
     * \return               number of bytes
     */
    static int sizeOf()
    {
      return sizeof(JPMTMask_t);
    }

  protected:
    JPMTMask_t  pmt_mask;
  };
}

#endif