
#ifndef __RHIT__
#define __RHIT__

#include <iostream>
#include <iomanip>
#include <iterator>



/**
 * Auxiliary class to define a hit.
 * A hit is given by its (3D) position, the hit time and amplitude
 */
class RHit : public TRIGGER::header, public TRIGGER::position, public RawHit {
public:
  RHit(const TRIGGER::header&   id, 
       const TRIGGER::position& pos, 
       const RawHit&            hit,
       const float&             sigma = 0) :
    TRIGGER::header(id),
    TRIGGER::position(pos),
    RawHit(hit),
    sigma_(sigma),
    n_(1),
    status_(true)
  {}
  
  RHit() :
    TRIGGER::header(),
    TRIGGER::position(),
    RawHit(),
    sigma_(0),
    n_(0),
    status_(false)
  {}

  const double& t()     const { return dt(); }
  const double& a()     const { return npe(); }
  const int&    n()     const { return n_; }
  const double& sigma() const { return sigma_; } 

  RHit& operator+=(const RHit& o)
  {
    if (lcm_id() != o.lcm_id())
      throw std::invalid_argument("lcm identifier mismatch");

    // any arlink
    arslink_ = -1; 

    // earliest time
    if (o.t() < t()) {
      set_dt(o.t());
      x_ = o.x();
      y_ = o.y();
      z_ = o.z();
    }
    
    // sum amplitudes
    set_npe(npe() + o.npe());

    //
    ++n_;

    return *this;
  }

  void print(std::ostream& out = std::cout) const
  {
    out << ' ' << std::setw(5) << lcm_id() 
	<< ' ' << std::setw(2) << arslink()
	<< ' ' << std::setw(7) << std::setprecision(4) << x()
	<< ' ' << std::setw(7) << std::setprecision(4) << y()
	<< ' ' << std::setw(7) << std::setprecision(4) << z()
	<< ' ' << std::setw(9) << std::setprecision(6) << t()
	<< ' ' << std::setw(9) << std::setprecision(6) << sigma();
  }

  /**
   * Sort hits.
   *
   * Sort hierarchy: LCM identifier <== ARS identifier <== time
   *
   * \param a  RHit object
   * \param b  RHit object
   * \return   true if a earlier than b; else false
   */
  static inline bool Sorter(const RHit& a, const RHit& b)
  {
    if (a.lcm_id() == b.lcm_id())
      if (a.arslink()/2 == b.arslink()/2)
	return a.t() < b.t();
      else
	return a.arslink()/2 < b.arslink()/2;
    else
      return a.lcm_id() < b.lcm_id();
  }

private:
  double sigma_;
  int    n_;
public:
  bool   status_;
};

static inline ostream& operator<<(ostream& out, RHit& o) { o.print(out); return out; }


/**
 * ARS identifier comparison
 * \param a  RHit object
 * \param b  RHit object
 * \return   true if a and b have same ARS identifier; else false
 */
static inline bool ARS_comparitor(const RHit& a, const RHit& b)
{
  return (a.lcm_id()  == b.lcm_id() &&
	  a.arslink() == b.arslink());
}

/**
 * PMT identifier comparison
 * \param a  RHit object
 * \param b  RHit object
 * \return   true if a and b have same LCM identifier; else false
 */
static inline bool PMT_comparitor(const RHit& a, const RHit& b)
{
  return (a.lcm_id()    == b.lcm_id() &&
	  a.arslink()/2 == b.arslink()/2);
}

/**
 * LCM identifier comparison
 * \param a  RHit object
 * \param b  RHit object
 * \return   true if a and b have same LCM identifier; else false
 */
static inline bool LCM_comparitor(const RHit& a, const RHit& b)
{
  return a.lcm_id() == b.lcm_id();
}

template<class hitIterator>
static inline hitIterator merge(hitIterator begin_, hitIterator end_)
{
  sort(begin_, end_, RHit::Sorter);

  /*
  for (hitIterator i = begin_; i != end_; ++i) {

    cout << "Hit lcmid, pmid, arslink, time, ampli : " << i->lcm_id() << " " << i->pm_id() << " "
	 << i->arslink() << " " << i->t() << " " << i->a() << endl;
  }
  */
    
  for (hitIterator i = begin_; i != end_; ) {

    hitIterator j = i;
    
    for (++j; j != end_ && j->lcm_id() == i->lcm_id(); ++j) {
      *i += *j;
      j->status_ = false;
    }
    
    i = j;
  }
  
  // removed hits that are merged
  
  for (hitIterator i = begin_; i != end_; ) {
    if (!i->status_)
      iter_swap(i,--end_);
    else
      ++i;
  }

  return end_;
}

#endif

