

#include "tbasefx.h"
#include "trop.h"
#include "tdoubleparam.h"
#include "tnotanimatableparam.h"
#include "trasterfx.h"
#include "tfxparam.h"
#include "tparamset.h"

// #define ALLOW_SHEAR

//==============================================================================

TGeometryFx::TGeometryFx() {
  setName(L"Geometry");
  enableComputeInFloat(true);
}

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

void TGeometryFx::doCompute(TTile &tile, double frame,
                            const TRenderSettings &ri) {
  TRasterFxPort *input = dynamic_cast<TRasterFxPort *>(getInputPort(0));
  assert(input);

  if (!input->isConnected()) return;

  if (!getActiveTimeRegion().contains(frame)) {
    TRasterFxP(input->getFx())->compute(tile, frame, ri);
    return;
  }

  if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster()) &&
      !TRasterFP(tile.getRaster()))
    throw TException("AffineFx unsupported pixel type");

  TAffine aff1 = getPlacement(frame);
  TRenderSettings ri2(ri);
  ri2.m_affine = ri2.m_affine * aff1;

  TRasterFxP src = getInputPort("source")->getFx();
  src->compute(tile, frame, ri2);
  return;
}

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

bool TGeometryFx::doGetBBox(double frame, TRectD &bBox,
                            const TRenderSettings &info) {
  TRasterFxPort *input = dynamic_cast<TRasterFxPort *>(getInputPort(0));
  assert(input);

  if (input->isConnected()) {
    TRasterFxP fx = input->getFx();
    assert(fx);
    bool ret = fx->doGetBBox(frame, bBox, info);
    if (getActiveTimeRegion().contains(frame))
      bBox = getPlacement(frame) * bBox;
    return ret;
  } else {
    bBox = TRectD();
    return false;
  }
  return true;
};

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

std::string TGeometryFx::getAlias(double frame,
                                  const TRenderSettings &info) const {
  TGeometryFx *tthis = const_cast<TGeometryFx *>(this);
  TAffine affine     = tthis->getPlacement(frame);

  std::string alias = getFxType();
  alias += "[";

  // alias degli effetti connessi alle porte di input separati da virgole
  // una porta non connessa da luogo a un alias vuoto (stringa vuota)

  for (int i = 0; i < getInputPortCount(); ++i) {
    TFxPort *port = getInputPort(i);
    if (port->isConnected()) {
      TRasterFxP ifx = port->getFx();
      assert(ifx);
      alias += ifx->getAlias(frame, info);
    }
    alias += ",";
  }

  return alias +
         (areAlmostEqual(affine.a11, 0) ? "0" : ::to_string(affine.a11, 5)) +
         "," +
         (areAlmostEqual(affine.a12, 0) ? "0" : ::to_string(affine.a12, 5)) +
         "," +
         (areAlmostEqual(affine.a13, 0) ? "0" : ::to_string(affine.a13, 5)) +
         "," +
         (areAlmostEqual(affine.a21, 0) ? "0" : ::to_string(affine.a21, 5)) +
         "," +
         (areAlmostEqual(affine.a22, 0) ? "0" : ::to_string(affine.a22, 5)) +
         "," +
         (areAlmostEqual(affine.a23, 0) ? "0" : ::to_string(affine.a23, 5)) +
         "]";
}

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

void TGeometryFx::transform(double frame, int port, const TRectD &rectOnOutput,
                            const TRenderSettings &infoOnOutput,
                            TRectD &rectOnInput, TRenderSettings &infoOnInput) {
  rectOnInput = rectOnOutput;
  TAffine aff = getPlacement(frame);

  infoOnInput          = infoOnOutput;
  infoOnInput.m_affine = infoOnInput.m_affine * aff;
}

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

bool TGeometryFx::toBeComputedInLinearColorSpace(bool settingsIsLinear,
                                                 bool tileIsLinear) const {
  return tileIsLinear;
}

//==================================================

NaAffineFx::NaAffineFx(bool isDpiAffine)
    : m_aff(TAffine()), m_isDpiAffine(isDpiAffine) {
  addInputPort("source", m_port);
  setName(L"Geometry-NaAffineFx");
}

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

TFx *NaAffineFx::clone(bool recursive) const {
  NaAffineFx *clonedFx = dynamic_cast<NaAffineFx *>(TFx::clone(recursive));
  assert(clonedFx);
  clonedFx->m_aff         = m_aff;
  clonedFx->m_isDpiAffine = m_isDpiAffine;
  return clonedFx;
}

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

