//
// Pibero Djawotho <pibero@indiana.edu>
// Indiana University
// 5 October 2006
//

// C++ STL
#include <vector>
#include <iterator>

// ROOT
#include "TClonesArray.h"

// STAR
#include "StMuDSTMaker/COMMON/StMuEvent.h"

// Local
#include "Track.h"
#include "EmcHit.h"
#include "EmcCluster.h"
#include "CtbHit.h"
#include "Particle.h"
#include "SmdHit.h"
#include "SmdCluster.h"
#include "Event.h"

template <class T> inline void copy(TClonesArray* from, TClonesArray* to)
{
  for (int i = 0; i < from->GetEntriesFast(); ++i)
    new ((*to)[to->GetEntriesFast()]) T(*(T*)from->At(i));
}

ClassImp(Event);

Event::Event()
{
  mRunId = 0;
  mEventId = 0;
  mRefMult = 0;
  memset(mL2Result, 0, sizeof(mL2Result));
  mTracks = new TClonesArray("Track", 100);
  mEmcHits = new TClonesArray("EmcHit", 4800);
  mEmcClusters = new TClonesArray("EmcCluster", 100);
  mCtbHits = new TClonesArray("CtbHit", 256);
  mParticles = new TClonesArray("Particle", 100);
  mSmdEtaHits = new TClonesArray("SmdHit", 1000);
  mSmdPhiHits = new TClonesArray("SmdHit", 1000);
  mSmdEtaClusters = new TClonesArray("SmdCluster", 100);
  mSmdPhiClusters = new TClonesArray("SmdCluster", 100);
}

Event::Event(const Event& event)
{
  mRunId = event.mRunId;
  mEventId = event.mEventId;
  mRefMult = event.mRefMult;
  mTimeStamp = event.mTimeStamp;
  mVertex = event.mVertex;
  mMagneticField = event.mMagneticField;
  mTriggerIds = event.mTriggerIds;
  memcpy(mL2Result, event.mL2Result, sizeof(mL2Result));

  mTracks = new TClonesArray("Track", event.mTracks->GetEntriesFast());
  mEmcHits = new TClonesArray("EmcHit", event.mEmcHits->GetEntriesFast());
  mEmcClusters = new TClonesArray("EmcCluster", event.mEmcClusters->GetEntriesFast());
  mCtbHits = new TClonesArray("CtbHit", event.mCtbHits->GetEntriesFast());
  mParticles = new TClonesArray("Particle", event.mParticles->GetEntriesFast());
  mSmdEtaHits = new TClonesArray("SmdHit", event.mSmdEtaHits->GetEntriesFast());
  mSmdPhiHits = new TClonesArray("SmdHit", event.mSmdPhiHits->GetEntriesFast());
  mSmdEtaClusters = new TClonesArray("SmdCluster", event.mSmdEtaClusters->GetEntriesFast());
  mSmdPhiClusters = new TClonesArray("SmdCluster", event.mSmdPhiClusters->GetEntriesFast());

  copy<Track>(event.mTracks, mTracks);
  copy<EmcHit>(event.mEmcHits, mEmcHits);
  copy<EmcCluster>(event.mEmcClusters, mEmcClusters);
  copy<CtbHit>(event.mCtbHits, mCtbHits);
  copy<Particle>(event.mParticles, mParticles);
  copy<SmdHit>(event.mSmdEtaHits, mSmdEtaHits);
  copy<SmdHit>(event.mSmdPhiHits, mSmdPhiHits);
  copy<SmdCluster>(event.mSmdEtaClusters, mSmdEtaClusters);
  copy<SmdCluster>(event.mSmdPhiClusters, mSmdPhiClusters);

  // Rebuild network of references
  //
  for (int i = 0; i < event.mTracks->GetEntriesFast(); ++i) {
    Track* track = (Track*)event.mTracks->At(i);
    Track* track2 = (Track*)mTracks->At(i);
    track2->setEvent(this);

    for (int j = 0; j < track->emcHits().GetEntriesFast(); ++j) {
      EmcHit* hit = (EmcHit*)track->emcHits().At(j);
      int idx = event.mEmcHits->IndexOf(hit);
      EmcHit* hit2 = (EmcHit*)mEmcHits->At(idx);
      track2->emcHits().Add(hit2);
    }

    EmcCluster* cluster = track->emcCluster();
    EmcCluster* cluster2 = 0;
    if (cluster) {
      int idx = event.mEmcClusters->IndexOf(cluster);
      cluster2 = (EmcCluster*)mEmcClusters->At(idx);
    }
    track2->setEmcCluster(cluster2);

    CtbHit* hit = track->ctbHit();
    CtbHit* hit2 = 0;
    if (hit) {
      int idx = event.mCtbHits->IndexOf(hit);
      hit2 = (CtbHit*)mCtbHits->At(idx);
    }
    track2->setCtbHit(hit2);
  }

#if 1

  for (int i = 0; i < event.mEmcHits->GetEntriesFast(); ++i) {
    EmcHit* hit = (EmcHit*)event.mEmcHits->At(i);
    EmcHit* hit2 = (EmcHit*)mEmcHits->At(i);
    for (int j = 0; j < hit->tracks().GetEntriesFast(); ++j) {
      Track* track = (Track*)hit->tracks().At(j);
      int idx = event.mTracks->IndexOf(track);
      Track* track2 = (Track*)mTracks->At(idx);
      hit2->tracks().Add(track2);
    }
  }

#else

  for (int i = 0; i < event.mEmcHits->GetEntriesFast(); ++i) {
    EmcHit* hit = (EmcHit*)event.mEmcHits->At(i);
    EmcHit* hit2 = (EmcHit*)mEmcHits->At(i);
    TIter next(&hit->tracks());
    while (Track* track = (Track*)next())
      hit2->tracks().Add(mTracks->At(mTracks->BinarySearch(track)));
  }

#endif

  for (int i = 0; i < event.mEmcClusters->GetEntriesFast(); ++i) {
    EmcCluster* cluster = (EmcCluster*)event.mEmcClusters->At(i);
    EmcCluster* cluster2 = (EmcCluster*)mEmcClusters->At(i);
    for (int j = 0; j < cluster->hits().GetEntriesFast(); ++j) {
      EmcHit* hit = (EmcHit*)cluster->hits().At(j);
      int idx = event.mEmcHits->IndexOf(hit);
      EmcHit* hit2 = (EmcHit*)mEmcHits->At(idx);
      cluster2->hits().Add(hit2);
    }
  }

  for (int i = 0; i < event.mParticles->GetEntriesFast(); ++i) {
    Particle* particle = (Particle*)event.mParticles->At(i);
    Particle* particle2 = (Particle*)mParticles->At(i);
    for (int j = 0; j < particle->daughters().GetEntriesFast(); ++j) {
      Track* track = (Track*)particle->daughters().At(j);
      int idx = event.mTracks->IndexOf(track);
      Track* track2 = (Track*)mTracks->At(idx);
      particle2->daughters().Add(track2);
    }
  }
}

