#include "BDSwicPeds.h"
#include "BDDevices.h"

#include <RawData/RawBeamMonBlock.h>
#include <RawData/RawBeamMonHeaderBlock.h>

#include <MessageService/MsgService.h>
CVSID("$Id: BDSwicPeds.cxx,v 1.10 2005/05/12 18:48:31 bv Exp $");


using namespace std;

// e12 protons, anything more and there is beam
const float maximum_toroid_intensity = 0.1; 
// units?, anything more and there is beam
const float maximum_bpm_intensity = 5.0;

// Determine if DAE and VME times are consistent:
// seconds, the worse case jitter in DAE timestamps
const float dae_jitter = 0.300;
// seconds, generous safety padding in addition to jitter
const float dae_vme_padding = 0.100;

BDSwicPeds::BDSwicPeds()
{
    this->Clear();
}

BDSwicPeds::BDSwicPeds(const SwicMap& map)
    : fSwicMap(map)
{
    this->Clear();
}

void BDSwicPeds::Clear()
{
    fBeginTS = VldTimeStamp::GetEOT(); // yes, EOT.
    fEndTS   = VldTimeStamp::GetBOT(); // yes, BOT

    size_t nswics = fSwicMap.size();
    if (!nswics) {
	vector<string> swics = BDDevices::SwicDevices();
	for (size_t ind=0; ind<swics.size(); ++ind) {
	    MSG("BD",Msg::kDebug)
		<< "BDSwicPeds::Clear: adding " << swics[ind] << endl;
	    fSwicMap[swics[ind]] = new UtilRunningAverageVector(96);
	}
	return;
    }
    SwicMap::iterator it, done = fSwicMap.end();
    for (it=fSwicMap.begin(); it != done; ++it) 
	it->second->Clear();
}

BDSwicPeds::~BDSwicPeds()
{
    SwicMap::iterator it, done = fSwicMap.end();
    for (it=fSwicMap.begin(); it != done; ++it) 
	delete it->second;
    fSwicMap.clear();
}

double total(const RawBeamData& rbd)
{
    int siz = rbd.GetDataLength();
    if (!siz) return 0;

    const double* data= rbd.GetData();
    double tot=0;
    for (int ind=0; ind<siz; ++ind) tot += data[ind];
    return tot;
}

bool BDSwicPeds::IsPedSpill(const RawBeamMonBlock& rbmb)
{
    // First find empty spills by requiring the toroids
    // to be existing and quiet
    const char* toroids[] = { "E:TR101D", "E:TOR101", "E:TRTGTD", "E:TORTGT", 0 };
    for (int ind=0; toroids[ind]; ++ind) {
	const RawBeamData *rbd = rbmb[toroids[ind]];
	if (!rbd) {
	    MSG("BD",Msg::kDebug)
		<< "SetSpill bailing because can't get toroid " << toroids[ind] << endl;
	    return false;
	}
	double tot = total(*rbd);
	if (tot > maximum_toroid_intensity) {
	    MSG("BD",Msg::kVerbose)
		<< "SetSpill bailing because toroid " << toroids[ind]
		<< " intensity too high: "
		<< tot << " > " << maximum_toroid_intensity << endl;
	    return false;
	}
    }

    // It's been found that some spills will have protons even with
    // quiet toroids!  So, require quiet BPMs as well.
    vector<string> bpm_intensity = BDDevices::BpmIntensities();
    for (size_t ind=0; ind<bpm_intensity.size(); ++ind) {
	const RawBeamData *rbd = rbmb[bpm_intensity[ind].c_str()];
	if (!rbd) continue;	// Don't just bail in case a BPM is simply removed
	double tot = total(*rbd);
	if (tot > maximum_bpm_intensity) {
	    MSG("BD",Msg::kDebug)
		<< "SetSpill bailing because bpm " << bpm_intensity[ind]
		<< " intensity too high: "
		<< tot << " > " << maximum_bpm_intensity << endl;
	    return false;
	}
    }

    // Go through SWIC devices and check if the VME and DAE times are
    // compatible.
    vector<string> swics = BDDevices::SwicDevices();
    RawBeamSwicData swic;
    double delay = rbmb.TclkTriggerDelay()/1.0e3;
    for (size_t ind=0; ind<swics.size(); ++ind) {
	const RawBeamData *rbd = rbmb[swics[ind].c_str()];
	if (!rbd) continue;
	swic.SetData(*rbd);
	double delta = rbd->GetSeconds() - swic.VmeSeconds() - delay;
	delta += rbd->GetMsecs()/1.0e3 - swic.VmeNanoseconds()/1.0e9;

	if (delta < -dae_vme_padding || delta > dae_jitter+dae_vme_padding ) {
	    MSG("BD",Msg::kWarning)
		<< "SetSpill bailing, SWIC " << swics[ind]
		<< " DAE-delay-VME time delta bad: "
		<< delta << endl;
	    return false;
	}

    }	

    return true;
}


void BDSwicPeds::SetSpill(const RawBeamMonHeaderBlock& rbmhb,
			  const RawBeamMonBlock& rbmb)
{
    VldTimeStamp vts = rbmhb.GetTimeStamp();
    if (vts < fBeginTS) fBeginTS = vts;
    if (vts > fEndTS) fEndTS = vts;

    if (!BDSwicPeds::IsPedSpill(rbmb)) return;

    // If we get here we are reasonably sure there are no protons in
    // the pipe.
    SwicMap::iterator it, done = fSwicMap.end();
    for (it=fSwicMap.begin(); it != done; ++it) {
	const RawBeamData* rbd = rbmb[it->first];
	if (!rbd) continue;
	
	RawBeamSwicData rbsd(*rbd);
	vector<int> idata;
	rbsd.UnscaledWireData(idata);
	vector<double> ddata;
	for (size_t ind=0; ind<idata.size(); ++ind)
	    ddata.push_back((double)idata[ind]);
	it->second->Add(ddata);
    }
}

const UtilRunningAverageVector* BDSwicPeds::GetChannels(const char* name) const
{
    SwicMap::const_iterator it = fSwicMap.find(name);
    if (it == fSwicMap.end()) return 0;
    return it->second;
}

const BDSwicPeds::SwicMap& BDSwicPeds::GetAllChannels() const
{
    return fSwicMap;
}
BDSwicPeds::SwicMap& BDSwicPeds::GetAllChannels()
{
    return fSwicMap;
}
