/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!   Author(s): Javier López , 7/2004 <mailto:jlopez@ifae.es>
!
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
//  MLiveTimeCalc
//
/////////////////////////////////////////////////////////////////////////////
#include "MLiveTimeCalc.h"

#include "MRawRunHeader.h"
#include "MRawEvtHeader.h"
#include "MLiveTime.h"

#include "MLog.h"
#include "MLogManip.h"

#include "MParList.h"
#include "MTaskList.h"

ClassImp(MLiveTimeCalc)
using namespace std;

Bool_t Debug = kFALSE;
Bool_t PrintNewRun = kTRUE;

MLiveTimeCalc::MLiveTimeCalc(const char *name, const char *title) : kSecTomSec(1e3), kDayToSec(24.*60.*60.), fRunHeader(NULL), fEvtHeader(NULL), fPresentEventTime(NULL), fLiveTime(NULL)
{

  fName  = name  ? name  : "MLiveTimeCalc";
  fTitle = title ? title : "Task to compute the live time from real data.";

  for (Int_t i=0; i<10; i++)
    fNumberLostEvents[i] = 0;
  
  fFirstEventMjd = 0.;
  fPresentEventMjd = 0.;
  fLastEventMjd = 0.;

  fPresentEventNumber = 0;
  fLastEventNumber = 0;

  fPresentEventRunNumber = 0;
  fLastEventRunNumber = 0;

  fRealTimeBinSize = 0.;

  fPrintNextEvent = kFALSE;
}

Int_t MLiveTimeCalc::PreProcess(MParList *pList)
{
  
  fEvtHeader = (MRawEvtHeader*)pList->FindObject(AddSerialNumber("MRawEvtHeader"));
  if (!fEvtHeader)
    {
      *fLog << err << AddSerialNumber("MRawEvtHeader") << " not found ... aborting" << endl;
      return kFALSE;
    }
  
  fRunHeader = (MRawRunHeader*)pList->FindObject(AddSerialNumber("MRawRunHeader"));
  if (!fRunHeader)
    {
      *fLog << err << AddSerialNumber("MRawRunHeader") << " not found ... aborting" << endl;
      return kFALSE;
    }
  
  fPresentEventTime = (MTime*)pList->FindObject(AddSerialNumber("MTime"));
  if (!fPresentEventTime)
    {
      *fLog << err << AddSerialNumber("MTime") << " not found ... aborting" << endl;
      return kFALSE;
    }
  
  
  fLiveTime = (MLiveTime*)pList->FindCreateObj(AddSerialNumber("MLiveTime"));
  if (!fLiveTime)
    {
      *fLog << err << AddSerialNumber("MLiveTime") << " cannot be created ... aborting" << endl;
      return kFALSE;
    }
  
  return kTRUE;
}

Int_t MLiveTimeCalc::Process()
{

  fPresentEventMjd       = fPresentEventTime->GetMjd(); 
  fLastEventMjd          = fLastEventTime.GetMjd();
  fPreaviusLastEventMjd  = fPreaviusLastEventTime.GetMjd();

  fPresentEventNumber    = fEvtHeader->GetDAQEvtNumber();
  fPresentEventRunNumber = fRunHeader->GetRunNumber();;
  
  if (fPrintNextEvent && Debug)
  {
      *fLog << dbg << GetName() << ": Printing next event" << endl;
      Print("all");
      fLiveTime->Print("last");
      *fLog << inf << endl; 
      fPrintNextEvent = kFALSE;
  }
	  
  if (fFirstEventMjd == 0)
  {
      fFirstEventMjd = fPresentEventMjd;

      fPreaviusLastEventTime = fLastEventTime;
      fLastEventTime = *fPresentEventTime;

      fPreaviusLastEventRunNumber = fLastEventRunNumber;
      fLastEventRunNumber = fPresentEventRunNumber;

      fPreaviusLastEventNumber = fLastEventNumber;
      fLastEventNumber = fPresentEventNumber;
  }
  else
  {
      if (isTimeStampOk())
      {
	  if (fRealTimeBinSize > 0 &&
	      (fPresentEventMjd - fFirstEventMjd)*kDayToSec > fRealTimeBinSize)
	  {

	      Double_t width = (fLastEventMjd - fFirstEventMjd)/2;
	      Double_t mean  = fFirstEventMjd + width;
	      
	      fLiveTime->SetRealTime(mean,width);
	      
	      if (Debug)
		{
		  *fLog << inf << GetName() << ": New time bin" << endl;
		  Print("all");
		  *fLog << GetName() << ": First event time " << setprecision(10) << fFirstEventMjd << setprecision(5) << endl;
		  fLiveTime->Print("last");
		}
	      
	      fLiveTime->AddBin();
	      fFirstEventMjd = fPresentEventMjd;
	  }      

	  if (fPresentEventRunNumber!=fLastEventRunNumber)
	  {
	      if (fLastEventRunNumber != 0 && PrintNewRun)
	      {
		  *fLog << dbg << GetName() << ": New run" << endl;
		  Print("all");
		  fLiveTime->Print("last");
		  *fLog << inf << endl;
	      }
	      fLastEventRunNumber = fPresentEventRunNumber;
	  }
	  else
	      fLiveTime->AddTime((fPresentEventMjd-fLastEventMjd)*kDayToSec);
	  

      
      fPreaviusLastEventTime = fLastEventTime;
      fLastEventTime = *fPresentEventTime;

      fPreaviusLastEventRunNumber = fLastEventRunNumber;
      fLastEventRunNumber = fPresentEventRunNumber;

      fPreaviusLastEventNumber = fLastEventNumber;      
      fLastEventNumber = fPresentEventNumber;

      }
  }

  return kTRUE;
}
  
