using namespace std;
#include "TRandom.h"
#include "hemcdigitizer.h" 
#include "hdebug.h"
#include "hades.h"
#include "hiterator.h"
#include "hruntimedb.h"
#include "hspectrometer.h"
#include "hemcdetector.h" 
#include "hgeantemc.h"
#include "hgeantkine.h"
#include "hevent.h"
#include "hlinearcategory.h"
#include "hemccalsim.h"
#include "hemcgeompar.h"
#include "hemcdigipar.h"
#include "hemccellstatuspar.h"
#include "hemcsimulpar.h"
#include "hstartdef.h"
#include "hstart2hit.h"
#include <iostream>
ClassImp(HEmcDigitizer)
HEmcDigitizer::HEmcDigitizer(void) {
  initVariables();
}
HEmcDigitizer::HEmcDigitizer(const Text_t *name, const Text_t *title) : HReconstructor(name,title) {
  initVariables();
}
HEmcDigitizer::~HEmcDigitizer(void) {
  if (iterGeantEmc) delete iterGeantEmc;
  clearCellobjects();
  cellobjects.clear();
}
void HEmcDigitizer::initVariables(void) {
  fGeantEmcCat     = NULL;
  fGeantKineCat    = NULL;
  fCalCat          = NULL;
  iterGeantEmc     = NULL;
  fGeomPar         = NULL;
  fDigiPar         = NULL;
  fStartHitCat     = NULL;
  zVertBorder      = -130.;    
  energyDepositCut = 15.;      
  signalVelocity   = 299.792;  
  halfOfCellLength = 210.;     
  embeddingmode    = 0;
  fLoc.setNIndex(2);
  fLoc.set(2,0,0);
}
Bool_t HEmcDigitizer::init(void) {
  fEmcDet = (HEmcDetector*)(gHades->getSetup()->getDetector("Emc"));
  if(!fEmcDet){
    Error("init","No Emc Detector found");
    return kFALSE;
  }
  
  cellobjects.resize(cellObjectsSize(), 0 );   
  
  fGeantEmcCat = gHades->getCurrentEvent()->getCategory(catEmcGeantRaw);  
  if (!fGeantEmcCat) {
    Error("HEmcDigitizer::init()","HGeant EMC input missing");
    return kFALSE;
  }
  iterGeantEmc = (HIterator *)fGeantEmcCat->MakeIterator("native");
  fGeantKineCat = (HLinearCategory*)gHades->getCurrentEvent()->getCategory(catGeantKine);
  if(!fGeantKineCat){
      Error("HEmcDigitizer::init()","No catGeantKine in input!");
      return kFALSE;
  }
  
  
  fCalCat = gHades->getCurrentEvent()->getCategory(catEmcCal);  
  if (fCalCat == NULL) {
    fCalCat = fEmcDet->buildMatrixCategory("HEmcCalSim",0.5);  
    gHades->getCurrentEvent()->addCategory(catEmcCal,fCalCat,"Emc");
  }
  if(fCalCat == NULL) return kFALSE;
  if(gHades->getEmbeddingMode()>0) embeddingmode = 1;
  
  
  fStartHitCat=gHades->getCurrentEvent()->getCategory(catStart2Hit);
  if (!fStartHitCat) Warning("init","Start hit level not defined; setting start time to 0");
  return setParameterContainers();
}
Bool_t HEmcDigitizer::setParameterContainers(void) {
  fGeomPar=(HEmcGeomPar*)gHades->getRuntimeDb()->getContainer("EmcGeomPar");
  if (!fGeomPar){
    Error("initParContainer","No EmcGeomPar parameter container");
    return kFALSE;
  }
  fDigiPar    =(HEmcDigiPar *)gHades->getRuntimeDb()->getContainer("EmcDigiPar");
  if (!fDigiPar){
    Error("initParContainer","No EmcDigiPar parameter container");
    return kFALSE;
  }
  fSimulPar    =(HEmcSimulPar *)gHades->getRuntimeDb()->getContainer("EmcSimulPar");
  if (!fSimulPar){
    Error("initParContainer","No EmcSimulPar parameter container");
    return kFALSE;
  }
  pStatuspar = (HEmcCellStatusPar*)gHades->getRuntimeDb()->getContainer("EmcCellStatusPar");
  if (!pStatuspar) return kFALSE;
  return kTRUE;
}
Bool_t HEmcDigitizer::reinit(void) {
  
  sigmaT               = fDigiPar->getSigmaT();
  phot2Energy[0]       = fDigiPar->getPhot2E();        
  phot2Energy[1]       = fDigiPar->getPhot2E2();       
  Float_t sigmaEIntern = fDigiPar->getSigmaEIntern();
  Float_t sigmaEReal   = fDigiPar->getSigmaEReal();    
  Float_t sigmaEReal2  = fDigiPar->getSigmaEReal2();   
  facEnergSmear[0]     = 1000.*TMath::Sqrt(sigmaEReal*sigmaEReal - sigmaEIntern*sigmaEIntern);
  facEnergSmear[1]     = 1000.*TMath::Sqrt(sigmaEReal2*sigmaEReal2 - sigmaEIntern*sigmaEIntern);
  for(Int_t s=0;s<6;s++) labTrans[s] = fGeomPar->getModule(s)->getLabTransform();
  return kTRUE;
}
Int_t HEmcDigitizer::execute(void) {
  
  
  
  Float_t startTimeSmearing = 0; 
  if (fStartHitCat && fStartHitCat->getEntries()>0) {
    HStart2Hit *pStartH = (HStart2Hit *) fStartHitCat->getObject(0);
    if(pStartH != NULL && pStartH->getResolution()!=-1000) {
      startTimeSmearing = pStartH->getResolution();
    }
  }
  
  
  if(embeddingmode > 0) {
    if(gHades->getEmbeddingDebug() == 1) {
      fCalCat->Clear();
    } else {
      Int_t nentr = fCalCat->getEntries();
      for(Int_t n=0;n<nentr;n++) {
        HEmcCalSim* pCal = (HEmcCalSim*)fCalCat->getObject(n);
        Int_t sec  = pCal->getSector();
        Int_t cell = pCal->getCell();
        if (sec<0 || sec>5 || cell<0 || cell>=emcMaxComponents) {
          Warning("HEmcDigitizer:execute","EmcCal cell address invalid: sec=%i cell=%i",sec,cell);
          continue;
        }
        Int_t ind = cellObjectIndex(sec,cell);
        if(cellobjects[ind] == NULL) {
          cellobjects[ind] = new celldata;
          cellobjects[ind]->reset();
        }
        cellobjects[ind]->isEmbeddedReal = kTRUE;
        celltrack* celltr = new celltrack;
        celltr->reset();
        celltr->trackEn   = pCal->getEnergy();
        celltr->gtime     = pCal->getTime();
        celltr->gtrack    = gHades->getEmbeddingRealTrackId();
        cellobjects[ind]->ctracks.push_back(celltr);
      }
    }
  }
  
  iterGeantEmc->Reset();
  HGeantEmc*  geantemc = 0;
  while ((geantemc=(HGeantEmc *)iterGeantEmc->Next())!=0) {
    Int_t trackNumber = geantemc->getTrack();
    Int_t sec         = geantemc->getSector();
    Int_t cell        = geantemc->getCell();
    if (sec<0 || sec>5 || cell<0 || cell>=emcMaxComponents) {
      Warning("HEmcDigitizer:execute","Emc Geant cell address invalid: sec=%i cell=%i",sec,cell);
      continue;
    }
    if(trackNumber <= 0 && trackNumber != -777) continue; 
    
    Int_t pos = HEmcDetector::getPositionFromCell(cell);
    if (pStatuspar->getCellStatus(sec, pos) == 0) continue;
    
    Float_t peHit, xHit, yHit, zHit, tofHit, momHit, trackLength;
    geantemc->getHit(peHit, xHit, yHit, zHit, tofHit, momHit, trackLength);
    if(peHit == 0.) continue;                             
    Int_t pmtType = fSimulPar->getPmtType(sec,cell);
    if(pmtType < 1 || pmtType > 2) continue;    
    Float_t energyHit = peHit * phot2Energy[pmtType-1];         
    
    Int_t ind = cellObjectIndex(sec,cell);
    if(cellobjects[ind] == NULL) {
      cellobjects[ind] = new celldata;
      cellobjects[ind]->reset();
      cellobjects[ind]->energy = 0.F;
    }
    celldata* cdata = cellobjects[ind];
    
    if(trackNumber == -777) {          
      cdata->energy += energyHit;
    } else {
      HGeantEmc* pFirstEmc = getInputHit(geantemc,trackNumber);
      if(trackNumber <= 0 || pFirstEmc == NULL) continue;
      
      tofHit -= (zHit+halfOfCellLength)/signalVelocity;
      
      Bool_t isInTheList = kFALSE;
      Int_t  numInpTrack = pFirstEmc->getTrack(); 
      if(std::find((cdata->inputTracks).begin(),(cdata->inputTracks).end(),numInpTrack) != cdata->inputTracks.end()) continue;
      cdata->inputTracks.push_back(numInpTrack);
      Float_t tofHitD;
      pFirstEmc->getHit(peHit, xHit, yHit, zHit, tofHitD, momHit, trackLength);
      Int_t pmtType = fSimulPar->getPmtType(sec,cell);
      energyHit = peHit * phot2Energy[pmtType-1];  
      
      for(UInt_t i=0;i<cdata->ctracks.size();i++) {
        celltrack* celltr = cdata->ctracks[i];
        if (celltr->gtrack == trackNumber) {
          
          isInTheList      = kTRUE;
          celltr->trackEn += energyHit;
          if (tofHit < celltr->gtime) celltr->gtime = tofHit;
          break;
        }
      }
      if( !isInTheList ) {
        
        
        celltrack* celltr = new celltrack;
        celltr->reset();
        celltr->trackEn   = energyHit;
        celltr->gtime     = tofHit;
        celltr->gtrack    = trackNumber;
        cdata->ctracks.push_back(celltr);
      }
    }
  } 
  
  for(UInt_t i = 0; i < cellobjects.size(); i ++) {
    celldata* cdata = cellobjects[i];
    if (cdata!=NULL && cdata->energy>0.) {
      if(cdata->ctracks.size() == 0) continue;
      Int_t cell = cellFromIndex(i);
      fLoc[0] = sectorFromIndex(i);
      fLoc[1] = cell;
      HEmcCalSim* cal = (HEmcCalSim*) fCalCat->getSlot(fLoc);
      if (cal == NULL) {
        Warning("HEmcDigitizer:execute","HEmcCalSim:getSlot(loc) failed!");
        continue;
      }
      Float_t sigmaE = 0.;
      Float_t energy = 0.;
      if(cdata->energy > 0.) {
        Int_t pmtType = fSimulPar->getPmtType(fLoc[0],cell);
        sigmaE = TMath::Sqrt(cdata->energy/1000.) * facEnergSmear[pmtType-1];
        energy = gRandom->Gaus(cdata->energy,sigmaE);
      }
      if(!cdata->isEmbeddedReal) {
        cal = new(cal) HEmcCalSim;
        cal->setSector(fLoc[0]);
        cal->setCell(cell);
        Char_t  row,col;
        fEmcDet->getRowCol(cell,row,col);
        cal->setRow(row);
        cal->setColumn(col);
      } else {  
        energy += cal->getEnergy();
        sigmaE  = TMath::Sqrt(sigmaE*sigmaE + cal->getSigmaEnergy()*cal->getSigmaEnergy());
      }
      
      cal->setEnergy(energy);
      cal->setSigmaEnergy(sigmaE);
      if(cal->getEnergy() < energyDepositCut) cal->setStatus(-1);  
      
      
      cdata->sortTime();
      Float_t    energySum = 0.;
      celltrack* celltr    = NULL;
      for(UInt_t k = 0; k < cdata->ctracks.size(); k++ ) {
        celltr = cdata->ctracks[k];
        energySum += celltr->trackEn;
        if (energySum > energyDepositCut) break;
      }
      if(!cdata->isEmbeddedReal || gHades->getEmbeddingRealTrackId() != celltr->gtrack) {
        cal->setTime(gRandom->Gaus(celltr->gtime,sigmaT) - startTimeSmearing);
        cal->setTimeTrack(celltr->gtrack);
        cal->setSigmaTime(sigmaT);
      } 
      
      
      cdata->sortEnergy();
      for(UInt_t k = 0; k < cdata->ctracks.size() && k<5; k++ ) {
        celltrack* celltr = cdata->ctracks[k];
        cal->setTrack(celltr->gtrack, celltr->trackEn);
      }     
      
      cal->setNHits(1);
      cal->setTotMult(cdata->ctracks.size());
    }
  }
  
  clearCellobjects();               
  return 0;
}
void  HEmcDigitizer::clearCellobjects(){
  
  
  for(UInt_t i = 0; i < cellobjects.size(); i++) {
    if(cellobjects[i]) cellobjects[i]->reset();
  }
}
HGeantEmc* HEmcDigitizer::getInputHit(HGeantEmc* pGeantEmc,Int_t &inputTrack) const {
  
  
  
  
  Int_t track = pGeantEmc->getTrack();
  inputTrack  = track;
  Int_t       sec        = pGeantEmc->getSector();
  Int_t       cell       = pGeantEmc->getCell();
  HGeantKine* kine       = (HGeantKine*)fGeantKineCat->getObject(track-1);
  
  HGeantEmc*  firstHitInCell = pGeantEmc;
  
  do {
    track = kine->getTrack();
    Int_t first = kine->getFirstEmcHit();
    if(first != -1) {
      
      kine->resetEmcIter();
      HGeantEmc* gemc = NULL;
      while( (gemc = (HGeantEmc*)kine->nextEmcHit()) != NULL) {
        if(gemc->getSector() != sec) continue;
        inputTrack = kine->getTrack();
        if(gemc->getCell() == cell) { 
          Float_t peHit,xHit, yHit, zHit, tofHit, momHit, trackLength;
          gemc->getHit(peHit, xHit, yHit, zHit, tofHit, momHit, trackLength);
          if(peHit > 0.) {
            firstHitInCell = gemc;
            break;  
          }
        }
      }
    }
  } while((kine = kine->getParent(track,fGeantKineCat)) != NULL);
  
  if(zVertBorder < 0.) {
    
    HGeomVector ver;
    kine = (HGeantKine*)fGeantKineCat->getObject(inputTrack-1);
    while(kTRUE) {
      kine->getVertex(ver);
      ver  = labTrans[sec].transTo(ver);
      if(ver.getZ() < zVertBorder || ver.getZ()>0.) break;
      kine = kine->getParent(inputTrack,fGeantKineCat);
      if(kine == NULL) break;
      inputTrack = kine->getTrack();
    }
  }
    
  return firstHitInCell;
}