#include "BDSpillAccessor.h"

#include <Validity/VldContext.h>

#include <MessageService/MsgService.h>
CVSID("$Id: BDSpillAccessor.cxx,v 1.24 2009/01/06 22:01:46 loiacono Exp $");

#include <DatabaseInterface/DbiResultPtr.tpl>
template class  DbiResultPtr<BeamMonSpill>;

#include <cmath>
using namespace std;

BDSpillAccessor* BDSpillAccessor::fInstance = 0;

BDSpillAccessor& BDSpillAccessor::Get()
{
    if (!fInstance) fInstance = new BDSpillAccessor();
    return *fInstance;
}


BDSpillAccessor::BDSpillAccessor()
    : fPrevPtr(0)
    , fCenterPtr(new DbiResultPtr<BeamMonSpill>)
    , fNextPtr(0)
    , fNeedNext(true)
    , fNeedPrev(true)
{
}
BDSpillAccessor::~BDSpillAccessor()
{
    if (fPrevPtr)   delete fPrevPtr;
    if (fCenterPtr) delete fCenterPtr;
    if (fNextPtr)   delete fNextPtr;
    fPrevPtr = fCenterPtr = fNextPtr = 0;
}

const BeamMonSpill* BDSpillAccessor::LoadSpill(VldTimeStamp vts, int which)
{
    const BeamMonSpill *before=0, *after=0;
    this->SeekClosest(vts,before,after);

    // Apply corrections and calibrations
    //cout << "before: "  << before->fTortgt << endl;
    if (before) this->CalibrateSpill(before);
    //cout << "after: "  << before->fTortgt << endl;
    if (after) this->CalibrateSpill(after);
    
    if (which < 0) return before;
    if (which > 0) return after;

    if (!before) return after;
    if (!after) return before;

    VldTimeStamp vts_before = before->SpillTime();
    VldTimeStamp vts_after = after->SpillTime();

    MSG("BDU",Msg::kDebug)
       << vts_before << " " << vts << " " << vts_after << endl;

    if (vts - vts_before < vts_after - vts)
	return before;
    return after;

}


void BDSpillAccessor::SeekClosest(const VldTimeStamp &vts,
				  const BeamMonSpill* &before,
				  const BeamMonSpill* &after)
{
    Detector::Detector_t det = Detector::kNear;
    SimFlag::SimFlag_t simflag = SimFlag::kData;
    VldContext vc(det,simflag,vts);

    fCenterPtr->NewQuery(vc,0,true);

    if (! fResKey.IsEqualTo(fCenterPtr->GetKey()))
    {
       fNeedPrev = fNeedNext = true;
    }
    
    const DbiValidityRec* vr = fCenterPtr->GetValidityRec();

    VldTimeStamp beg = vr->GetVldRange().GetTimeStart();
    VldTimeStamp end = vr->GetVldRange().GetTimeEnd();

    before=after=0;
    this->TableSearch(*fCenterPtr,vts,before,after);

    if (before && after) return;

    MSG("BDU",Msg::kDebug)
	<< vts << " not in center table, before @ " << (void*)before
	<< " after @ " << (void*)after 
	<< " seqno = " << vr->GetSeqNo() << endl;

    if (!before) {
       Detector::Detector_t det = Detector::kNear;
       SimFlag::SimFlag_t simflag = SimFlag::kData;
       VldContext vc(det,simflag,beg);
       if(!fPrevPtr || !fNeedPrev)
       {
          fPrevPtr = new DbiResultPtr<BeamMonSpill>();
       }
       fPrevPtr->NewQuery(vc,0,true);
              
       fNeedPrev = false;
       
       const BeamMonSpill* dummy=0;
       this->TableSearch(*fPrevPtr,vts,before,dummy);
       if(dummy)
       {
          MSG("BDU",Msg::kError)
             << "Found next time in previous table: "
             << dummy->SpillTime() << endl;
       }
       
    }
    if (!after){
       Detector::Detector_t det = Detector::kNear;
       SimFlag::SimFlag_t simflag = SimFlag::kData;
       VldContext vc(det,simflag,end);
       if(!fNextPtr || !fNeedNext)
       {
          fNextPtr = new DbiResultPtr<BeamMonSpill>();
       }
       fNextPtr->NewQuery(vc,0,true);
              
       fNeedNext = false;
       
       const BeamMonSpill* dummy=0;
       this->TableSearch(*fNextPtr,vts,dummy,after);
	if(dummy)
        {
           MSG("BDU",Msg::kError)
              << "Found previous time in next table: "
              << dummy->SpillTime() << endl;
        }
    }
    
}