void NaAffineFx::doDryCompute(TRectD &rect, double frame,
                              const TRenderSettings &info) {
  // keep the level column dpi affine for an fx which need to be applied to the
  // full sampled image (see Iwa_SmootherFx)
  if (getName() == L"LevelColumn AffineFx") {
    for (auto data : info.m_data) {
      LevelColumnAffineFxRenderData *levelColumnData =
          dynamic_cast<LevelColumnAffineFxRenderData *>(data.getPointer());
      if (levelColumnData && levelColumnData->m_isSet == false) {
        levelColumnData->m_aff   = getPlacement(frame);
        levelColumnData->m_isSet = true;
      }
    }
  }
  TRasterFx::doDryCompute(rect, frame, info);
}

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

FX_IDENTIFIER_IS_HIDDEN(NaAffineFx, "naAffineFx")

//==================================================================
// LevelColumnAffineFxRenderData implementation
//==================================================================

bool LevelColumnAffineFxRenderData::operator==(
    const TRasterFxRenderData &data) const {
  const LevelColumnAffineFxRenderData *theData =
      dynamic_cast<const LevelColumnAffineFxRenderData *>(&data);
  return theData->m_aff == m_aff && theData->m_isSet == m_isSet;
}

//==================================================================
//  ColumnColorFilterFx
//==================================================================

ColumnColorFilterFx::ColumnColorFilterFx() : m_colorFilter(TPixel::Black) {
  setName(L"ColumnColorFilterFx");
  addInputPort("source", m_port);
}

bool ColumnColorFilterFx::doGetBBox(double frame, TRectD &bBox,
                                    const TRenderSettings &info) {
  if (!m_port.isConnected()) return false;
  TRasterFxP fx = m_port.getFx();
  assert(fx);
  bool ret = fx->doGetBBox(frame, bBox, info);
  return ret;
}

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

  if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster()))
    throw TException("AffineFx unsupported pixel type");

  TRasterFxP src = m_port.getFx();
  src->compute(tile, frame, ri);

  TRop::applyColorScale(tile.getRaster(), m_colorFilter);
}

std::string ColumnColorFilterFx::getAlias(double frame,
                                          const TRenderSettings &info) const {
  std::string alias = getFxType();
  alias += "[";
  if (m_port.isConnected()) {
    TRasterFxP ifx = m_port.getFx();
    assert(ifx);
    alias += ifx->getAlias(frame, info);
  }
  alias += ",";

  return alias + std::to_string(m_colorFilter.r) + "," +
         std::to_string(m_colorFilter.g) + "," +
         std::to_string(m_colorFilter.b) + "," +
         std::to_string(m_colorFilter.m) + "]";
}

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

FX_IDENTIFIER_IS_HIDDEN(ColumnColorFilterFx, "columnColorFilterFx")

//==================================================================
//  Geometric Fx
//==================================================================

//==================================================================

//==================================================================

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

class InvertFx final : public TBaseRasterFx {
  FX_DECLARATION(InvertFx)
  TRasterFxPort m_input;
  TBoolParamP m_redChan, m_greenChan, m_blueChan, m_alphaChan;

public:
  InvertFx()
      : m_redChan(true)
      , m_greenChan(true)
      , m_blueChan(true)
      , m_alphaChan(false) {
    addInputPort("source", m_input);
    //   bindParam(this, "red_channel"  , m_redChan);
    //   bindParam(this, "green_channel", m_greenChan);
    //   bindParam(this, "blue_channel" , m_blueChan);
    //   bindParam(this, "alpha_channel", m_alphaChan);
    setName(L"InvertFx");
    enableComputeInFloat(true);
  };

  ~InvertFx() {};

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

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

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

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

    TRop::invert(tile.getRaster(), m_redChan->getValue(),
                 m_greenChan->getValue(), m_blueChan->getValue(),
                 m_alphaChan->getValue());
  }
};

//==================================================================
//  Video Fx
//==================================================================

//==================================================================

//===

// Geometric
// FX_IDENTIFIER(ScaleFx,       "scaleFx")
// FX_IDENTIFIER(MoveFx,        "moveFx")
// FX_IDENTIFIER(AffineFx,      "affineFx")
// FX_IDENTIFIER(CropFx,        "cropFx")

// Color
FX_IDENTIFIER(InvertFx, "invertFx")

// Video
// FX_IDENTIFIER(FieldFx,       "fieldFx")
// FX_IDENTIFIER(SwapFieldsFx,  "swapFieldsFx")
// FX_IDENTIFIER(DeInterlaceFx, "deInterlaceFx")
// FX_IDENTIFIER(InterlaceFx,   "interlaceFx")
