
#ifndef __REVENT__
#define __REVENT__

#include <string>
#include <iostream>
#include <iomanip>
#include <map>
#include <functional>

#include "antcc/Event.hh"
#include "trigger/util.hh"

#include "TriggeredEvent.hh"



class REvent : public TriggeredEvent {

public:

  class direction {
  public:
    direction() :
      theta_(0),
      phi_(0)
    {}

    direction(const double& theta,
	      const double& phi) :
      theta_(theta),
      phi_(phi)
    {}

    direction(std::istream& in) :
      theta_(0),
      phi_(0)
    {
      read(in);
    }
    
    std::istream& read(std::istream& in) 
    {
      in >> theta_ >> phi_;
      return in;
    }

    std::ostream& write(std::ostream& out) const
    {
      out << theta() << ' ' 
	  << phi();
      return out;
    }

    const double& theta()    const { return theta_; }
    const double& phi()      const { return phi_; }

    double dx() const { return sin(theta_) * cos(phi_); }
    double dy() const { return sin(theta_) * sin(phi_); }
    double dz() const { return cos(theta_); }

  protected:
    double theta_;
    double phi_;
  };


  class track1d : public direction {
  public:
    track1d() :
      direction(),     
      x_(0),
      y_(0),
      z_(0),
      t0_(0),
      beta_(1),
      prob_(0)
    {}

    track1d(const double& x,
	    const double& y,
	    const double& z,
	    const double& theta,
	    const double& phi,
	    const double& t0,
	    const double& beta,
	    const double& prob) :
      direction(theta,phi),
      x_(x),
      y_(y),
      z_(z),
      t0_(t0),
      beta_(beta),
      prob_(prob)
    {}

    track1d(std::istream& in) :
      direction(),
      x_(0),
      y_(0),
      z_(0),
      t0_(0),
      beta_(1),
      prob_(0)
    {
      read(in);
    }
    
    std::istream& read(std::istream& in) 
    {
      in >> x_ >> y_ >> z_;
      direction::read(in);
      in >> t0_ >> beta_;

      std::string buffer;
      getline(in,buffer);
      std::istrstream is(buffer.c_str());

      if (is)
	is >> prob_;
      else
	prob_ = 0;

      return in;
    }

    std::ostream& write(std::ostream& out) const
    {
      out << x()     << ' ' 
	  << y()     << ' ' 
	  << z()     << ' ' 
	  << theta() << ' ' 
	  << phi()   << ' ' 
	  << t0()    << ' ' 
	  << beta()  << ' ' 
	  << prob();
      return out;
    }

    const double& x()    const { return x_; }
    const double& y()    const { return y_; }
    const double& z()    const { return z_; }
    const double& t0()   const { return t0_; }
    const double& beta() const { return beta_; }
    const double& prob() const { return prob_; }

  protected:
    double x_;
    double y_;
    double z_;
    double t0_;
    double beta_;
    double prob_;
  };

  class track3d {
  public:
    track3d() :
      a_(0),
      b_(0),
      theta_(0),
      phi_(0),
      t0_(0),
      beta_(1),
      prob_(0),
      symmetry_(-1)
    {}
    
    track3d(const double& a,
	    const double& b,
	    const double& theta,
	    const double& phi,
	    const double& t0,
	    const double& beta,
	    const double& prob,
	    const int&    symmetry) :
      a_(a),
      b_(b),
      theta_(theta),
      phi_(phi),
      t0_(t0),
      beta_(beta),
      prob_(prob),
      symmetry_(symmetry)
    {}
    
    track3d(std::istream& in) :
      a_(0),
      b_(0),
      theta_(0),
      phi_(0),
      t0_(0),
      beta_(1),
      prob_(0),
      symmetry_(-1)
    {
      read(in);
    }
    
    std::istream& read(std::istream& in) 
    {
      in >> a_ >> b_ >> theta_ >> phi_ >> t0_ >> beta_;

      std::string buffer;
      getline(in,buffer);
      std::istrstream is(buffer.c_str());

      if (is)
	is >> prob_;
      else
	prob_ = 0;

      if (is)
	is >> symmetry_;
      else
	symmetry_ = -1;

      return in;
    }

    std::ostream& write(std::ostream& out) const
    {
      out << a()     << ' ' 
	  << b()     << ' ' 
	  << theta() << ' ' 
	  << phi()   << ' ' 
	  << t0()    << ' ' 
	  << beta()  << ' ' 
	  << prob()  << ' ' 
	  << symmetry();
      return out;
    }

    const double& a()        const { return a_; }
    const double& b()        const { return b_; }
    const double& theta()    const { return theta_; }
    const double& phi()      const { return phi_; }
    const double& t0()       const { return t0_; }
    const double& beta()     const { return beta_; }
    const double& prob()     const { return prob_; }
    const int&    symmetry() const { return symmetry_; }

  protected:
    double a_;
    double b_;
    double theta_;
    double phi_;
    double t0_;
    double beta_;
    double prob_;
    int    symmetry_;
  };
  
public:
  REvent() : TriggeredEvent() {}

  REvent(const TriggeredEvent& event) : TriggeredEvent(event) {}

