#include "BDSwicPedAccessor.h"
#include "BDDevices.h"
#include <Util/UtilBlockedRunningAverageVector.h>

#include <MessageService/MsgService.h>
CVSID("$Id: BDSwicPedAccessor.cxx,v 1.9 2006/05/27 07:31:45 rhatcher Exp $");


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

#include <string>
#include <vector>
using namespace std;
#include <cmath>

static const float max_profile_ped = 50.0; // ADC


static bool is_profile_monitor(const char* name)
{
    static const vector<string> pm_names =  BDDevices::ProfileMonitors();
    for (size_t ind=0; ind<pm_names.size(); ++ind) {
	if (pm_names[ind] == name) return true;
    }
    return false;
}

static void mask_high_peds(vector<double> &ped)
{
    for (int ind=0; ind<96; ++ind)
	if (fabs(ped[ind]) > max_profile_ped)
	    ped[ind] = 0.0;
}


BDSwicPedAccessor::BDSwicPedAccessor()
    : fDRP(new DbiResultPtr<BeamMonSwicPeds>)
{
}

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

void BDSwicPedAccessor::AddDevice(const char* device_name,
				  int minimum_sample_size)
{
    if (fSwicMap.find(device_name) != fSwicMap.end()) return;

    int det = Detector::kNear | Detector::kFar;
    int sf = SimFlag::kData;

    Device dev;
    dev.channels = new UtilBlockedRunningAverageVector(96,minimum_sample_size);
    dev.range = VldRange(det,sf,VldTimeStamp::GetEOT(),VldTimeStamp::GetBOT(),
			 "swicpeds");
    dev.is_profile = is_profile_monitor(device_name);
    fSwicMap[device_name] = dev;
}

bool BDSwicPedAccessor::SetSpillTime(VldContext vc)
{
    //cerr << "SetSpillTime with vc=" << vc << endl;


    // First find out if we need to bother
    bool finished = true;
    SwicMap::iterator it, done = fSwicMap.end();
    for (it=fSwicMap.begin(); it != done; ++it) {
	//cerr << it->first << ": vr=" << it->second.range << endl;

	if (! it->second.range.IsCompatible(vc)) {
	    //cerr << "Incompatible, vc=" << vc << endl
	    //<< "vr=" << it->second.range << endl;
	    finished = false;
	    break;
	}
    }
    if (finished) return false;

    // Got to hit the DB.  Maybe there is a smarter way to do this but
    // for now just clear everything and start fresh.
    for (it=fSwicMap.begin(); it != done; ++it) {
	if (!it->second.channels) {
	    MSG("BD",Msg::kWarning) << "No device channels for " << it->first << endl;
	    continue;
	}
	it->second.channels->Clear();
	it->second.range.SetTimeStart(VldTimeStamp::GetEOT());
	it->second.range.SetTimeEnd(VldTimeStamp::GetBOT());
    }

    int nrows = fDRP->NewQuery(vc,0,true);
    int n_empty_queries = 0;
    for (finished = false; !finished; nrows = fDRP->NextQuery(false)) {

	if (nrows)
	    n_empty_queries = 0;
	else 
	    ++n_empty_queries;

	if (n_empty_queries > 2) {
	    MSG("BD",Msg::kWarning) << "Too many empty queries, giving up\n";
	    return false;
	}

	const DbiValidityRec* vr = fDRP->GetValidityRec();
	//cerr << "Next query " << n_empty_queries << " with "
	//<< nrows << " rows, seqno="
	//<< vr->GetSeqNo() << endl
	//<< vr->GetVldRange() << endl;

	if (!nrows) {		// hit a gap.
	    SwicMap::iterator it, done = fSwicMap.end();
	    for (it=fSwicMap.begin(); it != done; ++it) {
		Device& dev = it->second;
		if (dev.range.GetTimeEnd() < vr->GetVldRange().GetTimeEnd())
		    dev.range.SetTimeEnd(vr->GetVldRange().GetTimeEnd());
		if (dev.range.GetTimeStart() > vr->GetVldRange().GetTimeStart())
		    dev.range.SetTimeStart(vr->GetVldRange().GetTimeStart());
	    }
	    continue;
	}

	finished = true;

	for (int row = 0; row<nrows; ++row) {
	    const BeamMonSwicPeds* peds = fDRP->GetRow(row);
	    string name = peds->GetDeviceName();
	    SwicMap::iterator it = fSwicMap.find(name);
	    if (it == fSwicMap.end()) {
		MSG("BD",Msg::kDebug) << "No Device for " << name << ", continuing\n";
		continue;
	    }
	    Device& dev = it->second;

	    if (!dev.channels) {
		MSG("BD",Msg::kWarning) << "No Device for " << name << endl;
		continue;
	    }

	    // don't need any more samples
	    if (dev.channels->IsSampleFull()) {
		MSG("BD",Msg::kDebug) << "Sample not yet full for " << name << ", continuing\n";
		continue;
	    }

	    // enlarge vld range to include this
	    if (dev.range.GetTimeEnd() < vr->GetVldRange().GetTimeEnd())
		dev.range.SetTimeEnd(vr->GetVldRange().GetTimeEnd());
	    if (dev.range.GetTimeStart() > vr->GetVldRange().GetTimeStart())
		dev.range.SetTimeStart(vr->GetVldRange().GetTimeStart());
	    
	    vector<double> means = peds->GetMeansAsDoubles();
	    if (dev.is_profile) mask_high_peds(means);
	    vector<double> sigmas = peds->GetSigmasAsDoubles();
	    dev.channels->Add(means,sigmas,peds->GetNsamples());

	    if (!dev.channels->IsSampleFull()) finished = false;
	}
    }

    return true;
}
int BDSwicPedAccessor::GetPeds(const char* device_name,
			       vector<double> &mean,
			       vector<double> &sigma) const
{
    SwicMap::const_iterator it = fSwicMap.find(device_name);
    if (it == fSwicMap.end()) {
	mean = vector<double>(96,0);
	sigma = vector<double>(96,0);
	return 0;
    }
    mean = it->second.channels->PopulationMean();
    sigma = it->second.channels->PopulationSigma();
    return it->second.channels->GetPopulationSize();
}
int BDSwicPedAccessor::GetPeds(const char* device_name,
			       vector<double> &mean) const
{
    SwicMap::const_iterator it = fSwicMap.find(device_name);
    if (it == fSwicMap.end()) {
	mean = vector<double>(96,0);
	return 0;
    }
    mean = it->second.channels->PopulationMean();
    return it->second.channels->GetPopulationSize();
}