Int_t MLiveTimeCalc::PostProcess()
{
  Double_t width = (fLastEventMjd - fFirstEventMjd)/2;
  Double_t mean  = fFirstEventMjd + width;
 
  fLiveTime->SetRealTime(mean,width);

  *fLog << dbg << endl;
  *fLog << dbg << GetName() << ": PostProcess" << endl;
  fLiveTime->Print("all");
  *fLog << inf << endl;

  *fLog << GetName() << " execution statistics:" << endl;
  *fLog << dec << setfill(' ');

  ULong_t fTotalNumberLostEvents = 0;
  for (Int_t i=0; i<6; i++)
    fTotalNumberLostEvents += fNumberLostEvents[i];

  *fLog << " " << setw(7) << fTotalNumberLostEvents << " (" << setw(3) ;
  *fLog << (Int_t)(fTotalNumberLostEvents*100/GetNumExecutions()) ;
  *fLog << "%) bad time stamp events" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[0]*100/fTotalNumberLostEvents) ;
  *fLog << "%) time stamp == 0" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[1]*100/fTotalNumberLostEvents) ;
  *fLog << "%) last time stamp  == 0" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[2]*100/fTotalNumberLostEvents) ;
  *fLog << "%) time stamp in the past" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[3]*100/fTotalNumberLostEvents) ;
  *fLog << "%) time stamp == last one" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[4]*100/fTotalNumberLostEvents) ;
  *fLog << "%) time stamp just with integer part" << endl;

  *fLog << "\t\t(" << setw(3) << (Int_t)(fNumberLostEvents[5]*100/fTotalNumberLostEvents) ;
  *fLog << "%) run number < last one" << endl;

  return kTRUE;
}

Bool_t MLiveTimeCalc::isTimeStampOk()
{

  Bool_t result = kTRUE;
  

  if (fPresentEventMjd == 0)
    {
	
      if (Debug)
	{
	    *fLog << err << GetName() << ": Present event time stamp equal to 0" << endl;
	    Print("all");
	}

      fNumberLostEvents[0]++;
      result = kFALSE;
      fPrintNextEvent = kTRUE;
    }
  else if (fLastEventMjd == 0)
    {
	
      if (Debug)
	{
	    *fLog << err << GetName() << ": Last event time stamp equal to 0" << endl;
	    Print("all");
	}

      fNumberLostEvents[1]++;
      result = kFALSE;
      fPrintNextEvent = kTRUE;
    }
  else if (fPresentEventMjd-fLastEventMjd < 0)
    {
      
      if (Debug)
	{
	    *fLog << err << GetName() << ": Present event with time stamp in the past" << endl;
	    Print("all");
	}
	  
      fNumberLostEvents[2]++;
      result = kFALSE;
      fPrintNextEvent = kTRUE;
    }
  else if (fPresentEventMjd-fLastEventMjd == 0)
    {
      
      if (Debug)
	{
	    *fLog << err << GetName() << ": Present event time stamp equal than last event" << endl;
	    Print("all");
	}
      
      fNumberLostEvents[3]++;
      result = kFALSE;
      fPrintNextEvent = kTRUE;
    }
  else if (fPresentEventNumber- fLastEventNumber<= 0 && fPresentEventRunNumber == fLastEventRunNumber)
    {
      
      if (Debug)
	{
	    *fLog << warn << GetName() << ": Present event number equal or smaller than last event" << endl;
	  Print("all");
	}

      result = kTRUE;
      fPrintNextEvent = kTRUE;
    }
  else if ((Int_t)fPresentEventMjd == fPresentEventMjd)
    {
      
      if (Debug)
	{
	    *fLog << err << GetName() << ": Present event time stamp idetical to midnight" << endl;
	    Print("all");
	}
      
      fNumberLostEvents[4]++;
      result = kFALSE;
      fPrintNextEvent = kTRUE;
    }
  else if  ((fPresentEventMjd-fLastEventMjd)*kDayToSec > 1.)
  {
      
      if (Debug)
      {
	  *fLog << warn << GetName() << ": Time from last event bigger than 1 sec" << endl;
	  Print("all");
      }
      
      result = kTRUE;
      fPrintNextEvent = kTRUE;
  }
  else if  (fPresentEventRunNumber-fLastEventRunNumber < 0)
  {
      
      if (Debug)
      {
	  *fLog << warn << GetName() << ": Present run number previuos than last one" << endl;
	  Print("all");
      }
      
      fNumberLostEvents[5]++;
      result = kTRUE;
      fPrintNextEvent = kTRUE;
  }

  return result;
}

void MLiveTimeCalc::Print(const Option_t *o) const
{

  *fLog << "Present event run number       ["  << fPresentEventRunNumber << "] event number [" << fPresentEventNumber << ']' << endl;
  *fLog << "Last event run number          ["  << fLastEventRunNumber << "] event number [" << fLastEventNumber << ']' << endl;
  *fLog << "Preavius last event run number ["  << fPreaviusLastEventRunNumber << "] event number [" << fPreaviusLastEventNumber << ']' << endl;
  *fLog << "Present, last and preavius to last event times:"  << endl;
  fPresentEventTime->Print();
  fLastEventTime.Print();
  fPreaviusLastEventTime.Print();
}
