#include "hades.h"
#include "hcategory.h"
#include "hdebug.h"
#include "hevent.h"
#include "heventheader.h"
#include "hldsource.h"
#include "hldsubevt.h"
#include "hruntimedb.h"
#include "hspectrometer.h"
#include "htofdetector.h"
#include "htofraw.h"
#include "htofrawsim.h"
#include "htoftrb3lookup.h"
#include "htoftrb3unpacker.h"
#include "tofdef.h"
#include "htrbnetunpacker.h"
#include <iostream>
using namespace std;
ClassImp(HTofTrb3Unpacker)
#define TOF_TIME_MIN -300.0
#define TOF_TIME_MAX  1500.0
Bool_t HTofTrb3Unpacker::fHasPrintedTDC = kFALSE;
HTofTrb3Unpacker::HTofTrb3Unpacker(vector<UInt_t>& ids) : HTrb3TdcUnpacker(ids),
    fLookup(0), fTimeRef(kTRUE), fTimeShift(0.) {
	
	pRawCat = NULL;
}
Bool_t HTofTrb3Unpacker::init(void) {
    
    HTofDetector* det = (HTofDetector*) gHades->getSetup()->getDetector("Tof");
    if (!det) {
        Error("init", "No TOF Detector found.");
        return kFALSE;
    }
    if(gHades->getEmbeddingMode()==0) {
	pRawCat=gHades->getSetup()->getDetector("Tof")->buildCategory(catTofRaw);
    } else {
	pRawCat=((HTofDetector*)(gHades->getSetup()->getDetector("Tof")))->buildMatrixCategory("HTofRawSim",0.5F);
    }
    if (!pRawCat) return kFALSE;
    gHades->getCurrentEvent ()->addCategory (catTofRaw, pRawCat, "Tof");
    fLoc.set(3, 0, 0, 0);
    fLookup = (HTofTrb3Lookup*) (gHades->getRuntimeDb()->getContainer("TofTrb3Lookup"));
    if (!fLookup) {
        Error("init", "No Pointer to parameter container TofTrb3Lookup.");
        return kFALSE;
    }
    if (NULL == trbNetUnpacker) {
        if (gHades->getDataSource()) {
            HDataSource* source = gHades->getDataSource();
            if (source->InheritsFrom("HldSource")) {
                trbNetUnpacker =
                        ((HldSource *) gHades->getDataSource())->getTrbNetUnpacker();
            } else {
                Warning("init",
                        "DataSource not inherited from HldSource! trbNetUnpacker == 0 ");
            }
        } else {
            Warning("init",
                    "Could not retrieve DataSource! trbNetUnpacker == 0 ");
        }
    }
    if (!trbNetUnpacker->init()) {
        Error("init()", "Failed to initialize HTrbNetUnpacker!");
        return kFALSE;
    }
    return kTRUE;
}
Bool_t HTofTrb3Unpacker::reinit(void)
{
    
    
    
    
    if (numTDC() == 0 )  
    {
	if (fMinAddress == 0 && fMaxAddress == 0)
	{
	    
	    Int_t numTDC = fLookup->getSize();
	    Int_t offset = fLookup->getArrayOffset() ;
	    for (Int_t slot = 0; slot < numTDC; ++slot) {
		Int_t trbnetaddress = offset+slot;
		HTofTrb3LookupBoard* tdc = fLookup->getBoard(trbnetaddress);
		if(tdc){
		    Int_t nChan = tdc->getSize();
		    if (trbnetaddress) {
			Int_t tindex = addTDC(trbnetaddress,nChan);
			Info("reinit", "Added TDC 0x%04x with %d channels from HTofTrb3Lookup to map index %d",
			     trbnetaddress, nChan,tindex);
		    }
		}
	    }
	    
	    setMinAddress(fLookup->getArrayOffset());
	    setMaxAddress(fLookup->getArrayOffset() + fLookup->getSize());
	    fUseTDCFromLookup = kTRUE; 
	    fHasPrintedTDC = kTRUE;
	} else {
            Info("reinit", "TDCs will be added in auto register mode between min address 0x%04x and max address 0x%04x!",fMinAddress,fMaxAddress);
	}
    }
    
    return kTRUE;
}
Int_t HTofTrb3Unpacker::execute(void) {
    if (gHades->isCalibration()) {
        
        return 1;
    }
    if (gHades->getCurrentEvent()->getHeader()->getId() == 0xe) {
        
        return 1;
    }
    
    
    if (!pSubEvt)
        return 1;
    Int_t nEvt = gHades->getCurrentEvent()->getHeader()->getEventSeqNumber();
    
    if (!decode()) {
        Error("decode",
                "subsubevent decoding failed!!! Evt Nr : %i SubEvtId: %x", nEvt,
                getSubEvtId());
        return -1;
    }
    
    
    
    
    if (fTimeRef) {
        correctRefTimeCh(REFCHAN);
    }
    const Double_t timeUnit=1e9;
    for (UInt_t ntdc = 0; ntdc < numActiveTDC(); ntdc++) {
        HTrb3TdcUnpacker::TDC* tdc = getActiveTDC(ntdc);
        
        HTofTrb3LookupBoard *board = fLookup->getBoard(tdc->getTrbAddr());
        if (!board) {
            if (debugFlag > 0)
                Warning("execute",
                        "Evt Nr : %i  SubEvId: %x (%i) unpacked but TDC Board 0x%x not in lookup table",
                        nEvt, getSubEvtId(), getSubEvtId(), tdc->getTrbAddr());
            continue;
        }
	
        for (UInt_t i = 0; i < tdc->numChannels(); i++) {
            if(REFCHAN == i) continue;
	    HTrb3TdcUnpacker::ChannelRec& theRecord = tdc->getCh(i);
#ifndef TOF_USE_TRAILING_WITHOUT_LEADING_EDGES
            if (theRecord.rising_mult < 1)
                continue;
#endif
            HTofTrb3LookupChan *chan = board->getChannel(i);
            if (chan == 0) {
                Warning("execute", "Missing channel %u in lookup table", i);
                continue;
            }
            chan->getAddress(fLoc[0], fLoc[1], fLoc[2]);
	    
	    if (fLoc[0] < 0)  { 
		continue;
	    }
            
            
#ifdef TOF_USE_TIME_WINDOW
            
            Double_t tmin = TOF_TIME_MIN;
            Double_t tmax = TOF_TIME_MAX;
#endif
            UInt_t lix = 0;
            for (lix = 0; lix < theRecord.rising_mult; ++lix) {
                Double_t tm0 = theRecord.rising_tm[lix] * timeUnit;
                if (debugFlag > 0)
                    Warning("execute", "JJJJ current hit leading: tm0:%e, apply time shift:%e", tm0, fTimeShift);
                tm0 +=fTimeShift;
#ifdef TOF_USE_TIME_WINDOW
                if ((tm0 < tmin) || (tm0 > tmax)) {
                    if (debugFlag > 0)
                        Warning("execute",
                                "JJJJ Rejecting leading hit outside tmin:%e or tmax:%e",
                                tmin, tmax);
                    continue; 
                }
#endif
                
                if (lix < theRecord.falling_mult) {
                    Double_t tm1 = theRecord.falling_tm[lix] * timeUnit;
                    if (debugFlag > 0)
                        Warning("execute", "JJJJ current hit falling: tm1:%e, apply time shift:%e",
                                tm1, fTimeShift);
                    tm1 +=fTimeShift;
#ifdef TOF_USE_TIME_WINDOW
                    if ((tm1 < tmin) || (tm1 > tmax)) {
                        if (debugFlag > 0)
                            Warning("execute",
                                    "JJJJ Rejecting trailing hit outside tmin:%e or tmax:%e",
                                    tmin, tmax);
#ifdef TOF_USE_LEADING_WITHOUT_TRAILING_EDGES
                        tm1=0.0; 
#else
                        continue; 
#endif
                    } 
#endif
                    
                    
                    
                    
                    
                    if (addRawHit(tm0, tm1, chan) != 0)
                        continue; 
                } 
                else {
#ifdef TOF_USE_LEADING_WITHOUT_TRAILING_EDGES
                    
                    
                    if(addRawHit(tm0, 0.0, chan)!=0)
                    continue;
#endif
                }
            } 
#ifdef TOF_USE_TRAILING_WITHOUT_LEADING_EDGES
            for (UInt_t tix=lix; tix< theRecord.falling_mult; ++tix)
            {
                Double_t tm1=theRecord.falling_tm[tix] * timeUnit;
#ifdef TOF_USE_TIME_WINDOW
                    if ((tm1 < tmin) || (tm1 > tmax)) {
                        if (debugFlag > 0)
                            Warning("execute",
                                    "JJJJ Rejecting trailing hit outside tmin:%e or tmax:%e",
                                    tmin, tmax);
			continue;
#endif
                if(addRawHit(0.0, tm1, chan)!=0)
		    continue;
            }
             
#endif
        } 
    } 
    return 1;
}
Int_t HTofTrb3Unpacker::addRawHit(Double_t t_leading, Double_t t_trailing,HTofTrb3LookupChan *chan)
{
    
    HTofRaw* raw = (HTofRaw*) pRawCat->getObject(fLoc);
    if (!raw) {
	raw = (HTofRaw *) pRawCat->getSlot(fLoc);
	if(gHades->getEmbeddingMode()==0) {
	    raw = new (raw) HTofRaw;
	} else{
	    raw=new (raw) HTofRawSim;
	}
	if(raw){
	    raw->setSector((Char_t)fLoc[0]);
	    raw->setModule((Char_t)fLoc[1]);
	    raw->setCell((Char_t)fLoc[2]);
	} else {
	    if (debugFlag > 0)
		Warning("addRawHit()", "Can't get slot sec=%i, column=%i, cell=%i",
			fLoc[0], fLoc[1],fLoc[2]);
	    return -1;
	}
    }
    Char_t side     = chan->getSide();
    if(side=='r') {
	raw->setRightTime(t_leading);
	raw->setRightCharge(t_trailing - t_leading);
    } else if (side=='l') {
	raw->setLeftTime(t_leading);
	raw->setLeftCharge(t_trailing - t_leading);
    }
    if (debugFlag > 1)
        Warning("addRawHit",
		"ADDING hit for sec:%d column:%d cell:%d tm0:%e tm1:%e",
		fLoc[0], fLoc[1], fLoc[2], t_leading, t_trailing);
    return 0;
}