#ifndef TReconCluster_hxx_seen
#define TReconCluster_hxx_seen

#include <TMatrixT.h>
#include <TMatrixTSym.h>

#include "IHandle.hxx"
#include "IReconBase.hxx"
#include "IClusterState.hxx"
#include "EoaCore.hxx"

namespace COMET {
    class IReconCluster;

    OA_EXCEPTION(EReconCluster,EoaCore);
    OA_EXCEPTION(EMomentsSize,EReconCluster);
}

/// Represent an extended energy deposition centered at a position (i.e. a
/// spheroidal blob of energy).  This type of energy deposit is described by
/// the amount of energy, and the central position of the deposit
/// (deposit,position,time) in an COMET::IClusterState object.  The specific hits
/// associated with the cluster are available through the GetHits method.  The
/// moments of the distribution can be retrieved using the GetMoments()
/// method.
/// 
/// A IReconCluster object is a relatively heavy object.  Small collecions of
/// hits (e.g. pairs of hits in a single P0D plane, or very local TPC charge
/// depositions) should be saved using a IComboHit.  See COMET::TChargeCluster2D
/// as an example.
///
/// The COMET::IReconCluster class is intended to describe the geometry of the
/// energy deposition in a detector, and not make the association with a
/// particular particle identification.  Assignment of particle types to the
/// detector measurements is done in the COMET::TReconParticle class.
class COMET::IReconCluster: public COMET::IReconBase {
public: 
    typedef TMatrixTSym<float> MomentMatrix;

    IReconCluster();

    /// copy constructor
    IReconCluster(const COMET::IReconCluster& cluster);

    virtual ~IReconCluster();

    /// Get the energy deposited in the cluster.
    double GetEDeposit() const;

    /// Get the cluster position.
    TLorentzVector GetPosition() const;

    /// Get the track starting position uncertainty.
    TLorentzVector GetPositionVariance() const;

    /// Get the number of (non-free) spacial dimensions 
    int GetDimensions() const;

    /// Check if this cluster has X information.
    bool IsXCluster() const;

    /// Check if this cluster has Y information.
    bool IsYCluster() const;

    /// Check if this cluster has Z information.
    bool IsZCluster() const;

    /// Get the moments of the charge distribution.
    const MomentMatrix& GetMoments() const;
    
    /// Set the moments of the charge distribution.
    void SetMoments(double xx, double yy, double zz, 
                    double xy, double xz, double yz);

    /// Set the moments of the charge distribution.  The input matrix is
    /// assumed to be symmetric and only the upper half of the elements are
    /// accessed.
    void SetMoments(const TMatrixT<double>& moments);
    
    /// @{ Fill the COMET::IReconCluster and COMET::IClusterState objects from a
    /// COMET::IHitSelection.  The position is set to average hit position (the
    /// hit position uncertainty is used), and the energy deposit is the sum
    /// of the hit charges.
    void FillFromHits(const char* name, const COMET::IHitSelection& hits) {
        FillFromHits(name, hits.begin(), hits.end());
    }
    void FillFromHits(const char* name, 
                      COMET::IHitSelection::const_iterator b,
                      COMET::IHitSelection::const_iterator e);
    /// @}

    /// List the results of in the cluster.
    virtual void ls(Option_t* opt = "") const; 

private:

    /// The moments for this cluster.
    MomentMatrix fMoments;

    ClassDef(IReconCluster,1);
};
#endif