#include "BDUniquify.h"
#include "BDEarliest.h"

#include <RawData/RawBeamData.h>
#include <RawData/RawBeamMonBlock.h>
#include <RawData/RawBeamMonHeaderBlock.h>
#include <RawData/RawRecord.h>
#include <MinosObjectMap/MomNavigator.h>
#include <Validity/VldTimeStamp.h>

#include <TIterator.h>

#include <algorithm>
#include <list>
#include <cmath>
using namespace std;

BDUniquify::BDUniquify(size_t qsize)
    : fSize(qsize)
{
}

static size_t count_good(const BDUniquify::BlockPair &p)
{
    vector<string> names = p.second->GetNames();
    size_t siz = names.size();
    VldTimeStamp vts = (*p.first).GetTimeStamp();
    int bad = 0;
    for (size_t ind=0; ind<siz; ++ind) {
	const RawBeamData* rbd = (*p.second)[names[ind]];
	if (!rbd) continue;

	VldTimeStamp tmp(rbd->GetSeconds(),rbd->GetMsecs()*1000000);
	if (fabs(vts - tmp) > 0.5) ++bad;
    }
    return siz-bad;
}


bool block_pair_less(const BDUniquify::BlockPair& a,
		     const BDUniquify::BlockPair& b)
{


    size_t sa = count_good(a);
    size_t sb = count_good(b);

    if (sa == sb) 
	return a.first->GetSpillCountNum() > b.first->GetSpillCountNum();

    return sa > sb;
}

vector<BDUniquify::BlockPair> 
BDUniquify::GetUniqueBlocks(const MomNavigator &mom)
{
    static BDEarliest earliest;

    list<BDUniquify::BlockPair> res;

    // Loop through all RawRecords in mom:
    TObject* tobj=0;
    TIter fragiter = mom.FragmentIter();
    while( ( tobj = fragiter.Next() ) ) {
	const RawRecord* rawrec = dynamic_cast<const RawRecord*>(tobj);
	if (!rawrec) continue;

	BlockPair p;
	

	TIter blockiter = rawrec->GetRawBlockIter();
	while( ( tobj = blockiter.Next() ) ) {
	    if (!p.first)
		p.first = dynamic_cast<const RawBeamMonHeaderBlock*>(tobj);
	    if (!p.second)
		p.second = dynamic_cast<const RawBeamMonBlock*>(tobj);
	}

	if (!p.first || !p.second) continue;

	earliest.SetSpill(*p.first,*p.second);
	double dae=0,vme=0;
	earliest.GetTimestamps(dae, vme);
	if (dae<1 && vme<1) continue;

	res.push_back(p);
    }

    // Sort so any duplicates with more devices are added first
    res.sort(block_pair_less);
    
    vector<BDUniquify::BlockPair> ret;
    list<BDUniquify::BlockPair>::iterator it, done = res.end();
    for (it=res.begin(); it != done; ++it) {
	BDUniquify::BlockPair p = *it;
	VldTimeStamp vts = p.first->GetTimeStamp();
	int event = p.second->TclkTriggerEvent();
	int delay = p.second->TclkTriggerDelay();
	if (! this->IsUnique(vts, event, delay)) continue;

	this->AddTimeStamp(vts,event,delay);
	ret.push_back(p);
    }
    
    return ret;

}

void BDUniquify::AddTimeStamp(const VldTimeStamp& vts, int event, int delay)
{
    int key = event<<24 + delay;
    deque<VldTimeStamp>& vtsl = fPastSpills[key];
    vtsl.push_front(vts);
    while (vtsl.size() > fSize) vtsl.pop_back();
}

bool BDUniquify::IsUnique(const VldTimeStamp& vts, int event, int delay) 
{
    int key = event<<24 + delay;
    map<int,deque<VldTimeStamp> >::iterator it = fPastSpills.find(key);

    if (it == fPastSpills.end()) return true;

    deque<VldTimeStamp> &vtsl = it->second;

    size_t siz = vtsl.size();
    for (size_t ind=0; ind<siz; ++ind) {
	if (fabs(vtsl[ind]-vts) < 0.5) return false;
    }

    return true;
}
