#ifndef HTRB3TDCUNPACKER_H
#define HTRB3TDCUNPACKER_H
#include "htrb3unpacker.h"
#include "htrb3calpar.h"
#include "hades.h"
#include "hevent.h"
#include "heventheader.h"
#include "TObject.h"
#include <vector>
#include <map>
class HTrb3CalparTdc;
class HTrb3TdcUnpacker : public HTrb3Unpacker  {
#define  REFCHAN 0
    
public:
   struct ChannelRec {
      Double_t rising_tm[10];
      Double_t falling_tm[10];
      UInt_t   rising_mult;
      UInt_t   falling_mult;
      Bool_t   hasData;
      ChannelRec() :
         rising_mult(0),
	  falling_mult(0),
          hasData(kFALSE) {}
      void clear()
      {
         rising_mult  = 0;
	 falling_mult = 0;
         hasData      = kFALSE;
      }
      void addHit(Bool_t rising, Double_t tm)
      {
         if (rising) {
            if (rising_mult < 10)
               rising_tm[rising_mult++] = tm;
	 } else {
            if (falling_mult < 10)
               falling_tm[falling_mult++] = tm;
	 }
         hasData = kTRUE;
      }
      Double_t getHit(Bool_t rising, UInt_t cnt)
      {
         return rising ?
            (cnt<rising_mult ? rising_tm[cnt] : 0.) :
            (cnt<falling_mult ? falling_tm[cnt] : 0.);
      }
      Double_t getLastHit(Bool_t rising)
      {
         return rising ?
            (rising_mult>0 ? rising_tm[rising_mult-1] : 0.) :
            (falling_mult>0 ? falling_tm[falling_mult-1] : 0.);
      }
      void substractRefTime(Double_t reftm)
      {
         for (UInt_t n=0;n<rising_mult;n++)
            rising_tm[n] -= reftm;
         for (UInt_t n=0;n<falling_mult;n++)
            falling_tm[n] -= reftm;
      }
   };
   static const size_t maxchan = 65;
   struct TDC {
      UInt_t pSubEvtId;
      UInt_t fTdcId;
      UInt_t nChan;
      UInt_t fCalibr[2]; 
      Int_t fNcalibr; 
      Bool_t  fFoundInStream;  
      ChannelRec fCh[maxchan];
      std::vector<Int_t>chanList; 
      Bool_t  fhasData ;  
      TDC(size_t numchannels=maxchan) :
         pSubEvtId(0),
         fTdcId(0),
	  nChan(numchannels), fNcalibr(0), fFoundInStream(kFALSE) {
	      chanList.reserve(maxchan);
	  }
      void clear()
      {
         for (UInt_t i = 0; i < nChan; ++i)
	     fCh[i].clear();
	 chanList.clear();
         fhasData = kFALSE;
      }
      Bool_t hasData(){
	  for (UInt_t ch=0;ch<numChannels();ch++) {
              if(ch==REFCHAN) continue;
	      if(fCh[ch].hasData) return kTRUE;
	  }
          return kFALSE;
      }
      UInt_t getTrbAddr() const { return fTdcId; }
      UInt_t numChannels() const { return nChan; }
      ChannelRec& getCh(UInt_t n) { return fCh[n]; }
      Int_t correctRefTimeCh(UInt_t refch = REFCHAN)
      {
         
	 if (refch>=numChannels()) return kFALSE;
         if(!fhasData)             return kTRUE;
	 if(!fFoundInStream)       return kTRUE;   
	 if (fCh[refch].rising_mult<=0)
	 {
	     Long_t seqnum = -1;
	     if (gHades != 0 && gHades->getCurrentEvent() != 0 &&
		 gHades->getCurrentEvent()->getHeader() != 0) {
		 seqnum = gHades->getCurrentEvent()->getHeader()->getEventSeqNumber();
	     }
	     cerr << "   Warning<HTrb3TdcUnpacker::TDC::correctRefTimeCh()>: No reference time for trb=0x" << hex << getTrbAddr() <<" sub event 0x"<<pSubEvtId<< dec << " in evt seq=" << seqnum << endl;
	     return kFALSE;
	 }
         Double_t reftm = fCh[refch].rising_tm[0];
         for (UInt_t ch=0;ch<numChannels();ch++)
            fCh[ch].substractRefTime(reftm);
         return kTRUE;
      }
   };
protected:
   HTrb3Calpar* calpar;         
   std::map<UInt_t, HTrb3CalparTdc*> fCalpars;
   std::vector<TDC> fTDCs;      
   std::vector<Int_t> factiveTDCs;      
   std::vector<Int_t> fFilledTDCs;      
   std::map<UInt_t, Int_t> fTDCsMap; 
   Int_t nCalSrc;               
   UInt_t fMinAddress; 
   UInt_t fMaxAddress; 
   Bool_t fUseTDCFromLookup; 
   Int_t getEventSeqNumber();
   void addCalpar(UInt_t id1, HTrb3CalparTdc*);
 public:
   HTrb3TdcUnpacker(vector<UInt_t>& ids);
   virtual ~HTrb3TdcUnpacker();
   virtual Bool_t reinit();
   UInt_t numTDC      () const   { return fTDCs.size(); }
   TDC *  getTDC      (UInt_t n) { return &(fTDCs[n]); }
   UInt_t numActiveTDC() const   { return factiveTDCs.size(); }
   TDC *  getActiveTDC(UInt_t n) { return &(fTDCs[factiveTDCs[n]]); }
   UInt_t numFilledTDC() const   { return fFilledTDCs.size(); }
   TDC *  getFilledTDC(UInt_t n) { return &(fTDCs[fFilledTDCs[n]]); }
   
   Int_t addTDC(UInt_t trbaddr, size_t numchannels=maxchan);
   void setCalibrationSource(UInt_t s) { nCalSrc = s; }
   Bool_t scanTdcData(UInt_t trbaddr, UInt_t* data, UInt_t datalen);
   Bool_t correctRefTimeCh(UInt_t ch = 0);
   void setMinAddress(UInt_t trbnetaddress) { fMinAddress=trbnetaddress; }
   void setMaxAddress(UInt_t trbnetaddress) { fMaxAddress=trbnetaddress; }
protected:
   void clearAll();
   virtual Bool_t decodeData(UInt_t trbaddr, UInt_t n, UInt_t * data);
   ClassDef(HTrb3TdcUnpacker,0); 
};
#endif /* !HTRB3TDCUNPACKER_H */