// =====================================================================
//  $Id: TEventManager.cc,v 1.7 2004/03/07 10:30:30 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TEventManager.cc,v $
//  Revision 1.7  2004/03/07 10:30:30  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.6  2004/03/04 14:53:43  goiwai
//  BeginOfEventAction -> AtFirst
//  EndOfEventAction -> AtLast
//  WaitEvent -> WaitTrigger
//
//  Revision 1.5  2003/10/06 17:01:32  goiwai
//  Clear()はRunManagerのStopRun()で呼ばれるんですが,そこで
//  theLastAccessID=-1としました.
//
//  Revision 1.4  2003/10/06 16:50:25  goiwai
//  ちょっとわかりづらい仕様なんですけど,CLDAQではイベントスタックはRunオ
//  ブジェクトが抱えます.このイベントスタックへのアクセスメソッドを
//  EventManagerに追加しました.
//  動作としてはクリア,検索,最新のイベント,前回のイベント,次のイベントの取
//  得です.
//  イベントマネージャーを通じてスタックのイベントを取得するたびに,スタッ
//  クのヒストリは更新されます.LastEventとかNextEventというのはこのヒスト
//  リを見てEventを返してきます.スタックサイズが足りなくてすでに目的のイベ
//  ントが存在しないときはEventID=-1のEventが返ってきます.
//
//  Revision 1.3  2003/07/30 16:18:09  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TEventManager.hh"
#include "TRunManager.hh"
#include "TEventAction.hh"
#include "TDataRecord.hh"
#include "TEventAction.hh"
#include "TEventStack.hh"
#include "TReadoutList.hh"
#include "TRun.hh"

TEventManager* TEventManager::theEventManager = 0;

TEventManager::TEventManager( Tint stacksize )
  : theStatus( tStatusDead ),
    theStackSize( stacksize ), 
    theNumberOfEvents( 0 ), 
    theEventAction( 0 ), 
    theEvent(),
    //theEventTimer( Tmsec )
    theLastAccessID( -1 )
{
  if ( theEventManager ) {
    Tcerr << "TEventManager::TEventManager: EventManager constructed twice." << Tendl;
    exit( -EFAULT );
  }
  theEventManager = this;
  theStatus = tStatusReady;
}

TEventManager::~TEventManager()
{
  Tstring head = "TEventManager::~TEventManager: ";
  if ( theEventAction ) {
    delete theEventAction;
    theEventAction = 0;
    Tcout << head << "EventAction was deleted." << Tendl;
  }
  theStatus = tStatusDead;
  Tcout << head << "EventManager was deleted." << Tendl;
  theEventManager = 0;
}

const TEvent& TEventManager::TakeEvent()
{
  //theEventTimer.Start();

  theStatus = tStatusJustTakingEvent;

  theEvent.Clear();
  theEvent.SetEventID( theNumberOfEvents );
  if ( theEventAction ) {
    theEventAction -> AtFirst( theEvent );
    // タイムアウト処理をするべきかも?
    theEvent.SetDataRecord( theEventAction -> WaitTrigger() -> Read() );
    theEventAction -> AtLast( theEvent );
  }

  theNumberOfEvents ++;

  theStatus = tStatusReady;
  return theEvent;
}

const TRun& TEventManager::RecordEvent()
{
  theStatus = tStatusJustRecordingEvent;
  TRun& run = TRunManager::GetRunManager() -> GetRun();
  if ( theNumberOfEvents == 0 || theStackSize <= 0 ) {
    theStatus = tStatusReady;
    //theEventTimer.Stop();
    return run;
  }


  TEventStack& eventstack = run.GetEventStack();
  if ( !eventstack.empty() && eventstack[ eventstack.size() - 1 ].GetEventID() == theEvent.GetEventID() ) {
    theStatus = tStatusReady;
    //theEventTimer.Stop();
    return run;
  }


  if ( theNumberOfEvents > theStackSize ) {
    eventstack.erase( eventstack.begin() );
  }

  eventstack.push_back( theEvent );

  theStatus = tStatusReady;
  //theEventTimer.Stop();

  return run;
}

