// @(#)root/eve:$Id$
// Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007

/*************************************************************************
 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TEveRGBAPalette.h"

#include "TColor.h"
#include "TStyle.h"
#include "TMath.h"

/** \class TEveRGBAPalette
\ingroup TEve
A generic, speed-optimised mapping from value to RGBA color
supporting different wrapping and range truncation modes.

Flag fFixColorRange: specifies how the palette is mapped to signal values:
  - true  - LowLimit -> HighLimit
  - false - MinValue -> MaxValue
*/

ClassImp(TEveRGBAPalette);

////////////////////////////////////////////////////////////////////////////////
/// Constructor.

TEveRGBAPalette::TEveRGBAPalette() :
   TObject(), TQObject(),
   TEveRefCnt(),

   fUIf(1), fUIc(0),

   fLowLimit(0), fHighLimit(0), fMinVal(0), fMaxVal(0),

   fUIDoubleRep     (kFALSE),
   fInterpolate     (kTRUE),
   fShowDefValue    (kTRUE),
   fFixColorRange   (kFALSE),
   fUnderflowAction (kLA_Cut),
   fOverflowAction  (kLA_Clip),

   fDefaultColor(-1),
   fUnderColor  (-1),
   fOverColor   (-1),

   fNBins(0), fCAMin(0), fCAMax(0), fColorArray(nullptr)
{
   SetLimits(0, 1024);
   SetMinMax(0,  512);

   SetDefaultColor(0);
   SetUnderColor(1);
   SetOverColor(2);
}

////////////////////////////////////////////////////////////////////////////////
/// Constructor.

TEveRGBAPalette::TEveRGBAPalette(Int_t min, Int_t max, Bool_t interp,
                                 Bool_t showdef, Bool_t fixcolrng) :
   TObject(), TQObject(),
   TEveRefCnt(),

   fUIf(1), fUIc(0),

   fLowLimit(0), fHighLimit(0), fMinVal(0), fMaxVal(0),

   fUIDoubleRep     (kFALSE),
   fInterpolate     (interp),
   fShowDefValue    (showdef),
   fFixColorRange   (fixcolrng),
   fUnderflowAction (kLA_Cut),
   fOverflowAction  (kLA_Clip),

   fDefaultColor(-1),
   fUnderColor  (-1),
   fOverColor   (-1),

   fNBins(0), fCAMin(0), fCAMax(0), fColorArray(nullptr)
{
   SetLimits(min, max);
   SetMinMax(min, max);

   SetDefaultColor(0);
   SetUnderColor(1);
   SetOverColor(2);
}

////////////////////////////////////////////////////////////////////////////////
/// Destructor.

TEveRGBAPalette::~TEveRGBAPalette()
{
   delete [] fColorArray;
}

////////////////////////////////////////////////////////////////////////////////
/// Set RGBA color 'pixel' for signal-value 'val'.