Event::~Event()
{
  Clear();
  delete mTracks;
  delete mEmcHits;
  delete mEmcClusters;
  delete mCtbHits;
  delete mParticles;
  delete mSmdEtaHits;
  delete mSmdPhiHits;
  delete mSmdEtaClusters;
  delete mSmdPhiClusters;
}

void Event::setEventInfo(StMuEvent* event)
{
  mRunId = event->runId();
  mEventId = event->eventId();
  mRefMult = event->refMult();
  mTimeStamp.Set(event->eventInfo().time());
  mVertex = event->primaryVertexPosition().xyz();
  mMagneticField = event->magneticField();
  vector<unsigned int> triggerIds = event->triggerIdCollection().nominal().triggerIds();
  copy(triggerIds.begin(), triggerIds.end(), inserter(mTriggerIds, mTriggerIds.begin()));
  memcpy(mL2Result, event->L2Result().GetArray(), event->L2Result().GetSize() * sizeof(unsigned int));
}

void Event::Clear(Option_t* option)
{
  mTriggerIds.clear();
  mTracks->Clear(option);
  mEmcHits->Clear(option);
  mEmcClusters->Clear(option);
  mCtbHits->Clear(option);
  mParticles->Clear(option);
  mSmdEtaHits->Clear(option);
  mSmdPhiHits->Clear(option);
  mSmdEtaClusters->Clear(option);
  mSmdPhiClusters->Clear(option);
}

float Event::energy() const
{
  float e = 0;
  TIter next(mEmcHits);
  while (EmcHit* hit = (EmcHit*)next()) { e += hit->energy(); }
  return e;
}

ostream& operator<<(ostream& out, const Event& event)
{
  out << "Run=" << event.mRunId
      << ", Event=" << event.mEventId
      << ", Date=" << event.mTimeStamp.GetDate()
      << ", Time=" << event.mTimeStamp.GetTime()
      << ", Vertex=" << event.mVertex.x() << " " << event.mVertex.y() << " " << event.mVertex.z()
      << ", Triggers=";
  copy(event.mTriggerIds.begin(), event.mTriggerIds.end(), ostream_iterator<int>(out, " "));
  out << ", nTracks=" << event.mTracks->GetEntriesFast()
      << ", nEmcHits=" << event.mEmcHits->GetEntriesFast()
      << ", nEmcClusters=" << event.mEmcClusters->GetEntriesFast()
      << ", nSmdEtaHits=" << event.mSmdEtaHits->GetEntriesFast()
      << ", nSmdPhiHits=" << event.mSmdPhiHits->GetEntriesFast()
      << ", nSmdEtaClusters=" << event.mSmdEtaClusters->GetEntriesFast()
      << ", nSmdPhiClusters=" << event.mSmdPhiClusters->GetEntriesFast()
      << ", nCtbHits=" << event.mCtbHits->GetEntriesFast()
      << ", nParticles=" << event.mParticles->GetEntriesFast();
  return out;
}
