#include "TEveCalo.h"
#include "TEveCaloData.h"
#include "TEveProjections.h"
#include "TEveProjectionManager.h"
#include "TEveRGBAPalette.h"
#include "TEveText.h"
#include "TEveTrans.h"
#include "TClass.h"
#include "TMathBase.h"
#include "TMath.h"
#include "TAxis.h"
#include "TGLUtil.h"
#include <cassert>
ClassImp(TEveCaloViz);
TEveCaloViz::TEveCaloViz(TEveCaloData* data, const char* n, const char* t) :
   TEveElement(),
   TNamed(n, t),
   TEveProjectable(),
   fData(0),
   fCellIdCacheOK(kFALSE),
   fEtaMin(-10),
   fEtaMax(10),
   fPhi(0.),
   fPhiOffset(TMath::Pi()),
   fAutoRange(kTRUE),
   fBarrelRadius(-1.f),
   fEndCapPosF(-1.f),
   fEndCapPosB(-1.f),
   fPlotEt(kTRUE),
   fMaxTowerH(100),
   fScaleAbs(kFALSE),
   fMaxValAbs(100),
   fValueIsColor(kFALSE),
   fPalette(0)
{
   
   fPickable = kTRUE;
   SetElementNameTitle(n, t);
   SetData(data);
}
TEveCaloViz::~TEveCaloViz()
{
   
   if (fPalette) fPalette->DecRefCount();
}
Float_t TEveCaloViz::GetDataSliceThreshold(Int_t slice) const
{
   
   return fData->RefSliceInfo(slice).fThreshold;
}
TEveElement* TEveCaloViz::ForwardSelection()
{
   
   
   return fData;
}
TEveElement* TEveCaloViz::ForwardEdit()
{
   
   
   
   return this;
}
void TEveCaloViz::SetDataSliceThreshold(Int_t slice, Float_t val)
{
   
   fData->SetSliceThreshold(slice, val);
}
Color_t TEveCaloViz::GetDataSliceColor(Int_t slice) const
{
   
   return fData->RefSliceInfo(slice).fColor;
}
void TEveCaloViz::SetDataSliceColor(Int_t slice, Color_t col)
{
   
   fData->SetSliceColor(slice, col);
}
void TEveCaloViz::SetEta(Float_t l, Float_t u)
{
   
   fEtaMin=l;
   fEtaMax=u;
   InvalidateCellIdCache();
}
void TEveCaloViz::SetPlotEt(Bool_t isEt)
{
   
   fPlotEt=isEt;
   if (fPalette)
      fPalette->SetLimits(0, TMath::CeilNint(GetMaxVal()));
   InvalidateCellIdCache();
}
Float_t TEveCaloViz::GetMaxVal() const
{
   
   return fData->GetMaxVal(fPlotEt);
}
void TEveCaloViz::SetPhiWithRng(Float_t phi, Float_t rng)
{
   
   using namespace TMath;
   fPhi = phi;
   fPhiOffset = rng;
   InvalidateCellIdCache();
}
Float_t TEveCaloViz::GetTransitionTheta() const
{
   
   return TMath::ATan(fBarrelRadius/fEndCapPosF);
}
Float_t TEveCaloViz::GetTransitionEta() const
{
   
   using namespace TMath;
   Float_t t = GetTransitionTheta()*0.5f;
   return -Log(Tan(t));
}
Float_t TEveCaloViz::GetTransitionThetaForward() const
{
   
   return TMath::ATan(fBarrelRadius/fEndCapPosF);
}
Float_t TEveCaloViz::GetTransitionEtaForward() const
{
   
   using namespace TMath;
   Float_t t = GetTransitionThetaForward()*0.5f;
   return -Log(Tan(t));
}
Float_t TEveCaloViz::GetTransitionThetaBackward() const
{
   
   return TMath::ATan(fBarrelRadius/fEndCapPosB);
}
Float_t TEveCaloViz::GetTransitionEtaBackward() const
{
   
   using namespace TMath;
   Float_t t = GetTransitionThetaBackward()*0.5f;
   
   return Log(-Tan(t));
}
void TEveCaloViz::SetData(TEveCaloData* data)
{
   
   if (data == fData) return;
   if (fData) fData->RemoveElement(this);
   fData = data;
   if (fData)
   {
      fData->AddElement(this);
      DataChanged();
   }
}
void TEveCaloViz::DataChanged()
{
   
   
   Double_t min, max, delta;
   fData->GetEtaLimits(min, max);
   if (fAutoRange) {
      fEtaMin = min;
      fEtaMax = max;
   } else {
      if (fEtaMin < min) fEtaMin = min;
      if (fEtaMax > max) fEtaMax = max;
   }
   fData->GetPhiLimits(min, max);
   delta = 0.5*(max - min);
   if (fAutoRange || fPhi < min || fPhi > max) {
      fPhi       = 0.5*(max + min);
      fPhiOffset = delta;
   } else {
      if (fPhiOffset > delta) fPhiOffset = delta;
   }
   if (fPalette)
   {
      Int_t hlimit = TMath::CeilNint(GetMaxVal());
      fPalette->SetLimits(0, hlimit);
      fPalette->SetMin(0);
      fPalette->SetMax(hlimit);
   }
   InvalidateCellIdCache();
}
Bool_t TEveCaloViz::AssertCellIdCache() const
{
   
   
 
   TEveCaloViz* cv = const_cast<TEveCaloViz*>(this);
   if (!fCellIdCacheOK) {
      cv->BuildCellIdCache();
      return kTRUE;
   } else {
      return kFALSE;
   }
}
Bool_t TEveCaloViz::CellInEtaPhiRng(TEveCaloData::CellData_t& cellData) const
{
   
   if (cellData.EtaMin() >= fEtaMin && cellData.EtaMax() <= fEtaMax)
   {
      if (TEveUtil::IsU1IntervalContainedByMinMax
          (fPhi-fPhiOffset, fPhi+fPhiOffset, cellData.PhiMin(), cellData.PhiMax()))
         return kTRUE;
   }
   return kFALSE;
}
void TEveCaloViz::AssignCaloVizParameters(TEveCaloViz* m)
{
   
   SetData(m->fData);
   fEtaMin    = m->fEtaMin;
   fEtaMax    = m->fEtaMax;
   fPhi       = m->fPhi;
   fPhiOffset = m->fPhiOffset;
   fBarrelRadius = m->fBarrelRadius;
   fEndCapPosF    = m->fEndCapPosF;
   fEndCapPosB    = m->fEndCapPosB;
   if (m->fPalette)
   {
      TEveRGBAPalette& mp = * m->fPalette;
      if (fPalette) fPalette->DecRefCount();
      fPalette = new TEveRGBAPalette(mp.GetMinVal(), mp.GetMaxVal(), mp.GetInterpolate());
      fPalette->SetDefaultColor(mp.GetDefaultColor());
   }
}
void TEveCaloViz::SetPalette(TEveRGBAPalette* p)
{
   
   if ( fPalette == p) return;
   if (fPalette) fPalette->DecRefCount();
   fPalette = p;
   if (fPalette) fPalette->IncRefCount();
}
Float_t TEveCaloViz::GetValToHeight() const
{
   
   if (fScaleAbs)
   {
      return fMaxTowerH/fMaxValAbs;
   }
   else
   {
     if (fData->Empty())
       return 1;
      return fMaxTowerH/fData->GetMaxVal(fPlotEt);
   }
}
TEveRGBAPalette* TEveCaloViz::AssertPalette()
{
   
   
   
   if (fPalette == 0) {
      fPalette = new TEveRGBAPalette;
      fPalette->SetDefaultColor((Color_t)4);
      Int_t hlimit = TMath::CeilNint(GetMaxVal());
      fPalette->SetLimits(0, hlimit);
      fPalette->SetMin(0);
      fPalette->SetMax(hlimit);
   }
   return fPalette;
}
void TEveCaloViz::Paint(Option_t* )
{
   
   if (fData)
   {
      PaintStandard(this);
   }
}
TClass* TEveCaloViz::ProjectedClass(const TEveProjection*) const
{
   
   return TEveCalo2D::Class();
}
void TEveCaloViz::SetupColorHeight(Float_t value, Int_t slice, Float_t& outH) const
{
   
   if (fValueIsColor)
   {
      outH = GetValToHeight()*fData->GetMaxVal(fPlotEt);
      UChar_t c[4];
      fPalette->ColorFromValue((Int_t)value, c);
      c[3] = fData->GetSliceTransparency(slice);
      TGLUtil::Color4ubv(c);
   }
   else
   {
      TGLUtil::ColorTransparency(fData->GetSliceColor(slice), fData->GetSliceTransparency(slice));
      outH = GetValToHeight()*value;
   }
}
ClassImp(TEveCalo3D);
TEveCalo3D::TEveCalo3D(TEveCaloData* d, const char* n, const char* t):
   TEveCaloViz(d, n, t),
   fRnrEndCapFrame    (kTRUE),
   fRnrBarrelFrame    (kTRUE),
   fFrameWidth        (0.5),
   fFrameColor        (kGray+1),
   fFrameTransparency (80)
{
   
   fCanEditMainColor        = kTRUE;
   fCanEditMainTransparency = kTRUE;
   fMainColorPtr = &fFrameColor;
}
void TEveCalo3D::BuildCellIdCache()
{
   
   fCellList.clear();
   fData->GetCellList(GetEta(), GetEtaRng(), GetPhi(), GetPhiRng(), fCellList);
   fCellIdCacheOK = kTRUE;
}
void TEveCalo3D::ComputeBBox()
{
   
   
   BBoxInit();
   Float_t th = (fData) ? GetValToHeight() * fData->GetMaxVal(fPlotEt) : 0;
   fBBox[0] = -fBarrelRadius - th;
   fBBox[1] =  fBarrelRadius + th;
   fBBox[2] =  fBBox[0];
   fBBox[3] =  fBBox[1];
   fBBox[4] =  fEndCapPosB - th;
   fBBox[5] =  fEndCapPosF + th;
}
ClassImp(TEveCalo2D);
TEveCalo2D::TEveCalo2D(const char* n, const char* t):
   TEveCaloViz(0, n, t),
   TEveProjected(),
   fOldProjectionType(TEveProjection::kPT_Unknown),
   fMaxESumBin( 0),
   fMaxEtSumBin(0)
{
   
}
TEveCalo2D::~TEveCalo2D()
{
   
   TEveCaloData::vCellId_t* cids;
   UInt_t n;
   
   n = fCellListsSelected.size();
   for(UInt_t i = 0; i < n; ++i) {
      cids = fCellListsSelected[i];
      if (cids) {
         cids->clear(); delete cids;
      }
   }
   fCellListsSelected.clear();
   
   n = fCellLists.size();
   for(UInt_t i = 0; i < n; ++i) {
      cids = fCellLists[i];
      if (cids) {
         cids->clear(); delete cids;
      }
   }
   fCellLists.clear();
}
void TEveCalo2D::UpdateProjection()
{
   
   if (fManager->GetProjection()->GetType() != fOldProjectionType)
   {
      fCellIdCacheOK=kFALSE;
      fOldProjectionType = fManager->GetProjection()->GetType();
   }
   ComputeBBox();
}
void TEveCalo2D::SetProjection(TEveProjectionManager* mng, TEveProjectable* model)
{
   
   TEveProjected::SetProjection(mng, model);
   TEveCaloViz* viz = dynamic_cast<TEveCaloViz*>(model);
   AssignCaloVizParameters(viz);
}
void TEveCalo2D::BuildCellIdCache()
{
   
   
   for (vBinCells_i it = fCellLists.begin(); it != fCellLists.end(); it++)
   {
      if (*it)
      {
         (*it)->clear();
         delete *it;
      }
   }
   fCellLists.clear();
   fCellLists.push_back(0);
   TEveProjection::EPType_e pt = fManager->GetProjection()->GetType();
   TEveCaloData::vCellId_t* clv; 
   Bool_t isRPhi = (pt == TEveProjection::kPT_RPhi);
   const TAxis* axis = isRPhi ? fData->GetPhiBins() :  fData->GetEtaBins();
   Int_t nBins = axis->GetNbins();
   Float_t min, max;
   if (isRPhi)
   {
      min = GetPhiMin() - fData->GetEps();
      max = GetPhiMax() + fData->GetEps();
      for (Int_t ibin = 1; ibin <= nBins; ++ibin) {
         clv = 0;
         if ( TEveUtil::IsU1IntervalOverlappingByMinMax
              (min, max, axis->GetBinLowEdge(ibin), axis->GetBinUpEdge(ibin)))
         {
            clv = new TEveCaloData::vCellId_t();
            fData->GetCellList(GetEta(), GetEtaRng(), axis->GetBinCenter(ibin), axis->GetBinWidth(ibin), *clv);
            if (!clv->size()) {
               delete clv; clv = 0;
            }
         }
         fCellLists.push_back(clv);
      }
   }
   else
   {
      min = GetEtaMin() - fData->GetEps();
      max = GetEtaMax() + fData->GetEps();
      for (Int_t ibin = 1; ibin <= nBins; ++ibin) {
         clv = 0;
         Float_t low = axis->GetBinLowEdge(ibin);
         Float_t up = axis->GetBinUpEdge(ibin) ;
         if (low >= min && up <= max)
         {
            clv = new TEveCaloData::vCellId_t();
            fData->GetCellList(axis->GetBinCenter(ibin), axis->GetBinWidth(ibin), fPhi, GetPhiRng(), *clv);
            if (!clv->size()) {
               delete clv; clv = 0;
            }
         }
         fCellLists.push_back(clv);
      }
   }
   
   if (!fScaleAbs)
   {
      fMaxESumBin  = 0;
      fMaxEtSumBin = 0;
      Float_t sumE  = 0;
      Float_t sumEt = 0;
      TEveCaloData::CellData_t  cellData;
      for (Int_t ibin = 1; ibin <= nBins; ++ibin) {
         TEveCaloData::vCellId_t* cids = fCellLists[ibin];
         if (cids)
         {
            sumE = 0; sumEt = 0;
            for (TEveCaloData::vCellId_i it = cids->begin(); it != cids->end(); it++)
            {  
               fData->GetCellData(*it, cellData);
               sumE  += cellData.Value(kFALSE);
               sumEt += cellData.Value(kTRUE);
            }
            fMaxESumBin  = TMath::Max(fMaxESumBin,  sumE);
            fMaxEtSumBin = TMath::Max(fMaxEtSumBin, sumEt);  
         }
      }
      ComputeBBox();
   }
   fCellIdCacheOK= kTRUE;
}
void TEveCalo2D::CellSelectionChanged()
{
   
   CellSelectionChangedInternal(fData->GetCellsSelected(), fCellListsSelected);
   CellSelectionChangedInternal(fData->GetCellsHighlighted(), fCellListsHighlighted);
}
void TEveCalo2D::CellSelectionChangedInternal(TEveCaloData::vCellId_t& inputCells, std::vector<TEveCaloData::vCellId_t*>& outputCellLists)
{
   
   Bool_t isRPhi = (fManager->GetProjection()->GetType() == TEveProjection::kPT_RPhi);
   const TAxis* axis = isRPhi ? fData->GetPhiBins() :  fData->GetEtaBins();
   
   for (vBinCells_i it = outputCellLists.begin(); it != outputCellLists.end(); it++)
   {
      if (*it)
      {
         (*it)->clear();
         delete *it;
      }
   }
   outputCellLists.clear();
   UInt_t nBins = axis->GetNbins();
   outputCellLists.resize(nBins+1);
   for (UInt_t b = 0; b <= nBins; ++b)
      outputCellLists[b] = 0;
   for(UInt_t bin = 1; bin <= nBins; ++bin)
   {
      TEveCaloData::vCellId_t* idsInBin = fCellLists[bin];
      if (!idsInBin)
         continue;
      for (TEveCaloData::vCellId_i i = idsInBin->begin(); i != idsInBin->end(); i++)
      { 
         for (TEveCaloData::vCellId_i j = inputCells.begin(); j != inputCells.end(); j++)
         {
            if( (*i).fTower == (*j).fTower && (*i).fSlice == (*j).fSlice)
            {
               if (!outputCellLists[bin])
                  outputCellLists[bin] = new TEveCaloData::vCellId_t();
               outputCellLists[bin]->push_back(TEveCaloData::CellId_t((*i).fTower, (*i).fSlice, (*i).fFraction));
            }
         }
      }
   }
}
void TEveCalo2D::SetScaleAbs(Bool_t sa)
{
   
   
   TEveCaloViz::SetScaleAbs(sa);
   BuildCellIdCache();
}
Float_t TEveCalo2D::GetValToHeight() const
{
   
   
   AssertCellIdCache();
   if (fScaleAbs)
   {
      return fMaxTowerH/fMaxValAbs;
   }
   else
   {
      if (fData->Empty())
         return 1;
      if (fPlotEt)
         return fMaxTowerH/fMaxEtSumBin;
      else
         return fMaxTowerH/fMaxESumBin;
   }
}
void TEveCalo2D::ComputeBBox()
{
   
   
   BBoxZero();
   Float_t x, y, z;
   Float_t th = fMaxTowerH                                           ;
   Float_t r  = fBarrelRadius + th;
   x = r,  y = 0, z = 0;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
   x = -r, y = 0, z = 0;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
   x = 0, y = 0, z = fEndCapPosF + th;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
   x = 0, y = 0, z = fEndCapPosB - th;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
   x = 0, y = r,  z = 0;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
   x = 0, y = -r, z = 0;
   fManager->GetProjection()->ProjectPoint(x, y, z, fDepth);
   BBoxCheckPoint(x, y, z);
}
ClassImp(TEveCaloLego);
TEveCaloLego::TEveCaloLego(TEveCaloData* d, const char* n, const char* t):
   TEveCaloViz(d, n, t),
   fFontColor(-1),
   fGridColor(-1),
   fPlaneColor(kRed-5),
   fPlaneTransparency(60),
   fNZSteps(6),
   fZAxisStep(0.f),
   fAutoRebin(kTRUE),
   fPixelsPerBin(12),
   fNormalizeRebin(kFALSE),
   fProjection(kAuto),
   f2DMode(kValSize),
   fBoxMode(kBack),
   fDrawHPlane(kFALSE),
   fHPlaneVal(0),
   fHasFixedHeightIn2DMode(kFALSE),
   fFixedHeightValIn2DMode(0.f),
   fDrawNumberCellPixels(18), 
   fCellPixelFontSize(12) 
{
   
   fMaxTowerH = 1;
   SetElementNameTitle("TEveCaloLego", "TEveCaloLego");
}
void TEveCaloLego::SetData(TEveCaloData* data)
{
   TEveCaloViz::SetData(data);
}
void TEveCaloLego::BuildCellIdCache()
{
   
   fCellList.clear();
   fData->GetCellList(GetEta(), GetEtaRng(), GetPhi(), GetPhiRng(), fCellList);
   fCellIdCacheOK = kTRUE;
}
void TEveCaloLego::ComputeBBox()
{
   
   
   
   BBoxZero();
   Float_t ex = 1.2; 
   Float_t a = 0.5*ex;
   fBBox[0] = -a;
   fBBox[1] =  a;
   fBBox[2] = -a;
   fBBox[3] =  a;
   
   Double_t em, eM, pm, pM;
   fData->GetEtaLimits(em, eM);
   fData->GetPhiLimits(pm, pM);
   Double_t r = (eM-em)/(pM-pm);
   if (r<1)
   {
      fBBox[2] /= r;
      fBBox[3] /= r;
   }
   else
   {
      fBBox[0] *= r;
      fBBox[1] *= r;
   }
   fBBox[4] =  0;
   if (fScaleAbs && !fData->Empty())
      fBBox[5] = GetMaxVal()*GetValToHeight();
   else
      fBBox[5] = fMaxTowerH;
}