#ifndef __JDETECTOR__JPMTIDENTIFIER__
#define __JDETECTOR__JPMTIDENTIFIER__

#include <istream>
#include <ostream>
#include <iomanip>

#include "JIO/JSerialisable.hh"

#include "JDetector/JModuleIdentifier.hh"
#include "JDetector/JPMTReadoutAddress.hh"
#include "Jeep/JPrint.hh"


/**
 * \author mdejong
 */

namespace JDETECTOR {}
namespace JPP { using namespace JDETECTOR; }

namespace JDETECTOR {

  using JIO::JReader;
  using JIO::JWriter;


  /**
   * PMT identifier.
   */
  class JPMTIdentifier :
    public JModuleIdentifier,
    public JPMTReadoutAddress
  {
  public:
    /**
     * Default constructor.
     */
    JPMTIdentifier() :
      JModuleIdentifier (),
      JPMTReadoutAddress()
    {}
    

    /**
     * Constructor.
     *
     * \param  id             module identifier
     * \param  tdc            TDC
     */
    JPMTIdentifier(const JModuleIdentifier& id,
		   const int                tdc) :
      JModuleIdentifier (id),
      JPMTReadoutAddress(tdc)
    {}


    /**
     * Get PMT identifier.
     *
     * \return                PMT identifier
     */
    const JPMTIdentifier& getPMTIdentifier() const
    {
      return static_cast<const JPMTIdentifier&>(*this);
    }


    /**
     * Set PMT identifier.
     *
     * \param  id             PMT identifier
     */
    void setPMTIdentifier(const JPMTIdentifier& id)
    {
      static_cast<JPMTIdentifier&>(*this) = id;
    }


    /**
     * Get module identifier.
     *
     * \return                module identifier
     */
    int getModuleID() const 
    { 
      return getID();
    }


    /**
     * Get PMT address (= TDC).
     *
     * \return                PMT address
     */
    int getPMTAddress() const 
    { 
      return tdc;
    }


    /**
     * Check validity.
     *
     * \return                true if PMT identifier is valid; else false
     */
    bool is_valid() const
    {
      return !(getID() < 0 || tdc < 0);
    }
  

    /**
     * Read PMT identifier from input.
     *
     * \param  in             input stream
     * \param  object         PMT identifier
     * \return                input stream
     */
    friend inline std::istream& operator>>(std::istream& in, JPMTIdentifier& object)
    {
      in >> static_cast<JModuleIdentifier&> (object);
      in >> static_cast<JPMTReadoutAddress&>(object);

      return in;
    }


    /**
     * Write PMT identifier to output.
     *
     * \param  out            output stream
     * \param  object         PMT identifier
     * \return                output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const JPMTIdentifier& object)
    {
      using namespace std;
      
      out << setw(10) << static_cast<const JModuleIdentifier&> (object) << ' ';
      out << setw(2)  << static_cast<const JPMTReadoutAddress&>(object);

      return out;
    }


    /**
     * Read PMT identifier from input.
     *
     * \param  in             reader
     * \param  object         PMT identifier
     * \return                reader
     */
    friend inline JReader& operator>>(JReader& in, JPMTIdentifier& object)
    {
      in >> static_cast<JModuleIdentifier&> (object);
      in >> static_cast<JPMTReadoutAddress&>(object);

      return in;
    }


    /**
     * Write PMT identifier to output.
     *
     * \param  out            writer
     * \param  object         PMT identifier
     * \return                writer
     */
    friend inline JWriter& operator<<(JWriter& out, const JPMTIdentifier& object)
    {
      out << static_cast<const JModuleIdentifier&> (object);
      out << static_cast<const JPMTReadoutAddress&>(object);

      return out;
    }
  };

 
  /**
   * Less than operator for PMT identifiers.
   *
   * \param  first       PMT identifier
   * \param  second      PMT identifier
   * \result             true if first PMT lower than second PMT; else false
   */
  inline bool operator<(const JPMTIdentifier& first, const JPMTIdentifier& second)
  {
    if (first.getModuleID() == second.getModuleID())
      return first.getPMTAddress() < second.getPMTAddress();
    else
      return first.getModuleID()   < second.getModuleID();
  }

 
  /**
   * Equal operator for PMT identifiers.
   *
   * \param  first       PMT identifier
   * \param  second      PMT identifier
   * \result             true if first PMT equal second PMT; else false
   */
  inline bool operator==(const JPMTIdentifier& first, const JPMTIdentifier& second)
  {
    return (first.getModuleID()   == second.getModuleID()  &&
	    first.getPMTAddress() == second.getPMTAddress());
  }


  /**
   * Get PMT label for monitoring and other applications.\n
   * The format is "(XXXXXXXXXX,YY)", where XXXXXXXXXX is the module idetifier and YY the PMT readout channel.
   * 
   * \param  id                 PMT identifier
   * \return                    label
   */
  inline std::string getLabel(const JPMTIdentifier& id)
  {
    using namespace std;
    using namespace JPP;

    return MAKE_STRING("(" << FILL(10,'0') << id.getID() << "," << FILL(2,'0') << id.getPMTAddress() << ")");
  }
}

#endif