

//#include "trop.h"
#include "tfxparam.h"
#include <math.h>
#include "stdfx.h"
#include "hsvutil.h"
#include "globalcontrollablefx.h"

class HSVScaleFx final : public GlobalControllableFx {
  FX_PLUGIN_DECLARATION(HSVScaleFx)

  TRasterFxPort m_input;
  TDoubleParamP m_hue;
  TDoubleParamP m_sat;
  TDoubleParamP m_value;
  TDoubleParamP m_hueScale;
  TDoubleParamP m_satScale;
  TDoubleParamP m_valueScale;

public:
  HSVScaleFx()
      : m_hue(0.0)
      , m_sat(0.0)
      , m_value(0.0)
      , m_hueScale(100.0)
      , m_satScale(100.0)
      , m_valueScale(100.0)

  {
    bindParam(this, "hue", m_hue);
    bindParam(this, "saturation", m_sat);
    bindParam(this, "value", m_value);
    bindParam(this, "hue_scale", m_hueScale);
    bindParam(this, "saturation_scale", m_satScale);
    bindParam(this, "value_scale", m_valueScale);
    m_hue->setValueRange(-180, 180);
    m_sat->setValueRange(-1, 1);
    m_value->setValueRange(-1, 1);
    m_hueScale->setValueRange(0, (std::numeric_limits<double>::max)());
    m_satScale->setValueRange(0, (std::numeric_limits<double>::max)());
    m_valueScale->setValueRange(0, (std::numeric_limits<double>::max)());
    addInputPort("Source", m_input);
  }

  ~HSVScaleFx(){};

  bool doGetBBox(double frame, TRectD &bBox,
                 const TRenderSettings &info) override {
    if (m_input.isConnected()) {
      m_input->doGetBBox(frame, bBox, info);
      return true;
    }

    return false;
  };

  void doCompute(TTile &tile, double frame, const TRenderSettings &) override;

  bool canHandle(const TRenderSettings &info, double frame) override {
    return true;
  }
};

template <typename PIXEL, typename CHANNEL_TYPE>
void doHSVScale(const TRasterPT<PIXEL> &ras, double hue, double sat,
                double value, double hueScale, double satScale,
                double valueScale) {
  int j;
  ras->lock();
  for (j = 0; j < ras->getLy(); j++) {
    PIXEL *pix    = ras->pixels(j);
    PIXEL *endPix = pix + ras->getLx();

    for (; pix < endPix; ++pix) {
      if (pix->m == 0) continue;

      double m = pix->m;

      double r, g, b, h, s, v;
      // depremult(pix);
      r = pix->r / m;
      g = pix->g / m;
      b = pix->b / m;
      OLDRGB2HSV(r, g, b, &h, &s, &v);
      /*     int hsv[3];
rgb2hsv(hsv, *pix);
h =((double)hsv[0]/255.)*360.;
s =hsv[1]/255.;
v =hsv[2]/255.;*/
      h += hue;
      s += sat;
      v += value;
      h *= hueScale;
      s *= satScale;
      v *= valueScale;
      /*     hsv[0]=tcrop((int)((h/360.)*255.),0,255);
hsv[1]=tcrop((int)s*255, 0,255);
hsv[2]=tcrop((int)v*255, 0,255);
hsv2rgb(*pix,hsv);*/

      OLDHSV2RGB(h, s, v, &r, &g, &b);
      pix->r = (CHANNEL_TYPE)(r * m);
      pix->g = (CHANNEL_TYPE)(g * m);
      pix->b = (CHANNEL_TYPE)(b * m);
      // premultiply(*pix);
    }
  }
  ras->unlock();
}
//------------------------------------------------------------------------------

void HSVScaleFx::doCompute(TTile &tile, double frame,
                           const TRenderSettings &ri) {
  if (!m_input.isConnected()) return;

  m_input->compute(tile, frame, ri);

  double hue        = m_hue->getValue(frame);
  double sat        = m_sat->getValue(frame);
  double value      = m_value->getValue(frame);
  double hueScale   = m_hueScale->getValue(frame) / 100.0;
  double satScale   = m_satScale->getValue(frame) / 100.0;
  double valueScale = m_valueScale->getValue(frame) / 100.0;

  TRaster32P raster32 = tile.getRaster();

  if (raster32)
    doHSVScale<TPixel32, UCHAR>(raster32, hue, sat, value, hueScale, satScale,
                                valueScale);
  else {
    TRaster64P raster64 = tile.getRaster();
    if (raster64)
      doHSVScale<TPixel64, USHORT>(raster64, hue, sat, value, hueScale,
                                   satScale, valueScale);
    else
      throw TException("HSVScale: unsupported Pixel Type");
  }
}

//------------------------------------------------------------------

FX_PLUGIN_IDENTIFIER(HSVScaleFx, "hsvScaleFx")
