#ifndef OAEVENT_IHIT_HXX
#define OAEVENT_IHIT_HXX

#include <iostream>
#include <vector>

#include <TObject.h>
#include <TVector3.h>

#include "IDatum.hxx"
#include "IHandle.hxx"
#include "IChannelId.hxx"
#include "IGeometryId.hxx"

#include "method_deprecated.hxx"

namespace COMET {
    class IHit;

    /// Base class for all exceptions associated with the IHit classes.
    OA_EXCEPTION(EHit,EoaCore);

    /// The index of the requested contributor, digit, or channel was out of
    /// range.
    OA_EXCEPTION(EHitOutOfRange,EoaCore);

#ifdef USE_EDigitOutOfRange
    /// The index of the requested digit was out of range.  This isn't used
    /// since all the OutOfRange exceptions use EHitOutOfRange to make traps
    /// less complex.
    OA_EXCEPTION(EDigitOutOfRange,EoaCore);
#endif
}

/// The base class for a hit detector element.  This corresponds to a real
/// measurement inside the detector after it has been fully calibrated.  Hits
/// correspond to a particular geometric element inside of the detector.  In
/// many parts of the detector, a geometric element also corresponds to a
/// particular electronics channel, however, the match to the detector
/// geometry, and not the electroncis channel.
class COMET::IHit : public TObject {
public:
    IHit();
    IHit(const COMET::IHit& h);
    virtual ~IHit();

    /// Return the geometry identifier of the volume associated with this hit.
    /// This uniquely identifies the volume containing the hit (e.g. the
    /// scintillator bar, or the TPC micromega pad).  Notice that it is
    /// possible for there to be more than one hit in a single volume.  For
    /// instance, if a scintillator has double ended readout, it will have two
    /// associated channels that will both generate a hit for the same volume.
    /// This sort of double hit will generally be handled using a IReconHit.
    ///
    /// For most users, the geometry identifiers replace the functionality of
    /// the TGeoManager node identifiers.  The most common usage of the node
    /// identifier is to set the current node using TGeoManager::CdNode().
    /// The same operation can be done with a IGeometryId using
    /// IGeomIdManager::CdId(IGeometryId).  The CdId method makes the volume
    /// identified by the IGeometryId into the current TGeoManager volume, and
    /// then the user can use the various TGeoManager methods to query
    /// information.  For instance, the node identifier associated with a
    /// IGeometryId can be found using
    ///
    /// \code
    /// IHandle<IHit> myHit = ... Must have a value ...
    /// IOADatabase::Get().GeomId().CdId(myHit->GetGeomId());
    /// int node = gGeoManager->GetCurrentNodeId();
    /// \endcode
    ///
    /// Notice that using IGeomIdManager::CdId() changes the state of the
    /// TGeoManager in exactly the same way as TGeoManager::CdNode().
    ///
    /// The name of the volume is accessible using IGeometryId::GetName(), and
    /// the position is available either through IHit::GetPosition(), or
    /// IGeometryId::GetPosition().
    virtual IGeometryId GetGeomID(void) const;

    /// Return the calibrated "charge" for the hit.
    virtual double GetCharge(void) const;

    /// Return true if the calibrated charge is valid. 
    virtual bool HasValidCharge(void) const;

    /// Return the calibrated "time" for the hit.
    virtual double GetTime(void) const;

    /// Return true if the calibrated time is valid.
    virtual bool HasValidTime(void) const;

    /// The center of the volume associated with this hit.
    virtual const TVector3& GetPosition(void) const;

    /// Return the "spread" of the hit position.  This is the extent in the X,
    /// Y, and Z directions.  For instance, a P0D bar is about
    /// (3cm)x(2m)x(1.5cm), so the spread is (1.3cm)x(1m)x(0.75cm)
    virtual const TVector3& GetSpread(void) const;

    /// Return the "uncertainty" of the hit position.  This is the position
    /// resolution of the hit.
    virtual const TVector3& GetUncertainty(void) const;


    /// Print the hit information.
    virtual void ls(Option_t *opt = "") const;


    /// Return the channel identifier associated with this hit.  If the hit
    /// has an associated channel identifier, then this will return a valid
    /// IChannelId.  If there isn't an associated hit, this will return an
    /// invalid value (check with IChannelId::IsValid).  If the index is out
    /// of range, then this will throw an EHitOutOfRange exception.  This
    /// information is also available through the digit, and it is an error
    /// condition if the two values disagree.
    virtual IChannelId GetChannelID(int i=0) const;

    /// Return the number of channel identifiers associated with the hit.
    virtual int GetChannelIDCount() const;

    /// Define the status bits used by the IHit object.  These can't collide
    /// with any status bits defined in TObject (the parent class for IHit),
    /// and none of the IHit children can define a status bit that collides
    /// with these definitions.  Bits 14 to 23 are available for use.
    enum EStatusBits {
      kInvalidTime        = BIT(18),   // if time is not valid.
      kInvalidCharge      = BIT(19)    // if charge is not valid.
    };

protected:
    
    /// Set the validity of the calibrated time.
    virtual void SetTimeValidity(bool valid);

    /// Set the validity of the calibrated charge.
    virtual void SetChargeValidity(bool valid);
    
    /// The geometry node that was hit
    IGeometryId fGeomID;

private:
    ClassDef(IHit,5);
};
#endif