void BDSpillAccessor::TableSearch(DbiResultPtr<BeamMonSpill> &table,
				  const VldTimeStamp& vts, 
				  const BeamMonSpill* &before,
				  const BeamMonSpill* &after)
{
    before = after = 0;

    int nrows = table.GetNumRows();
    if (nrows <= 0) return;

    double minneg = 1e10;
    double minpos = 1e10;
    for (int row = 0; row < nrows; ++row) {
	const BeamMonSpill* spill = table.GetRow(row);
	double dt = spill->SpillTime() - vts;
	if (dt <= 0) {
	    if (-dt < minneg) {
		minneg = -dt;
		before = spill;
	    }
	}
	else {
	    if (dt < minpos) {
		minpos = dt;
		after = spill;
	    }
	}
    }
}

void BDSpillAccessor::CalibrateSpill(const BeamMonSpill* spill)
{
    // Apply corrections to the data pulled out of the batabase
    //
    // Apply corrections to tortgt
    // From Mary:
    // (Corrected for the new values from Sasha after he realised
    // that he had distributed the wrong numbers - 2005-11-16)
    //
    // Recommend only using TORTGT. There were several iterations of changes
    // applied to TOR101 in early June. It was recalibrated again on July 22
    // (-4.0%). TORTGT was changed only once - on July 26th.
    //
    // TORTGT calibrations (same calibrations apply to TRTGTD (20080313)):
    // --------------------
    // Before July 26th 2:17:41 GMT Doug Jensens calibrations indicate a
    // correction of -4.0% to the ACNET values of TORTGT.
    //
    // The period July 26th 2:17:41 GMT to August 1st 22:43:23 GMT is when the
    // Beam data process was applying the wrong ACNET hardware calibration. The
    // BDP correction is -12.6%. There is a further correction of -0.0 % based on
    // Doug Jensens studies = -12.6% total
    //
    // After August 1st 22:43:23 the only correction needed is Doug Jensen's
    // -0.0%.
    //
    BeamMonSpill* spill_cor = const_cast<BeamMonSpill*>(spill);

    // Avoid double calibration
    if (spill_cor->fStatus.bits.calibrated) return;
    spill_cor->fStatus.bits.calibrated = 1;

    int timesec = spill_cor->SpillTime().GetSec();
    double tortgt_cor = 1.0;
    double trtgtd_cor = 1.0;
    //  Before July 26th, 2005  2:17:41 UTC
    if (timesec < 1122344261) 
    {
      tortgt_cor = 0.96;
      trtgtd_cor = 0.96;
    }
    // After previous date but before Aug 1st 2005 22:43:23
    else if (timesec < 1122936203)
    { 
      tortgt_cor = 0.874;
      trtgtd_cor = 0.874;
    }
    // after previous date
    else 
    {
      tortgt_cor = 1.0;
      trtgtd_cor = 1.0;
    }
    //
    spill_cor->fTortgt *= tortgt_cor;
    spill_cor->fTrtgtd *= trtgtd_cor;
    //
    // Horn Current Calibrations
    // -------------------------
    // From Jim Hylen (2005-10-28):
    //   A -1.6% change applied to total horn current would be fine -
    //   the individual offsets etc. are pretty minor.
    //   So far there is no known time dependence.
    //
    // There was a miscommunication about the direction of the change,
    // the total horn current should be larger by 1.8%.  
    //
    double horcur_cor = 0.982;
    spill_cor->fHornCur /= horcur_cor;
}    