  virtual void filter(std::istream& in, const std::string& key, const bool& isReadingEvent)
  {
    if (isReadingEvent) {

      if (key == "cluster:") {
	int n;
	in >> n;
	cluster.insert(std::make_pair(n,direction(in)));
	return;
      }

      if (key == "prefit:") {
	int n;
	in >> n;
	prefit.insert(std::make_pair(n,track1d(in)));
	return;
      }

      if (key == "prefitmu:") {
	int n;
	in >> n;
	prefitmu.insert(std::make_pair(n,track1d(in)));
	return;
      }

 
      if (key == "fit:") {
	int n;
	in >> n;
	fit.insert(std::make_pair(n,track3d(in)));
	return;
      }

      if (key == "fitmu:") {
	int n;
	in >> n;
	fitmu.insert(std::make_pair(n,track3d(in)));
	return;
      }
   }

    TriggeredEvent::filter(in, key, isReadingEvent);
  }

  /** set number of event */
  void set_eventNumber(const int& event_number) { eventNumber_ = event_number; }

  std::istream& Streamer(std::istream&);
  std::ostream& Streamer(std::ostream&) const;

  class OStream : public TriggeredEvent::OStream {
  public:
    OStream(std::ostream& out, const REvent& event);
  };

  typedef std::multimap<int, direction, std::greater<int> > t_cluster;
  typedef std::multimap<int, track1d,   std::greater<int> > t_prefit;
  typedef std::multimap<int, track3d,   std::greater<int> > t_fit;

  t_cluster cluster;
  t_prefit  prefit;
  t_prefit  prefitmu;
  t_fit     fit;
  t_fit     fitmu;
};



std::ostream& operator<<(std::ostream& out, const REvent::direction& o) { return o.write(out); }
std::ostream& operator<<(std::ostream& out, const REvent::track1d    o) { return o.write(out); }
std::ostream& operator<<(std::ostream& out, const REvent::track3d    o) { return o.write(out); }

std::istream& operator>>(std::istream& in, REvent::direction& o) { return o.read(in); }
std::istream& operator>>(std::istream& in, REvent::track1d&   o) { return o.read(in); }
std::istream& operator>>(std::istream& in, REvent::track3d&   o) { return o.read(in); }


REvent::OStream::OStream(std::ostream& out, const REvent& event) :
  TriggeredEvent::OStream(out, event)
{
  for (t_cluster::const_iterator i = event.cluster.begin(); i != event.cluster.end(); ++i)
    *this << "cluster: " << i->first << ' ' << i->second << endl;
  
  for (t_prefit::const_iterator i = event.prefit.begin(); i != event.prefit.end(); ++i)
    *this << "prefit:  " << i->first << ' ' << i->second << endl;

  for (t_prefit::const_iterator i = event.prefitmu.begin(); i != event.prefitmu.end(); ++i)
    *this << "prefitmu:  " << i->first << ' ' << i->second << endl;
  
  for (t_fit::const_iterator i = event.fit.begin(); i != event.fit.end(); ++i)
    *this << "fit:     " << i->first << ' ' << i->second << endl;

  for (t_fit::const_iterator i = event.fitmu.begin(); i != event.fitmu.end(); ++i)
    *this << "fitmu:     " << i->first << ' ' << i->second << endl;
}


std::istream& REvent::Streamer(std::istream& in)
{
  cluster.clear();
  prefit.clear();  
  prefitmu.clear();
  fit.clear();  
  fitmu.clear();
  return TriggeredEvent::Streamer(in);
}


std::ostream& REvent::Streamer(std::ostream& out) const
{
  REvent::OStream os(out,*this);
  return out;
}



UTIL::position  getPosition(const Vec3D& v)  { return UTIL::position (v.x, v.y, v.z); }
UTIL::direction getDirection(const Vec3D& v) { return UTIL::direction(v.x, v.y, v.z); }

/**
 * dot product
 * \param  a direction
 * \param  b direction
 * \return   dot product
 */
static inline double dot(const UTIL::direction& a, const REvent::direction& b)
{
  return (a.dx() * b.dx() +
	  a.dy() * b.dy() +
	  a.dz() * b.dz());
}

/**
 * dot product
 * \param  a direction
 * \param  b direction
 * \return   dot product
 */
static inline double dot(const REvent::direction& a, const UTIL::direction& b)
{
  return (a.dx() * b.dx() +
	  a.dy() * b.dy() +
	  a.dz() * b.dz());
}



class TDirection {
public:
  TDirection(const UTIL::direction& dir) :
    dir_(dir)
  {}

  bool operator()(const UTIL::direction& a, const UTIL::direction& b)
  {
    return dot(dir_,a) > dot(dir_,b);
  }

  bool operator()(REvent::t_cluster::const_reference a, REvent::t_cluster::const_reference b)
  {
    UTIL::direction a_(a.second.theta(), a.second.phi());
    UTIL::direction b_(b.second.theta(), b.second.phi());
    
    return (*this)(a_,b_);
  }

  bool operator()(REvent::t_prefit::const_reference a, REvent::t_prefit::const_reference b)
  {
    UTIL::direction a_(a.second.theta(), a.second.phi());
    UTIL::direction b_(b.second.theta(), b.second.phi());
    
    return (*this)(a_,b_);
  }

  const UTIL::direction& dir_;
};

#endif

