/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mvdColorDynamicsController.h"


/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdVectorImageModel.h"
#include "mvdColorDynamicsWidget.h"

namespace mvd
{
/*
  TRANSLATOR mvd::ColorDynamicsController

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/


/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*****************************************************************************/
ColorDynamicsController::ColorDynamicsController(ColorDynamicsWidget* widget, QObject* p) : AbstractModelController(widget, p)
{
}

/*****************************************************************************/
ColorDynamicsController::~ColorDynamicsController()
{
}

/*****************************************************************************/
void ColorDynamicsController::Connect(AbstractModel* model)
{
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();

  //
  // Connect GUI to controller.
  QObject::connect(colorDynamicsWidget, SIGNAL(LowQuantileChanged(RgbwChannel, double)),
                   // to:
                   this, SLOT(OnLowQuantileChanged(RgbwChannel, double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(HighQuantileChanged(RgbwChannel, double)),
                   // to:
                   this, SLOT(OnHighQuantileChanged(RgbwChannel, double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(LowIntensityChanged(RgbwChannel, double)),
                   // to:
                   this, SLOT(OnLowIntensityChanged(RgbwChannel, double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(HighIntensityChanged(RgbwChannel, double)),
                   // to:
                   this, SLOT(OnHighIntensityChanged(RgbwChannel, double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(ResetQuantileClicked(RgbwChannel)),
                   // to:
                   this, SLOT(OnResetQuantileClicked(RgbwChannel)));

  QObject::connect(colorDynamicsWidget, SIGNAL(ResetIntensityClicked(RgbwChannel)),
                   // to:
                   this, SLOT(OnResetIntensityClicked(RgbwChannel)));

  QObject::connect(colorDynamicsWidget, SIGNAL(ApplyAllClicked(RgbwChannel, double, double)),
                   // to:
                   this, SLOT(OnApplyAllClicked(RgbwChannel, double, double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(LinkToggled(RgbwChannel, bool)),
                   // to:
                   this, SLOT(OnLinkToggled(RgbwChannel, bool)));

  QObject::connect(colorDynamicsWidget, SIGNAL(NoDataFlagToggled(bool)),
                   // to:
                   this, SLOT(OnNoDataFlagToggled(bool)));

  QObject::connect(colorDynamicsWidget, SIGNAL(NoDataValueChanged(double)),
                   // to:
                   this, SLOT(OnNoDataValueChanged(double)));

  QObject::connect(colorDynamicsWidget, SIGNAL(NoDataButtonPressed()),
                   // to:
                   this, SLOT(RefreshHistogram()));

  QObject::connect(colorDynamicsWidget, SIGNAL(GammaValueChanged(double)),
                   // to:
                   this, SLOT(OnGammaValueChanged(double)));

  //
  // Connect controller to model.
  QObject::connect(this, SIGNAL(ModelUpdated()),
                   // to:
                   model, SLOT(OnModelUpdated()));
}

/*****************************************************************************/
void ColorDynamicsController::virtual_ResetWidget(bool isSettingModel)
{
  //
  // Reset color-dynamics widget mode (grayscale/RGB).
  assert(GetModel() != NULL && GetModel() == GetModel<VectorImageModel>());
  const VectorImageModel* model = GetModel<VectorImageModel>();

  assert(GetWidget() != NULL && GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* widget = GetWidget<ColorDynamicsWidget>();

  widget->SetGrayscaleActivated(model->GetSettings().IsGrayscaleActivated());

  RgbwChannel channels = RGBW_CHANNEL_ALL;
  /*
  model->GetSettings().IsGrayscaleActivated()
  ? RGBW_CHANNEL_WHITE
  : RGBW_CHANNEL_RGB;
  */

  //
  // Reset color-dynamics widget.

  // Set constrained editing ON/OFF.
  // (must be called before settings intensity/quantiles)
  SetBoundsEnabled(channels, isSettingModel);

  // Set ranges.
  ResetIntensityRanges(channels);

  // Set intensities based on applied quantiles (restoring
  // dataset-descriptor settings).
  SetIntensities(channels);

  // Setup no-data state.
  SetNoData();
  SetGamma();
}

/*****************************************************************************/
void ColorDynamicsController::ClearWidget()
{
  // qDebug() << this << "::ClearWidget()";

  assert(GetWidget() != NULL && GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* widget = GetWidget<ColorDynamicsWidget>();

  widget->SetGrayscaleActivated(true);

  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, RGBW_CHANNEL_ALL))
    return;

  // Block this controller's signals to prevent display refreshes
  // but let let widget(s) signal their changes so linked values
  // will be correctly updated.
  bool thisSignalsBlocked = this->blockSignals(true);
  {
    for (CountType i = begin; i < end; ++i)
    {
      RgbwChannel channel = static_cast<RgbwChannel>(i);

      ColorBandDynamicsWidget* colorBandDynWgt = widget->GetChannel(channel);


      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        colorBandDynWgt->SetBounded(true);
        colorBandDynWgt->SetLinkButtonEnabled(true);

        colorBandDynWgt->SetMinIntensity(0.0);
        colorBandDynWgt->SetMaxIntensity(1.0);

        colorBandDynWgt->SetLowIntensity(0.0);
        colorBandDynWgt->SetHighIntensity(1.0);

        colorBandDynWgt->SetLowQuantile(2.0);
        colorBandDynWgt->SetHighQuantile(2.0);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::Disconnect(AbstractModel* model)
{
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();

  //
  // Disconnect controller to model.
  QObject::disconnect(this, SIGNAL(ModelUpdated()),
                      // to:
                      model, SLOT(OnModelUpdated()));

  //
  // Disconnect GUI from controller.
  QObject::disconnect(colorDynamicsWidget, SIGNAL(LowQuantileChanged(RgbwChannel, double)),
                      // from:
                      this, SLOT(OnLowQuantileChanged(RgbwChannel, double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(HighQuantileChanged(RgbwChannel, double)),
                      // from:
                      this, SLOT(OnHighQuantileChanged(RgbwChannel, double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(LowIntensityChanged(RgbwChannel, double)),
                      // from:
                      this, SLOT(OnLowIntensityChanged(RgbwChannel, double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(HighIntensityChanged(RgbwChannel, double)),
                      // from:
                      this, SLOT(OnHighIntensityChanged(RgbwChannel, double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(ResetQuantileClicked(RgbwChannel)),
                      // from:
                      this, SLOT(OnResetQuantileClicked(RgbwChannel)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(ResetIntensityClicked(RgbwChannel)),
                      // from:
                      this, SLOT(OnResetIntensityClicked(RgbwChannel)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(ApplyAllClicked(RgbwChannel, double, double)),
                      // from:
                      this, SLOT(OnApplyAllClicked(RgbwChannel, double, double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(LinkToggled(RgbwChannel, bool)),
                      // to:
                      this, SLOT(OnLinkToggled(RgbwChannel, bool)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(NoDataFlagToggled(bool)),
                      // to:
                      this, SLOT(OnNoDataFlagToggled(bool)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(NoDataValueChanged(double)),
                      // to:
                      this, SLOT(OnNoDataValueChanged(double)));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(NoDataButtonPressed()),
                      // to:
                      this, SLOT(RefreshHistogram()));

  QObject::disconnect(colorDynamicsWidget, SIGNAL(GammaValueChanged(double)),
                      // to:
                      this, SLOT(OnGammaValueChanged(double)));
}

/*****************************************************************************/
void ColorDynamicsController::ResetIntensityRanges(RgbwChannel channels)
{
  // Trace.
  // qDebug()
  //   << this
  //   << "::ResetIntensityRanges(" << RGBW_CHANNEL_NAMES[ channels ] << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();

  //
  // Access image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  //
  // Access histogram from generic image-model.
  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  // if( !histogramModel->IsValid() )
  //   return;

  // Get min/max pixels.
  DefaultImageType::PixelType minPx(histogramModel->GetMinPixel());
  DefaultImageType::PixelType maxPx(histogramModel->GetMaxPixel());

  // Get image rengering settings.
  const VectorImageSettings& settings = imageModel->GetSettings();

  // Block this controller's signals to prevent display refreshes
  // but let let widget(s) signal their changes so linked values
  // will be correctly updated.
  bool thisSignalsBlocked = this->blockSignals(true);
  {
    for (CountType i = begin; i < end; ++i)
    {
      RgbwChannel channel = static_cast<RgbwChannel>(i);

      ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

      assert(std::numeric_limits<DefaultImageType::PixelType::ValueType>::has_infinity);

      DefaultImageType::PixelType::ValueType min(-std::numeric_limits<DefaultImageType::PixelType::ValueType>::infinity());
      DefaultImageType::PixelType::ValueType max(+std::numeric_limits<DefaultImageType::PixelType::ValueType>::infinity());

      if (colorBandDynWgt->IsBounded())
      {
        min = minPx[settings.GetRgbwChannel(channel)];
        max = maxPx[settings.GetRgbwChannel(channel)];
      }

      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // Set min/max intensity bounds.
        colorBandDynWgt->SetMinIntensity(min);
        colorBandDynWgt->SetMaxIntensity(max);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::ResetIntensities(RgbwChannel channels)
{
  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();

  //
  // Access image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  //
  // Access histogram from generic image-model.
  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  // Get min/max pixels.
  DefaultImageType::PixelType minPx(histogramModel->GetMinPixel());
  DefaultImageType::PixelType maxPx(histogramModel->GetMaxPixel());

  // Get image rengering settings.
  const VectorImageSettings& settings = imageModel->GetSettings();

  // Assign values to controlled widget.
  for (CountType i = begin; i < end; ++i)
  {
    RgbwChannel channel = static_cast<RgbwChannel>(i);

    ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

    DefaultImageType::PixelType::ValueType min(minPx[settings.GetRgbwChannel(channel)]);

    DefaultImageType::PixelType::ValueType max(maxPx[settings.GetRgbwChannel(channel)]);

    // Block this controller's signals to prevent display refreshes
    // but let let widget(s) signal their changes so linked values
    // will be correctly updated.
    bool thisSignalsBlocks = this->blockSignals(true);
    {
      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // Set low & high intensities.
        colorBandDynWgt->SetLowIntensity(min);
        OnLowIntensityChanged(channel, min);

        colorBandDynWgt->SetHighIntensity(max);
        OnHighIntensityChanged(channel, max);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
    this->blockSignals(thisSignalsBlocks);

    emit LowIntensityChanged(channel, min, false);
    emit HighIntensityChanged(channel, max, false);
  }
}

/*****************************************************************************/
void ColorDynamicsController::SetIntensities(RgbwChannel channels)
{
  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();

  //
  // Access image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  // Get image rengering settings.
  const VectorImageSettings& settings = imageModel->GetSettings();


  // Assign values to controlled widget.
  for (CountType i = begin; i < end; ++i)
  {
    RgbwChannel channel = static_cast<RgbwChannel>(i);

    ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

    ParametersType::ValueType low = settings.GetLowIntensity(channel);
    ParametersType::ValueType hi  = settings.GetHighIntensity(channel);

    // Block this controller's signals to prevent display refreshes
    // but let let widget(s) signal their changes so linked values
    // will be correctly updated.
    bool thisSignalsBlocked = this->blockSignals(true);
    {
      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // Set low & high intensities.
        colorBandDynWgt->SetLowIntensity(low);
        OnLowIntensityChanged(channel, low);

        colorBandDynWgt->SetHighIntensity(hi);
        OnHighIntensityChanged(channel, hi);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
    this->blockSignals(thisSignalsBlocked);

    emit LowIntensityChanged(channel, low, true);
    emit HighIntensityChanged(channel, hi, true);
  }
}

/*****************************************************************************/
void ColorDynamicsController::SetNoData()
{
  assert(GetModel() == GetModel<VectorImageModel>());
  const VectorImageModel* imageModel = GetModel<VectorImageModel>();

  if (imageModel == NULL)
    return;

  const ImageProperties* imageProperties = imageModel->GetProperties();
  assert(imageProperties != NULL);

  assert(GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* widget = GetWidget<ColorDynamicsWidget>();
  assert(widget != NULL);

  bool thisSignalsBlocked = this->blockSignals(true);
  {
    bool widgetSignalsBlocked = widget->blockSignals(true);
    {
      widget->SetNoDataButtonChecked(true);
      widget->SetNoDataChecked(imageProperties->IsNoDataEnabled());
      widget->SetNoDataValue(imageProperties->GetNoData());
    }
    widget->blockSignals(widgetSignalsBlocked);
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::SetGamma()
{
  assert(GetModel() == GetModel<VectorImageModel>());
  const VectorImageModel* imageModel = GetModel<VectorImageModel>();

  if (imageModel == NULL)
    return;

  assert(GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* widget = GetWidget<ColorDynamicsWidget>();
  assert(widget != NULL);

  bool thisSignalsBlocked = this->blockSignals(true);
  {
    bool widgetSignalsBlocked = widget->blockSignals(true);
    {
      widget->SetGamma(imageModel->GetSettings().GetGamma());
    }
    widget->blockSignals(widgetSignalsBlocked);
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::SetBoundsEnabled(RgbwChannel channels, bool isSettingModel)
{
  // qDebug()
  //   << this
  //   << "::SetBoundsEnabled(" << channels << "," << isSettingModel << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();
  assert(colorDynamicsWidget != NULL);

  //
  // Access image-model.
  const VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  //
  // Access histogram-model.
  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  bool hasValidHistogram = histogramModel->IsValid() && !histogramModel->IsMonoValue();

  // Block this controller's signals to prevent display refreshed
  // but let let widget(s) signal their changes so linked values
  // will be correctly updated.
  bool thisSignalsBlocked = this->blockSignals(true);
  {
    // Assign values to controlled widget.
    for (CountType i = begin; i < end; ++i)
    {
      RgbwChannel channel = static_cast<RgbwChannel>(i);

      ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // MANTIS-1144:
        //
        // SetBoundsEnabled() must keep track of previous state because it
        // is also called (circular loop) when editing quantile and/or
        // intensity is finished.
        //
        // {
        colorBandDynWgt->SetBounded((!isSettingModel ? colorBandDynWgt->IsBounded() : true) && hasValidHistogram);
        // }
        colorBandDynWgt->SetLinkButtonEnabled(hasValidHistogram);
        colorBandDynWgt->SetDefaultsButtonEnabled(hasValidHistogram);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::ResetQuantiles(RgbwChannel channels)
{
  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();
  assert(colorDynamicsWidget != NULL);

  // Block this controller's signals to prevent display refreshed
  // but let let widget(s) signal their changes so linked values
  // will be correctly updated.
  bool thisSignalsBlocked = this->blockSignals(true);
  {
    // Assign values to controlled widget.
    for (CountType i = begin; i < end; ++i)
    {
      RgbwChannel channel = static_cast<RgbwChannel>(i);

      ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        colorBandDynWgt->SetLowQuantile(2.0);
        OnLowQuantileChanged(channel, 2.0);

        colorBandDynWgt->SetHighQuantile(2.0);
        OnHighQuantileChanged(channel, 2.0);
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::RestoreQuantiles(RgbwChannel channels)
{
  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channels))
    return;

  //
  // Access color-dynamics widget.
  ColorDynamicsWidget* colorDynamicsWidget = GetWidget<ColorDynamicsWidget>();


  // Block this controller's signals to prevent display refreshed
  // but let let widget(s) signal their changes so linked values
  // will be correctly updated.
  bool thisSignalsBlocked = this->blockSignals(true);
  {
    // Assign values to controlled widget.
    for (CountType i = begin; i < end; ++i)
    {
      RgbwChannel channel = static_cast<RgbwChannel>(i);

      ColorBandDynamicsWidget* colorBandDynWgt = colorDynamicsWidget->GetChannel(channel);

      // Block widget's signals...
      //...but force call to valueChanged() slot to force refresh.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        OnLowQuantileChanged(channel, colorBandDynWgt->GetLowQuantile());
        OnHighQuantileChanged(channel, colorBandDynWgt->GetHighQuantile());
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }
  }
  this->blockSignals(thisSignalsBlocked);
}

/*****************************************************************************/
void ColorDynamicsController::RefreshHistogram()
{
  // Trace.
  // qDebug()
  //   << this
  //   << "::RefreshHistogram()";

  // Get image-model.
  assert(GetModel() == GetModel<VectorImageModel>());
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  // Refresh histogram-model.
  imageModel->RefreshHistogram();

  // Refresh color-dynamics.
  {
    assert(imageModel->GetHistogramModel() != NULL);

    SetBoundsEnabled(RGBW_CHANNEL_ALL, true);

    if (imageModel->GetHistogramModel()->IsValid())
      ResetQuantiles(RGBW_CHANNEL_ALL);
    else
      ResetIntensities(RGBW_CHANNEL_ALL);
  }

  // Signal histogram has been refreshed.
  emit HistogramRefreshed();

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
/* SLOTS                                                                     */
/*****************************************************************************/
void ColorDynamicsController::OnRgbChannelIndexChanged(RgbwChannel channel, int)
{
  // qDebug()
  //   << this
  //   << "::OnRgbChannelIndexChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << band <<
  //   ")";

  //
  // Reset color-dynamics widget.
  ResetQuantiles(channel);

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnGrayChannelIndexChanged(int)
{
  // qDebug()
  //   << this
  //   << "::OnGrayChannelIndexChanged(" << band << ")";

  //
  // Reset color-dynamics widget.
  ResetQuantiles(RGBW_CHANNEL_WHITE);

  // Signal model has been updated.
  emit ModelUpdated();
}

/*******************************************************************************/
void ColorDynamicsController::OnGrayscaleActivated(bool activated)
{
  // qDebug()
  //   << this
  //   << "::OnGrayscaleActivated(" << activated << ")";

  //
  // Toggle grayscale-mode activation-state of controlled widget.
  GetWidget<ColorDynamicsWidget>()->SetGrayscaleActivated(activated);

  //
  // Reset color-dynamics widget.
  ResetQuantiles(activated ? RGBW_CHANNEL_WHITE : RGBW_CHANNEL_RGB);

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnLowQuantileChanged(RgbwChannel channel, double value)
{
  // qDebug()
  //   << this
  //   << "::OnLowQuantileChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << value
  //   << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channel))
    return;

  // Get image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  if (!histogramModel->IsValid())
    return;

  // Reference settings.
  VectorImageSettings& settings = imageModel->GetSettings();

  // Get color-dynamics widgets.
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt != NULL);

  // Update quantile intensity in model.
  for (CountType i = begin; i < end; ++i)
  {
    // RGB/W Channel.
    RgbwChannel chan = static_cast<RgbwChannel>(i);

    // Calculate quantile intensity.
    HistogramModel::MeasurementType intensity = histogramModel->Quantile(settings.GetRgbwChannel(chan), 0.01 * value, BOUND_LOWER);

    // Set intensity.
    settings.SetLowIntensity(chan, intensity);

    // Access color-band widget.
    ColorBandDynamicsWidget* colorBandDynWgt = colorDynWgt->GetChannel(chan);

    // check pointer.
    assert(colorBandDynWgt != NULL);

    // Block widget signals to prevent recursive signal/slot loops.
    bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
    {
      // Refresh low-intensity display.
      colorBandDynWgt->SetLowIntensity(intensity);
    }
    colorBandDynWgt->blockSignals(widgetSignalsBlocked);

    emit LowIntensityChanged(chan, intensity, true);
  }

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnHighQuantileChanged(RgbwChannel channel, double value)
{
  // qDebug()
  //   << this
  //   << "::OnHighQuantileChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << value
  //   << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channel))
    return;

  // Get image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  if (!histogramModel->IsValid())
    return;

  // Reference settings.
  VectorImageSettings& settings = imageModel->GetSettings();

  // Get color-dynamics widgets.
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt != NULL);

  // Update quantile intensity in model.
  for (CountType i = begin; i < end; ++i)
  {
    // RGB/W Channel.
    RgbwChannel chan = static_cast<RgbwChannel>(i);

    // Calculate quantile intensity.
    HistogramModel::MeasurementType intensity = histogramModel->Quantile(settings.GetRgbwChannel(chan), 0.01 * value, BOUND_UPPER);

    // Set intensity.
    settings.SetHighIntensity(chan, intensity);

    // Access color-band widget.
    ColorBandDynamicsWidget* colorBandDynWgt = colorDynWgt->GetChannel(chan);

    // check pointer.
    assert(colorBandDynWgt != NULL);

    // Block widget signals to prevent recursive signal/slot loops.
    bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
    {
      // Refresh high-intensity display.
      colorBandDynWgt->SetHighIntensity(intensity);
    }
    widgetSignalsBlocked = colorBandDynWgt->blockSignals(widgetSignalsBlocked);

    emit HighIntensityChanged(chan, intensity, true);
  }

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnLowIntensityChanged(RgbwChannel channel, double value)
{
  // qDebug()
  //   << this
  //   << "::OnLowIntensityChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << value
  //   << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channel))
    return;

  // Get image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  // if( !histogramModel->IsValid() )
  //   return;

  // Get color-dynamics widgets.
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt);

  // Update parameter value.
  for (CountType i = begin; i < end; ++i)
  {
    RgbwChannel chan = static_cast<RgbwChannel>(i);

    imageModel->GetSettings().SetLowIntensity(chan, value);

    ColorBandDynamicsWidget* colorBandDynWgt = colorDynWgt->GetChannel(chan);

    if (colorBandDynWgt->IsBounded() && histogramModel->IsValid())
    {
      // Block widget signals to prevent recursive signal/slot loops.
      bool wgtSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // Refresh quantile display.
        colorBandDynWgt->SetLowQuantile(100.0 * histogramModel->Percentile(imageModel->GetSettings().GetRgbwChannel(chan), value, BOUND_LOWER));
      }
      wgtSignalsBlocked = colorBandDynWgt->blockSignals(wgtSignalsBlocked);
    }

    emit LowIntensityChanged(chan, value, true);
  }

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnHighIntensityChanged(RgbwChannel channel, double value)
{
  // qDebug()
  //   << this
  //   << "::OnHighIntensityChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << value
  //   << ")";

  //
  // Calculate loop bounds. Return if nothing to do.
  CountType begin = -1;
  CountType end   = -1;

  if (!RgbwBounds(begin, end, channel))
    return;

  // Get image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  // if( !histogramModel->IsValid() )
  //   return;

  // Get color-dynamics widgets.
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt);

  // Update parameter value.
  for (CountType i = begin; i < end; ++i)
  {
    RgbwChannel chan = static_cast<RgbwChannel>(i);

    imageModel->GetSettings().SetHighIntensity(chan, value);

    ColorBandDynamicsWidget* colorBandDynWgt = colorDynWgt->GetChannel(chan);

    if (colorBandDynWgt->IsBounded() && histogramModel->IsValid())
    {
      // Block widget signals to prevent recursive signal/slot loops.
      bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
      {
        // Refresh quantile display.
        colorBandDynWgt->SetHighQuantile(100.0 *
                                         imageModel->GetHistogramModel()->Percentile(imageModel->GetSettings().GetRgbwChannel(chan), value, BOUND_UPPER));
      }
      colorBandDynWgt->blockSignals(widgetSignalsBlocked);
    }

    emit HighIntensityChanged(chan, value, true);
  }

  // Signal model has been updated.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnResetIntensityClicked(RgbwChannel channel)
{
  // qDebug()
  //   << this
  //   << "::OnResetIntensityClicked(" << RGBW_CHANNEL_NAMES[ channel ] << ")";

  ResetIntensities(channel);

  // Emit signal to force histogram refresh.
  emit HistogramRefreshed();

  // Now, emit this controller's signal to cause display refresh.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnResetQuantileClicked(RgbwChannel channel)
{
  // qDebug()
  //   << this
  //   << "::OnResetQuantileChanged(" << RGBW_CHANNEL_NAMES[ channel ] << ")";

  ResetQuantiles(channel);

  // Emit signal to force histogram refresh.
  emit HistogramRefreshed();

  // Now, emit this controller's signal to cause display refresh.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnApplyAllClicked(RgbwChannel, double low, double high)
{
  // qDebug()
  //   << this
  //   << "::OnApplyAllChanged("
  //   << RGBW_CHANNEL_NAMES[ channel ] << "," << low << "," << high << ")";

  // Get image-model.
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  const HistogramModel* histogramModel = imageModel->GetHistogramModel();
  assert(histogramModel != NULL);

  if (!histogramModel->IsValid())
    return;

  // Reference settings.
  VectorImageSettings& settings = imageModel->GetSettings();

  CountType begin;
  CountType end;

  if (!RgbBounds(begin, end, settings.IsGrayscaleActivated() ? RGBW_CHANNEL_WHITE : RGBW_CHANNEL_RGB))
    return;

  // Get color-dynamics widgets.
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt != NULL);

  for (CountType i = begin; i < end; ++i)
  {
    RgbwChannel chan = static_cast<RgbwChannel>(i);

    VectorImageSettings::ChannelVector::value_type band(settings.GetRgbwChannel(chan));

    HistogramModel::MeasurementType lintensity = histogramModel->Quantile(band, 0.01 * low, BOUND_LOWER);

    // Calculate quantile intensity.
    HistogramModel::MeasurementType uintensity = histogramModel->Quantile(band, 0.01 * high, BOUND_UPPER);

    // Update quantile intensity in model.
    settings.SetLowIntensity(chan, lintensity);
    settings.SetHighIntensity(chan, uintensity);

    ColorBandDynamicsWidget* colorBandDynWgt = colorDynWgt->GetChannel(chan);
    assert(colorBandDynWgt != NULL);

    // Block widget signals to prevent recursive signal/slot loops.
    bool widgetSignalsBlocked = colorBandDynWgt->blockSignals(true);
    {
      // Refresh intensities display.
      // TODO: Remove SetLow/HighIntensity(): overwrite by SetLow/HighQuantiles().
      colorBandDynWgt->SetHighIntensity(uintensity);
      colorBandDynWgt->SetLowIntensity(lintensity);

      // Regresh quantile display.
      colorBandDynWgt->SetLowQuantile(low);
      colorBandDynWgt->SetHighQuantile(high);
    }
    colorBandDynWgt->blockSignals(widgetSignalsBlocked);

    emit LowIntensityChanged(chan, lintensity, false);
    emit HighIntensityChanged(chan, uintensity, false);
  }

  // Force histogram refresh.
  emit HistogramRefreshed();

  // Now, emit this controller's signal to cause display refresh.
  emit ModelUpdated();
}

/*****************************************************************************/
void ColorDynamicsController::OnNoDataFlagToggled(bool enabled)
{
  // Trace.
  // qDebug()
  //   << this
  //   << "::OnNoDataFlagToggled(" << enabled << ")";

  // Get image-model.
  assert(GetModel() == GetModel<VectorImageModel>());
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  // Reference settings.
  ImageProperties* properties = imageModel->GetProperties();

  // Get color-dynamics widgets.
  assert(GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt != NULL);

  // Store property.
  // properties->SetNoDataEnabled( colorDynWgt->IsNoDataChecked() );
  properties->SetNoDataEnabled(enabled);

  //
  colorDynWgt->SetNoDataButtonChecked(false);
}

/*****************************************************************************/
void ColorDynamicsController::OnNoDataValueChanged(double value)
{
  // Trace.
  // qDebug()
  //   << this
  //   << "::OnNoDataValueChanged(" << value << ")";

  // Get image-model.
  assert(GetModel() == GetModel<VectorImageModel>());
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  // Reference settings.
  ImageProperties* properties = imageModel->GetProperties();

  // Get color-dynamics widgets.
  assert(GetWidget() == GetWidget<ColorDynamicsWidget>());
  ColorDynamicsWidget* colorDynWgt = GetWidget<ColorDynamicsWidget>();
  assert(colorDynWgt != NULL);

  // Store property.
  // properties->SetNoData( colorDynWgt->GetNoDataValue() );
  properties->SetNoData(value);

  //
  colorDynWgt->SetNoDataButtonChecked(false);
}

/*****************************************************************************/
void ColorDynamicsController::OnLinkToggled(RgbwChannel channel, bool checked)
{
  // Trace.
  // qDebug()
  //   << this
  //   << "::OnLinkToggled("
  //   << RGBW_CHANNEL_NAMES[ channel ] << ", " << checked
  //   << ")";

  ResetIntensityRanges(channel);

  if (checked)
  {
    RestoreQuantiles(channel);

    emit ModelUpdated();
  }
}

/*****************************************************************************/
void ColorDynamicsController::OnGammaValueChanged(double value)
{
  // qDebug()
  //   << this
  //   << "::OnGammaValueChanged("
  //   << value
  //   <<")";

  // Get image-model.
  assert(GetModel() == GetModel<VectorImageModel>());
  VectorImageModel* imageModel = GetModel<VectorImageModel>();
  assert(imageModel != NULL);

  // Get widget.
  // assert( GetWidget()==GetWidget< ColorDynamicsWidget >() );
  // ColorDynamicsWidget* widget = GetWidget< ColorDynamicsWidget >();
  // assert( widget!=NULL );

  // Store gamma into settings.
  // imageModel->GetSettings().SetGamma( widget->GetGamma() );
  imageModel->GetSettings().SetGamma(value);

  // Emit refresh signal.
  emit ModelUpdated();
}

/*****************************************************************************/

} // end namespace 'mvd'
