#include "htofwalkpar.h"
#include "hades.h"
#include "hruntimedb.h"
#include "hparamlist.h"
#include "htofdigipar.h"
#include "htofcalpar.h"
#include "htofgeompar.h"
#include "htofraw.h"
ClassImp(HTofWalkPar)
HTofWalkPar::HTofWalkPar(const Char_t* name,const Char_t* title,
                       const Char_t* context)
    : HParCond(name,title,context)
{
    fWPars.Set(6*8*8*4*20*9); 
    fWErrors.Set(6*8*8*4*20); 
    fSideIsUsed.Set(6*8*8*2);
    fParsSX.Set(6*8*8*3);
    fParsDX.Set(6*8*8*5);
    fParsGeantToData.Set(2);
    fParsGroupVelocityData.Set(6*8*8);
    fParsGroupVelocityScale =1;
    func          = new TF1("func",HTofWalkPar::ffunc,0,5000,9);
    fdxsigma      = new TF1("fdxsigma" ,"[0]+[1]*x**[2]",0,25);
    fdxoffset     = new TF1("fdxoffset","[0]+[1]*exp(x*[2])+[3]*exp(x*[4])",0,25);
    fscaleDxSigma = new TF1("fscaleDxSigma","pol2",0,1);
    clear();
}
HTofWalkPar::~HTofWalkPar()
{
    
    delete fdxsigma;
    delete fdxoffset;
    delete fscaleDxSigma;
    delete func;
}
void HTofWalkPar::getContainers()
{
    fCalPar      =(HTofCalPar  *)gHades->getRuntimeDb()->getContainer("TofCalPar");
    fTofGeometry =(HTofGeomPar *)gHades->getRuntimeDb()->getContainer("TofGeomPar");
    fDigiPar     =(HTofDigiPar *)gHades->getRuntimeDb()->getContainer("TofDigiPar");
}
void HTofWalkPar::clear()
{
    fWPars.Reset(0.);
    fWErrors.Reset(0.);
    fSideIsUsed.Reset(1);
    fParsSX.Reset(0);
    fParsDX.Reset(0);
    fParsGeantToData.Reset(0);
    fParsGroupVelocityData.Reset(0);
    fParsGroupVelocityScale =1;
    status = kFALSE;
    resetInputVersions();
    changed = kFALSE;
}
void HTofWalkPar::putParams(HParamList* l)
{
    
    
    if (!l) return;
    l->add("fWPars"                 ,fWPars      );
    l->add("fWErrors"               ,fWErrors    );
    l->add("fSideIsUsed"            ,fSideIsUsed );
    l->add("fParsSX"                ,fParsSX      );
    l->add("fParsDX"                ,fParsDX      );
    l->add("fParsGeantToData"       ,fParsGeantToData);
    l->add("fParsGroupVelocityData" ,fParsGroupVelocityData);
    l->add("fParsGroupVelocityScale",fParsGroupVelocityScale);
}
Bool_t HTofWalkPar::getParams(HParamList* l)
{
    if (!l) return kFALSE;
    if(!( l->fill("fWPars"                 , &fWPars     ))) return kFALSE;
    if(!( l->fill("fWErrors"               , &fWErrors   ))) return kFALSE;
    if(!( l->fill("fSideIsUsed"            , &fSideIsUsed))) return kFALSE;
    if(!( l->fill("fParsSX"                , &fParsSX    ))) return kFALSE;
    if(!( l->fill("fParsDX"                , &fParsDX    ))) return kFALSE;
    if(!( l->fill("fParsGeantToData"       , &fParsGeantToData))) return kFALSE;
    if(!( l->fill("fParsGroupVelocityData" , &fParsGroupVelocityData))) return kFALSE;
    if(!( l->fill("fParsGroupVelocityScale", &fParsGroupVelocityScale))) return kFALSE;
    return kTRUE;
}
Double_t HTofWalkPar::ffunc(Double_t* x, Double_t* par)
{
    if(x[0]<par[8]) return par[2]+par[0]*exp(x[0]*par[1]);
    else            return par[7]+par[3]*exp(x[0]*par[4])+par[5]*pow(x[0],par[6]);
}
Float_t HTofWalkPar::getOffset(Float_t x, Int_t side, Float_t q, Int_t sector, Int_t module, Int_t cell)
{
    Double_t corrs[4] = {0.,0.,0.,0.};
    Double_t locX [4] = {0.,0.,0.,0.};
    HTofDigiParCell& cellg = (*fDigiPar)[sector][module][cell];
    Float_t hl = cellg.getHalfLen();
    if(x!=x)x = 0;
    Float_t xtemp = hl;
    if(side==0 || side==1) {
	xtemp = hl - x;
    } else {
	xtemp = hl + x;
    }
    if(xtemp>hl*2.) xtemp = hl*2.;
    if(xtemp<0)     xtemp = 0.;
    Int_t xbin  = floor(xtemp/(hl*2.)*20);
    Int_t xbint = floor((xtemp + hl/20.)/(hl*2.)*20);
    if(xbin<0)    xbin = 0;
    if(xbin>19)   xbin = 19;
    if(xbint<0)  xbint = 0;
    if(xbint>19) xbint = 19;
    Int_t binsX[4] = {0,1,2,3};
    Int_t binUsed = 0;
    if(xbin==19) {
	binsX[0] = 16;
	binsX[1] = 17;
	binsX[2] = 18;
	binsX[3] = 19;
	binUsed = 3;
    } else if(xbin>1){
	binsX[0] =xbin-2;
	binsX[1] =xbin-1;
	binsX[2] =xbin;
	binsX[3] =xbin+1;
        binUsed = 2;
    }
    for(Int_t i=0;i<4;i++) {
        if(binsX[i]<20)
        locX[i] = hl*2./20.*(binsX[i]) + hl/20.;
        else
        locX[i] = hl*2./20.*(binsX[i]) + hl/20.;
    }
    const Int_t nPar=func->GetNpar();
    Double_t pars[nPar];
    for(Int_t i=0;i<4;i++) {
	const Float_t* par= getPars(sector,module,cell,side,binsX[i]);
	for(Int_t j=0;j<nPar;j++) {
             pars[j]=par[j];
	}
	func->SetParameters(pars);
	corrs[i] = func->Eval(q);
    }
    
    if(binsX[binUsed]>0 && binsX[binUsed]<19) {
	if(xtemp<locX[binUsed]) {
	    return corrs[binUsed-1]+(xtemp-locX[binUsed-1])*(corrs[binUsed]-corrs[binUsed-1])/(locX[binUsed]-locX[binUsed-1]);
	} else {
	    return corrs[binUsed]+(xtemp-locX[binUsed])*(corrs[binUsed+1]-corrs[binUsed])/(locX[binUsed+1]-locX[binUsed]);
	}
    } else if(binsX[binUsed]==0) {
	return corrs[binUsed]+(xtemp-locX[binUsed])*(corrs[binUsed+1]-corrs[binUsed])/(locX[binUsed+1]-locX[binUsed]);
    } else {
	return corrs[binUsed-1]+(xtemp-locX[binUsed-1])*(corrs[binUsed]-corrs[binUsed-1])/(locX[binUsed]-locX[binUsed-1]);
    }
}
void HTofWalkPar::getOffsets(Float_t x, Float_t ql,Float_t qr,
			     Int_t sector, Int_t module, Int_t cell,
			     Float_t& const1,Float_t& const2,Float_t& const3,Float_t& const4)
{
    const1 = getOffset(x, 0, ql, sector, module, cell);
    const2 = getOffset(x, 2, qr, sector, module, cell);
    const3 = getOffset(x, 1, qr, sector, module, cell);
    const4 = getOffset(x, 3, ql, sector, module, cell);
}
void HTofWalkPar::getErrors(Float_t x, Float_t ql,Float_t qr,
			    Int_t sector, Int_t module, Int_t cell,
			    Float_t& const1,Float_t& const2,Float_t& const3,Float_t& const4)
{
    const1 = fabs(getOffset(x, 0, ql*0.93, sector, module, cell)      - getOffset(x, 0, ql*1.07, sector, module, cell));
    const2 = fabs(getOffset(x, 2, qr*0.93, sector, module, cell)      - getOffset(x, 2, qr*1.07, sector, module, cell));
    const3 = fabs(getOffset(x, 1, qr*0.93, sector, module, cell)      - getOffset(x, 1, qr*1.07, sector, module, cell));
    const4 = fabs(getOffset(x, 3, ql*0.93, sector, module, cell)      - getOffset(x, 3, ql*1.07, sector, module, cell));
    if(const1<0.02) const1 = 0.02;
    if(const2<0.02) const2 = 0.02;
    if(const3<0.02) const3 = 0.02;
    if(const4<0.02) const4 = 0.02;
}
void HTofWalkPar::getTofPos(HTofRaw* tofraw,Float_t& tof,Float_t& pos, Float_t x, Float_t startTMP) {
    Int_t sector = tofraw->getSector();
    Int_t module = tofraw->getModule();
    Int_t cell   = tofraw->getCell();
    HLocation loc;
    loc.set(3,0,0,0);
    loc[0]=sector;
    loc[1]=module;
    loc[2]=cell;
    tof = -100.;
    pos = -10000.;
    HTofCalParCell& cellp=(*fCalPar)[loc[0]][loc[1]][loc[2]];
    HTofDigiParCell& cellg=(*fDigiPar)[loc[0]][loc[1]][loc[2]];
    Float_t hl = cellg.getHalfLen();
    Float_t vg = cellp.getVGroup();
    Float_t ql = tofraw->getLeftCharge()  - cellp.getPedestalL();
    Float_t qr = tofraw->getRightCharge() - cellp.getPedestalR();
    Float_t tl = (tofraw->getLeftTime()*0.097)   - startTMP - cellp.getTimK();
    Float_t tr = (tofraw->getRightTime()*0.097)  - startTMP - cellp.getTimK();
    Float_t atof;
    Float_t axpos = x;
    Float_t xl = 0.;
    Float_t xr = 0.;
    xl = hl-axpos;
    xr = hl+axpos;
    Float_t corrl1;
    Float_t corrl2;
    Float_t corrr1;
    Float_t corrr2;
    Float_t errl1;
    Float_t errl2;
    Float_t errr1;
    Float_t errr2;
    getOffsets(axpos, ql, qr, sector, module, cell, corrl1,corrr1,corrl2,corrr2);
    getErrors (axpos, ql, qr, sector, module, cell, errl1 ,errr1 ,errl2 ,errr2);
    
    Float_t wl1  = 1e-12;
    Float_t wl2  = 1e-12;
    Float_t wr1  = 1e-12;
    Float_t wr2  = 1e-12;
    Float_t sigl = 0.180;
    Float_t sigr = 0.180;
    xl = hl-axpos;
    xr = hl+axpos;
    Int_t xbinl1 = floor(xl/(hl*2.)*20);
    Int_t xbinr1 = floor(xr/(hl*2.)*20);
    if(xbinl1<0)  xbinl1 = 0;
    if(xbinl1>19) xbinl1 = 19;
    if(xbinr1<0)  xbinr1 = 0;
    if(xbinr1>19) xbinr1 = 19;
    sigl = getError(sector, module, cell, 0, xbinl1);
    sigr = getError(sector, module, cell, 3, xbinr1);
    if(sigl==0)sigl = 0.4;
    if(sigr==0)sigr = 0.4;
    
    if(getIsUsed(sector,module,cell,0)) {
	wl1 = 1./sqrt(sigl*sigl+errl1*errl1);
	wr2 = 1./sqrt(sigr*sigr+errr2*errr2);
    }
    sigl = getError(sector, module, cell, 1, xbinl1);
    sigr = getError(sector, module, cell, 2, xbinr1);
    if(sigl==0)sigl = 0.4;
    if(sigr==0)sigr = 0.4;
    
    if(getIsUsed(sector,module,cell,1)) {
	wl2 = 1./sqrt(sigl*sigl+errl2*errl2);
	wr1 = 1./sqrt(sigr*sigr+errr1*errr1);
    }
    if(ql<0 && qr<0) return;
    if(ql<0) {wl1=0.;wr2=0.; }
    if(qr<0) {wl2=0.;wr1=0.; }
    if((wl1==0.&&wl2==0.)||(wr1==0.&&wr2==0.)) return;
    axpos = 0.5 * ( ((tr-corrr1)*wr1+(tr-corrr2)*wr2)/(wr1+wr2) - ((tl-corrl1)*wl1+(tl-corrl2)*wl2)/(wl1+wl2) ) * vg ;
    atof  = ( (tl-corrl1+axpos/vg)*wl1+(tl-corrl2+axpos/vg)*wl2 + (tr-corrr1-axpos/vg)*wr1+(tr-corrr2-axpos/vg)*wr2  )/(wl1+wl2+wr1+wr2);
    tof = atof;
    pos = axpos-cellp.getPosK();
}
Double_t HTofWalkPar::getDxSigma (Int_t s,Int_t m,Int_t c,Double_t eloss)
{
    const   Double_t* SX = getParsSX(s,m,c);
    fdxsigma->SetParameters( SX );
    return fdxsigma -> Eval(eloss);
}
Double_t HTofWalkPar::getDxSigmaDigi (Int_t s,Int_t m,Int_t c,Double_t eloss,Double_t vgroup,Double_t beta)
{
    const   Double_t* SX = getParsSX(s,m,c);
    fdxsigma->SetParameters( SX );
    if(beta<0.3)  beta = 0.3;
    if(beta>0.96) beta = 0.96;
    fscaleDxSigma ->SetParameters(fParsGroupVelocityScale.GetArray());
    Double_t scale = fscaleDxSigma ->Eval(beta);
    return fdxsigma -> Eval(eloss) * (getGroupVelocity(s,m,c)/vgroup) * scale;
}
Double_t HTofWalkPar::getDxOffset(Int_t s,Int_t m,Int_t c,Double_t eloss)
{
    const   Double_t* DX = getParsDX(s,m,c);
    fdxoffset->SetParameters( DX );
    return fdxoffset -> Eval(eloss);
}
Double_t HTofWalkPar::getNormedDX(Int_t s,Int_t m,Int_t c,Double_t eloss,Double_t rkDX,Bool_t sim){
    Double_t sigma = HTofWalkPar::getDxSigma(s,m,c,eloss);
    if(sim){
	return rkDX/sigma;
    } else {
	Double_t offset = HTofWalkPar::getDxOffset(s,m,c,eloss);
	return (rkDX-offset)/sigma;
    }
}
void HTofWalkPar::Streamer(TBuffer &R__b)
{
   
   UInt_t R__s, R__c;
   if (R__b.IsReading()) {
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
      HParCond::Streamer(R__b);
      fWPars.Streamer(R__b);
      fWErrors.Streamer(R__b);
      fSideIsUsed.Streamer(R__b);
      if(R__v < 2){
           fParsSX.Set(6*8*8*3);
	   fParsDX.Set(6*8*8*5);
	   fParsGeantToData.Set(2);
           fParsSX.Reset(0);
           fParsDX.Reset(0);
	   fParsGeantToData.Reset(0);
	   fParsGroupVelocityData .Set(6*8*8);
	   fParsGroupVelocityData .Reset(0);
	   fParsGroupVelocityScale.Set(3);
           fParsGroupVelocityScale.Reset(0);
      } else {
	  fParsSX.Streamer(R__b);
	  fParsDX.Streamer(R__b);
	  fParsGeantToData.Streamer(R__b);
	  fParsGroupVelocityData.Streamer(R__b);
	  fParsGroupVelocityScale.Streamer(R__b);
      }
      R__b.CheckByteCount(R__s, R__c, HTofWalkPar::IsA());
   } else {
      R__c = R__b.WriteVersion(HTofWalkPar::IsA(), kTRUE);
      HParCond::Streamer(R__b);
      fWPars.Streamer(R__b);
      fWErrors.Streamer(R__b);
      fSideIsUsed.Streamer(R__b);
      fParsSX.Streamer(R__b);
      fParsDX.Streamer(R__b);
      fParsGeantToData.Streamer(R__b);
      fParsGroupVelocityData.Streamer(R__b);
      fParsGroupVelocityScale.Streamer(R__b);
      R__b.SetByteCount(R__c, kTRUE);
   }
}