
#ifndef __TRIGGERED_EVENT__
#define __TRIGGERED_EVENT__

#include <string>
#include <iostream>
#include <iomanip>

#include "antcc/Event.hh" 
#include "antcc/Hit.hh" 
#include "dataformat/PhysicsEvent.hh" 
#include "trigger/trigger.hh"
#include "trigger/trigger_io.hh"
#include "clock/clock.hh"


class TriggeredEvent : public UserEvent {
protected:
  class Key : public Event::Key {
    /**
     * fixed key words
     */
  public:
    static const std::string trigger;
    static const std::string triggered_hit;
    static const std::string triggered_rawhit;
    static const std::string triggered_spehit;
   };

public:
  
  /**
   * default constructor
   */
  TriggeredEvent() : 
    UserEvent(), 
    trigger_(0) 
  {}

  /**
   * constructor
   * \parm in    input stream
   */
  TriggeredEvent(std::istream& in) :
    UserEvent(),
    trigger_(0)
  { 
    Streamer(in); 
  }

  /**
   * constructor
   * \parm event Monte Carlo event
   */
  TriggeredEvent(const Event& event) : 
    UserEvent(event), 
    trigger_(0) 
  {}

  /**
   * Add physics event information.
   * \param  event     physics event
   * \return           TriggeredEvent
   */
  TriggeredEvent& operator+=(const PhysicsEvent& event)
  {
    trigger_ = event.EventType();
      
    // copy triggered hits
    copy(event.TriggeredSPEHits().begin(), 
	 event.TriggeredSPEHits().end(), 
	 back_inserter(triggeredSpeHitList_));
				
    return *this;
  }
  
  /**
   * Add snapshot data
   * \param  interface interface to calibration and alignment information
   * \param  eventTime event time
   * \param  begin_    begin hit iterator
   * \param  end_      end hit iterator
   */
  TriggeredEvent& add(TRIGGER_IO::TriggerInterfaceModel& interface, 
		      std::vector<SPE_Hit>::const_iterator begin_,
		      std::vector<SPE_Hit>::const_iterator end_)
  {
    const double timeOffset = CLOCK::getTimeOfRTS(eventTime()) - eventTime();

    for (std::vector<SPE_Hit>::const_iterator i = begin_; i != end_; ++i) {
	
      TRIGGER::header id(i->lcm_id, i->ars_id);

      TRIGGER_IO::SPE_reader spe_reader = interface.getSPEreader(id);
      
      RawHitList_.push_back(RawHit(std::distance(begin_,i),
				   interface.getPmtId(id),
				   spe_reader.timeSinceRTS(i->timestamp,i->tvc) + timeOffset,
				   spe_reader.a(i->avc)));
    }

    return *this;
  }


  /**
   * Add RawHit information
   * \param  interface interface to calibration and alignment information
   */
  void makeRawHits(TRIGGER_IO::TriggerInterfaceModel& interface)
  {
    const double timeOffset = CLOCK::getTimeOfRTS(eventTime()) - eventTime();

    for (std::vector<SPE_Hit>::iterator i = triggeredSpeHitList_.begin(); i != triggeredSpeHitList_.end(); ++i) {
	
      TRIGGER::header id(i->lcm_id, i->ars_id);

      TRIGGER_IO::SPE_reader spe_reader = interface.getSPEreader(id);
      
      triggeredRawHitList_.push_back(RawHit(std::distance(triggeredSpeHitList_.begin(),i),
					    interface.getPmtId(id),
					    spe_reader.timeSinceRTS(i->timestamp,i->tvc) + timeOffset,
					    spe_reader.a(i->avc)));
    }
  }
      
  /**
   * Add (true) Hit information
   * \param  interface interface to calibration and alignment information
   */
  void makeHits(TRIGGER_IO::TriggerInterfaceModel& interface)
  {
    const double MAXIMAL_TIME_DIFFERENCE = 50; // [ns]

    const double& timeOfEvent = eventTime();
    const double& timeOfRTS   = CLOCK::getTimeOfRTS(timeOfEvent);

    for (std::vector<Hit>::const_iterator source = HitList().begin(); source != HitList().end(); ++source) {
      
      for (std::vector<SPE_Hit>::const_iterator target = triggeredSpeHitList_.begin(); target != triggeredSpeHitList_.end(); ++target) {
	
	TRIGGER::header id(target->lcm_id,target->ars_id);
	
	if (id == interface.getHeader(source->pm_id(),0) ||
	    id == interface.getHeader(source->pm_id(),1)) {
	  
	  TRIGGER_IO::SPE_reader spe_reader = interface.getSPEreader(id);
	  
	  const double ta = timeOfEvent + source->pure_dt();
	  const double tb = timeOfRTS   + spe_reader.timeSinceRTS(target->timestamp,target->tvc);
	  
	  if (std::fabs(ta-tb) <= MAXIMAL_TIME_DIFFERENCE) {
	    triggeredHitList_.push_back(*source);
	    break;
	  }
	}
      }
    }
  }

  /**
   * Processing of trigger keys.
   * \param in             input stream
   * \param key            last read key
   * \param isReadingEvent true if reading event; else false  
   */
  virtual void filter(std::istream& in, const std::string& key, const bool& isReadingEvent)
  {
    if (isReadingEvent) {
      if      (key == Key::trigger) {
	in >> std::hex >> trigger_ >> std::dec;
	in.ignore(INT_MAX,'\n');
      } else if (key == Key::triggered_hit)
	triggeredHitList_.push_back(Hit(in));  
      else if (key == Key::triggered_rawhit)
	triggeredRawHitList_.push_back(RawHit(in));  
      else if (key == Key::triggered_spehit)
	triggeredSpeHitList_.push_back(SPE_Hit(in));  
      else
	UserEvent::filter(in, key, isReadingEvent);
    }
  }

  std::istream& Streamer(std::istream& in)
  {
    trigger_ = 0;

    triggeredHitList_.clear();
    triggeredRawHitList_.clear();
    triggeredSpeHitList_.clear();

    UserEvent::Streamer(in);

    return in;
  }

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

  class OStream : public UserEvent::OStream {
  public:
    OStream(std::ostream& out, const TriggeredEvent& event) :
      UserEvent::OStream(out, event)
    {
      *this << Key::trigger << " " << std::hex << event.trigger() << " " << std::dec << std::endl;

      for (std::vector<Hit>::const_iterator i = event.triggeredHitList().begin(); i != event.triggeredHitList().end(); ++i)
	*this << Key::triggered_hit << " " << *i << std::endl;

      for (std::vector<RawHit>::const_iterator i = event.triggeredRawHitList().begin(); i != event.triggeredRawHitList().end(); ++i)
	*this << Key::triggered_rawhit << " " << *i << std::endl;

      for (std::vector<SPE_Hit>::const_iterator i = event.triggeredSpeHitList().begin(); i != event.triggeredSpeHitList().end(); ++i)
	*this << Key::triggered_spehit << " " << *i << std::endl;
    }
  };

  const unsigned int trigger() const { return trigger_; } //!< trigger bit pattern

  const std::vector<Hit>&     triggeredHitList()    const { return triggeredHitList_; }
  const std::vector<RawHit>&  triggeredRawHitList() const { return triggeredRawHitList_; }
  const std::vector<SPE_Hit>& triggeredSpeHitList() const { return triggeredSpeHitList_; }
 
private:
  unsigned int         trigger_;
  std::vector<Hit>     triggeredHitList_;
  std::vector<RawHit>  triggeredRawHitList_;
  std::vector<SPE_Hit> triggeredSpeHitList_;
};

#endif

