#ifndef TChannelId_hxx_seen
#define TChannelId_hxx_seen

#include <Rtypes.h>
#include <string>
#include <iostream>
#include "EoaCore.hxx"
#include "method_deprecated.hxx"

namespace COMET {
    /// Base class for all exceptions associated with the IGeometryId classe.
    OA_EXCEPTION(EChannelId,EoaCore);

    class IChannelId;
}

///Base class for channel identifiers.  This can answer simple interrogations
///about channel such as the sub-detector type, and can preform all of the
///basic conversions.
class COMET::IChannelId {
public:
    /// Enumeration defining the sub-detector identifiers.  These tell you
    /// which sub-detector a channel is associated with.
    enum SubDetId {
        kCDC         = 1,
        kCTH         = 2,
        kStrawTrk    = 3,
        kECAL        = 4,
        kCosmicVeto  = 5,
        kMuX         = 6,
        kProtBeamMon = 7,
        kTOF         = 8,
  kECALTrig    = 9,
        kMaxDetector
    };

    ///Construct a channel id from a raw UInt_t.
    explicit IChannelId(UInt_t id = 0);

    ///Copy constructor
    IChannelId(const COMET::IChannelId& src);

    virtual ~IChannelId();

    ///Get the sub-detector code for this channel
    virtual const SubDetId GetSubDetector() const;

    ///Check if this channel is a CDC channel.
    virtual const Bool_t IsCDCChannel() const;

    ///Check if a UInt_t is a CDC channel id, without the user having to
    ///construct a channel id.
    static const Bool_t IsCDCChannel(UInt_t code) {
       return IChannelId(code).IsCDCChannel();
    }

    ///Check if this channel is a CTH channel.
    virtual const Bool_t IsCTHChannel() const;

    ///Check if a UInt_t is a CTH channel id, without the user having to
    ///construct a channel id.
    static const Bool_t IsCTHChannel(UInt_t code) {
       return IChannelId(code).IsCTHChannel();
    }

    virtual const Bool_t IsStrawTrkChannel() const;

    static const Bool_t IsStrawTrkChannel(UInt_t code){
        return IChannelId(code).IsStrawTrkChannel();
    }

    virtual const Bool_t IsECALChannel() const;

    static const Bool_t IsECALChannel(UInt_t code){
        return IChannelId(code).IsECALChannel();
    }

    virtual const Bool_t IsTOFChannel() const;

    static const Bool_t IsTOFChannel(UInt_t code){
        return IChannelId(code).IsTOFChannel();
    }

    virtual const Bool_t IsECALTrigChannel() const;

    static const Bool_t IsECALTrigChannel(UInt_t code){
        return IChannelId(code).IsECALTrigChannel();
    }

    ///Check that this is a valid channel id, i.e. that the MSB is 1 and the
    ///sub-detector field is in range.
    virtual const Bool_t IsValid() const;

#ifdef USE_DEPRECATED_AsInt
#warning Using deprecated AsInt.
    /// Deprecated in favor of AsUInt().
    ///
    /// \deprecated Use AsUInt().
    virtual const UInt_t AsInt() const METHOD_DEPRECATED {return fChannelId;} 
#endif

    ///Get the internal UInt_t representation
    virtual const UInt_t AsUInt() const {return fChannelId;} 
  
    ///Format (as much as is possible) as a human readable string
    virtual std::string AsString() const; 

    ///Automatic conversion to char pointer (use with care).  This simplifies
    ///output to an i/o stream, but should be used with *great* caution.  The
    ///pointer to char actually points to a buffer generated by
    ///std::string::c_str() and is only valid until the next call to a
    ///non-constant string method (that includes a string distructor).
    operator const char * () const {return AsString().c_str();} 

    ///Print the detector this channel is associated with as a human readable
    ///string
    virtual std::string SubDetAsString() const;

    /// Equal operator
    virtual bool operator== (const IChannelId& rhs) const {
        return AsUInt() == rhs.AsUInt();
    }

    /// Not equal operator
    virtual bool operator!= (const IChannelId& rhs) const {
        return AsUInt() != rhs.AsUInt();
    }

    /// Less-than operator
    virtual bool operator< (const IChannelId& rhs) const {
        return AsUInt() < rhs.AsUInt();
    }

    /// Less-than-or-equal operator
    virtual bool operator<= (const IChannelId& rhs) const {
        return AsUInt() <= rhs.AsUInt();
    }

    /// Greater-than operator
    virtual bool operator> (const IChannelId& rhs) const {
        return AsUInt() > rhs.AsUInt();
    }

    /// Greater-than-or-equal operator
    virtual bool operator>= (const IChannelId& rhs) const {
        return AsUInt() >= rhs.AsUInt();
    }

protected:
    /// Implimentation of AsString for all concrete classes
    std::string GetAsString(const char* detName, UInt_t channel) const;

    /// Get a bit field out of the channel identifier.
    unsigned int GetField(int msb, int lsb) const;

    /// Set a bit field in the channel identifier.
    void SetField(int val, int msb, int lsb);

    /// Set the guard bit for this id.  This must be done explicitly in a
    /// derived class.
    void SetGuardBit();

    ///Set the sub-detector code for this channel
    virtual void SetSubDetector(int det);

    /// Define the bit fields for the channel id guard bit and sub-detector
    /// fields.  The channel id is a 32 bit field with bits number from 0
    /// (lsb) to 31 (msb).  The sub-fields are defined
    ///
    /// g dddddd xxxxxxxxxxxxxxxxxxxxxxxx
    ///
    ///    - g(1) Bit 31:  A "guard bit" for the channel identifier.  This
    ///            must be 1. 
    ///    - d(6) Bits 25-30: A six bit sub-detector identifier.
    ///    - x(25) Bits 0-24: A 25 bit field reserved to the IChannelId
    ///            sub-classes. 
    ///
    /// Note: The shift and the least significant bit (lsb) must be the same.
    enum kBitDefinitions {
        kGuardBitMSB = 31,    kGuardBitLSB = 31,
        kSubDetMSB   = 30,    kSubDetLSB   = 25,
        kSubClassMSB = 24,    kSubClassLSB = 0
    };

private:
    UInt_t fChannelId;

#define MAKE_MASK(msb,lsb) (((1<<(((msb)-(lsb))+1))-1)<<(lsb))
    enum BitMask {kGuard_Mask=MAKE_MASK(kGuardBitMSB, kGuardBitLSB)};
#undef MAKE_MASK

    ClassDef(IChannelId, 1)
};
#endif