void TEveRGBAPalette::SetupColor(Int_t val, UChar_t* pixel) const
{
   using namespace TMath;
   Float_t div  = Max(1, fCAMax - fCAMin);
   Int_t   nCol = gStyle->GetNumberOfColors();

   Float_t f;
   if      (val >= fCAMax) f = nCol - 1;
   else if (val <= fCAMin) f = 0;
   else                    f = (val - fCAMin)/div*(nCol - 1);

   if (fInterpolate) {
      Int_t  bin = (Int_t) f;
      Float_t f2 = f - bin, f1 = 1.0f - f2;
      TEveUtil::ColorFromIdx(f1, gStyle->GetColorPalette(bin),
                             f2, gStyle->GetColorPalette(Min(bin + 1, nCol - 1)),
                             pixel);
   } else {
      TEveUtil::ColorFromIdx(gStyle->GetColorPalette((Int_t) Nint(f)), pixel);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Construct internal color array that maps signal value to RGBA color.

void TEveRGBAPalette::SetupColorArray() const
{
   if (fColorArray)
      delete [] fColorArray;

   if (fFixColorRange) {
      fCAMin = fLowLimit; fCAMax = fHighLimit;
   } else {
      fCAMin = fMinVal;   fCAMax = fMaxVal;
   }
   fNBins = fCAMax - fCAMin + 1;

   fColorArray = new UChar_t [4 * fNBins];
   UChar_t* p = fColorArray;
   for(Int_t v = fCAMin; v <= fCAMax; ++v, p+=4)
      SetupColor(v, p);
}

////////////////////////////////////////////////////////////////////////////////
/// Clear internal color array.

void TEveRGBAPalette::ClearColorArray()
{
   if (fColorArray) {
      delete [] fColorArray;
      fColorArray = nullptr;
      fNBins = fCAMin = fCAMax = 0;
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Set low/high limits on signal value. Current min/max values are
/// clamped into the new limits.

void TEveRGBAPalette::SetLimits(Int_t low, Int_t high)
{
   fLowLimit  = low;
   fHighLimit = high;

   if (fMaxVal < fLowLimit)  SetMax(fLowLimit);
   if (fMinVal < fLowLimit)  SetMin(fLowLimit);
   if (fMinVal > fHighLimit) SetMin(fHighLimit);
   if (fMaxVal > fHighLimit) SetMax(fHighLimit);

   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set low/high limits and rescale current min/max values.

void TEveRGBAPalette::SetLimitsScaleMinMax(Int_t low, Int_t high)
{
   Float_t rng_old = fHighLimit - fLowLimit;
   Float_t rng_new = high - low;

   fMinVal = TMath::Nint(low + (fMinVal - fLowLimit)*rng_new/rng_old);
   fMaxVal = TMath::Nint(low + (fMaxVal - fLowLimit)*rng_new/rng_old);
   fLowLimit  = low;
   fHighLimit = high;

   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set current min value.

void TEveRGBAPalette::SetMin(Int_t min)
{
   fMinVal = TMath::Min(min, fMaxVal);
   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set current max value.

void TEveRGBAPalette::SetMax(Int_t max)
{
   fMaxVal = TMath::Max(max, fMinVal);
   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set current min/max values.

void TEveRGBAPalette::SetMinMax(Int_t min, Int_t max)
{
   fMinVal = min;
   fMaxVal = max;
   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set flag determining whether GUI editor and overlays should show limits
/// and axis values as real values with mapping from integer value i to real
/// value d as: d = f*i + fc

void TEveRGBAPalette::SetUIDoubleRep(Bool_t b, Double_t f, Double_t c)
{
   fUIDoubleRep = b;
   if (fUIDoubleRep) {
      fUIf = f;  fUIc = c;
   } else {
      fUIf = 1;  fUIc = 0;
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Set interpolation flag. This determines how colors from ROOT's
/// palette are mapped into RGBA values for given signal.

void TEveRGBAPalette::SetInterpolate(Bool_t b)
{
   fInterpolate = b;
   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set flag specifying how the palette is mapped to signal values:
///  true  - LowLimit -> HighLimit
///  false - MinValue -> MaxValue

void TEveRGBAPalette::SetFixColorRange(Bool_t v)
{
   fFixColorRange = v;
   ClearColorArray();
}

////////////////////////////////////////////////////////////////////////////////
/// Set default color.

void TEveRGBAPalette::SetDefaultColor(Color_t ci)
{
   fDefaultColor = ci;
   TEveUtil::ColorFromIdx(ci, fDefaultRGBA, kTRUE);
}

////////////////////////////////////////////////////////////////////////////////
/// Set default color.

void TEveRGBAPalette::SetDefaultColorPixel(Pixel_t pix)
{
   SetDefaultColor(Color_t(TColor::GetColor(pix)));
}

////////////////////////////////////////////////////////////////////////////////
/// Set default color.

void TEveRGBAPalette::SetDefaultColorRGBA(UChar_t r, UChar_t g, UChar_t b, UChar_t a)
{
   fDefaultColor = Color_t(TColor::GetColor(r, g, b));
   fDefaultRGBA[0] = r;
   fDefaultRGBA[1] = g;
   fDefaultRGBA[2] = b;
   fDefaultRGBA[3] = a;
}

////////////////////////////////////////////////////////////////////////////////
/// Set underflow color.

void TEveRGBAPalette::SetUnderColor(Color_t ci)
{
   fUnderColor = ci;
   TEveUtil::ColorFromIdx(ci, fUnderRGBA, kTRUE);
}

////////////////////////////////////////////////////////////////////////////////
/// Set underflow color.

void TEveRGBAPalette::SetUnderColorPixel(Pixel_t pix)
{
   SetUnderColor(Color_t(TColor::GetColor(pix)));
}

////////////////////////////////////////////////////////////////////////////////
/// Set underflow color.

void TEveRGBAPalette::SetUnderColorRGBA(UChar_t r, UChar_t g, UChar_t b, UChar_t a)
{
   fUnderColor = Color_t(TColor::GetColor(r, g, b));
   fUnderRGBA[0] = r;
   fUnderRGBA[1] = g;
   fUnderRGBA[2] = b;
   fUnderRGBA[3] = a;
}

////////////////////////////////////////////////////////////////////////////////
/// Set overflow color.

void TEveRGBAPalette::SetOverColor(Color_t ci)
{
   fOverColor = ci;
   TEveUtil::ColorFromIdx(ci, fOverRGBA, kTRUE);
}

////////////////////////////////////////////////////////////////////////////////
/// Set overflow color.

void TEveRGBAPalette::SetOverColorPixel(Pixel_t pix)
{
   SetOverColor(Color_t(TColor::GetColor(pix)));
}

////////////////////////////////////////////////////////////////////////////////
/// Set overflow color.

void TEveRGBAPalette::SetOverColorRGBA(UChar_t r, UChar_t g, UChar_t b, UChar_t a)
{
   fOverColor = Color_t(TColor::GetColor(r, g, b));
   fOverRGBA[0] = r;
   fOverRGBA[1] = g;
   fOverRGBA[2] = b;
   fOverRGBA[3] = a;
}

////////////////////////////////////////////////////////////////////////////////
/// Emit the "MinMaxValChanged()" signal.
/// This is NOT called automatically from SetMin/Max functions but
/// it IS called from TEveRGBAPaletteEditor after it changes the
/// min/max values.

void TEveRGBAPalette::MinMaxValChanged()
{
   Emit("MinMaxValChanged()");
}