Tvoid TEventManager::ShowStatus() const
{
  switch ( theStatus ) {
    case tStatusReady:
      Tcout << "STATUS" << Tendl;
      Tcout << Ttab << "Ready(" << theStatus << ")" << Tendl;
      Tcout << "DESCRIPTION" << Tendl;
      Tcout << Ttab << "Just be preparing for next event or suspended the run." << Tendl;
      break;
    case tStatusJustTakingEvent:
      Tcout << "STATUS" << Tendl;
      Tcout << Ttab << "JustTakingEvent(" << theStatus << ")" << Tendl;
      Tcout << "DESCRIPTION" << Tendl;
      Tcout << Ttab << "This status is kept on during following procedure:" << Tendl;
      Tcout << Ttab << "  1. Clear the last event." << Tendl;
      Tcout << Ttab << "  2. Set the event ID." << Tendl;
      Tcout << Ttab << "  3. Execute AtFirst() method." << Tendl;
      Tcout << Ttab << "  4. Wait for trigger." << Tendl;
      Tcout << Ttab << "  5. Readout from modules according to the readout list." << Tendl;
      Tcout << Ttab << "  6. Execute AtLast() method." << Tendl;
      break;
    case tStatusJustRecordingEvent:
      Tcout << "STATUS" << Tendl;
      Tcout << Ttab << "JustRecordingEvent(" << theStatus << ")" << Tendl;
      Tcout << "DESCRIPTION" << Tendl;
      Tcout << Ttab << "Stacking the taken event record." << Tendl;
      break;
    case tStatusDead:
      Tcout << "STATUS" << Tendl;
      Tcout << Ttab << "Dead(" << theStatus << ")" << Tendl;
      Tcout << "DESCRIPTION" << Tendl;
      Tcout << Ttab << "Serious trouble." << Tendl;
      break;
    case tStatusUnknown:
    default:
      Tcout << "STATUS" << Tendl;
      Tcout << Ttab << "Unknown(" << theStatus << ")" << Tendl;
      Tcout << "DESCRIPTION" << Tendl;
      Tcout << Ttab << "Can not resolve status paramter." << Tendl;
      break;
  }

  Tcout << "STACK" << Tendl;
  Tcout << Ttab << TRunManager::GetRunManager()->GetRun().GetEventStack().size() << "/" << theStackSize << Tendl;

  Tcout << "NUMBER OF EVENTS" << Tendl;
  Tcout << Ttab << theNumberOfEvents << Tendl;

  Tcout << "ID" << Tendl;
  Tcout << Ttab << theEvent.GetEventID() << Tendl;

  //Tcout << "TIMER" << Tendl;
  //Tcout << Ttab << theEventTimer << Tendl;

  if ( theEventAction != 0 ) {
    Tcout << "ACTION" << Tendl;
    Tcout << Ttab << typeid( *theEventAction ).name() << Tendl;
  }

  return;
}

Tvoid TEventManager::SetStackSize( Tint stacksize )
{
  TRunManager* manager = TRunManager::GetRunManager();
  if ( manager -> GetStatus() == tStatusStandby ) {
    theStackSize = stacksize;
  } else {
    manager -> ShowStatus();
    Tstring head = "TEventManager::SetStackSize: ";
    Tcerr << head << "if you (re)set stacking size, you must stop a this run." << Tendl;
    Tcerr << head << "nothing to do, return." << Tendl;
  }
  return;
}

Tvoid TEventManager::SetEventAction( TEventAction* action )
{
  if ( theEventAction ) {
    Tstring head = "TEventManager::SetEventAction: ";
    delete theEventAction;
    Tcout << head << "EventAction was deleted." << Tendl;
    theEventAction = 0;
  }
  theEventAction = action;
  return;
}

Tvoid TEventManager::Clear()
{
  theNumberOfEvents = 0;
  theEvent.Clear();
  theLastAccessID = -1;
  return;
}

Tvoid TEventManager::ClearEventStack()
{
  TouchNewestEvent();
  TRunManager::GetRunManager()->GetRun().GetEventStack().clear();
  return;
}

Tint TEventManager::FindEvent( Tint id ) const
{
  const TEventStack& stack = TRunManager::GetRunManager()->GetRun().GetEventStack();
  Tint stacklen = (Tint)stack.size();
  for ( Tint i = 0; i < stacklen; i ++ ) {
    if ( stack[ i ].GetEventID() == id ) {
      return i;
    }
  }
  return -1;
}

Tbool TEventManager::HasEvent( Tint id ) const
{
  if ( FindEvent( id ) >= 0 ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

Tvoid TEventManager::TouchNewestEvent()
{
  GetNewestEvent();
  return;
}

TEvent TEventManager::GetNewestEvent()
{
  const TEventStack& stack = TRunManager::GetRunManager()->GetRun().GetEventStack();
  Tint stacklen = (Tint)stack.size();
  Tint maxid = -1;
  for ( Tint i = 0; i < stacklen; i ++ ) {
    if ( stack[ i ].GetEventID() > maxid ) {
      maxid = i;
    }
  }
  theLastAccessID = maxid;
  return stack[ maxid ];
}

TEvent TEventManager::GetNextEvent()
{
  Tint id = theLastAccessID + 1;
  return GetEvent( id );
}

TEvent TEventManager::GetLastEvent()
{
  Tint id = theLastAccessID;
  return GetEvent( id );
}

TEvent TEventManager::GetEvent( Tint id )
{
  TEvent evt( -1 );
  Tint pos = FindEvent( id );
  if ( pos >= 0 ) {
    const TEventStack& stack = TRunManager::GetRunManager()->GetRun().GetEventStack();
    evt = stack[ pos ];
    theLastAccessID = id;
  }
  return evt;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TEventManager)
#endif
