/*
 * 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 "mvdImageViewManipulator.h"

//
// 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 "mvdGui.h"
#include "mvdImageViewRenderer.h"

namespace mvd
{
/*
  TRANSLATOR mvd::ImageViewManipulator

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

  Context comment for translator.
*/


/*****************************************************************************/
/* CONSTANTS                                                                 */

const int    ImageViewManipulator::DEFAULT_GRANULARITY                = 1;
const int    ImageViewManipulator::DEFAULT_ALPHA_GRANULARITY          = 10;
const double ImageViewManipulator::DEFAULT_DYNAMICS_SHIFT_GRANULARITY = 0.01;
const int    ImageViewManipulator::DEFAULT_SCROLL_GRANULARITY         = 4;
const int    ImageViewManipulator::DEFAULT_ZOOM_GRANULARITY           = 2;

const double ImageViewManipulator::DEFAULT_DELTA = 0.1;

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */
/*****************************************************************************/

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */
/*****************************************************************************/
#if USE_VIEW_SETTINGS_SIDE_EFFECT

ImageViewManipulator::ImageViewManipulator(const otb::ViewSettings::Pointer& viewSettings, QObject* p)
  : AbstractImageViewManipulator(p),
    m_MousePressPosition(),
    m_ViewSettings(viewSettings),
    m_Timer(NULL),
    m_NativeSpacing(),
    m_MousePressOrigin(),
    m_RenderMode(AbstractImageViewRenderer::RenderingContext::RENDER_MODE_FULL),
    m_ZoomFactor(1.0),
    m_AlphaGranularity(ImageViewManipulator::DEFAULT_ALPHA_GRANULARITY),
    m_DynamicsShiftGranularity(ImageViewManipulator::DEFAULT_DYNAMICS_SHIFT_GRANULARITY),
    m_ScrollGranularity(ImageViewManipulator::DEFAULT_SCROLL_GRANULARITY),
    m_ZoomGranularity(ImageViewManipulator::DEFAULT_ZOOM_GRANULARITY),
    m_IsMouseDragging(false)
{
  m_NativeSpacing.Fill(1.0);
}

#else // USE_VIEW_SETTINGS_SIDE_EFFECT

ImageViewManipulator::ImageViewManipulator(QObject* p)
  : AbstractImageViewManipulator(p),
    m_MousePressPosition(),
    m_ViewSettings(otb::ViewSettings::New()),
    m_Timer(NULL),
    m_NativeSpacing(),
    m_MousePressOrigin(),
    m_RenderMode(AbstractImageViewRenderer::RenderingContext::RENDER_MODE_FULL),
    m_ZoomFactor(1.0),
    m_AlphaGranularity(ImageViewManipulator::DEFAULT_ALPHA_GRANULARITY),
    m_DynamicsShiftGranularity(ImageViewManipulator::DEFAULT_DYNAMICS_GRANULARITY),
    m_ScrollGranularity(ImageViewManipulator::DEFAULT_SCROLL_GRANULARITY),
    m_ZoomGranularity(ImageViewManipulator::DEFAULT_ZOOM_GRANULARITY),
    m_IsMouseDragging(false)
{
  m_NativeSpacing.Fill(1.0);
}

#endif // USE_VIEW_SETTINGS_SIDE_EFFECT

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

/******************************************************************************/
void ImageViewManipulator::SetViewportSize(int width, int height)
{
  SizeType size;

  size[0] = width;
  size[1] = height;

  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->SetViewportSize(size);
}

/******************************************************************************/
SizeType ImageViewManipulator::GetViewportSize() const
{
  assert(!m_ViewSettings.IsNull());

  return m_ViewSettings->GetViewportSize();
}

/******************************************************************************/
void ImageViewManipulator::SetOrigin(const PointType& origin)
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->SetOrigin(origin);
}

/******************************************************************************/
PointType ImageViewManipulator::GetOrigin() const
{
  assert(!m_ViewSettings.IsNull());

  return m_ViewSettings->GetOrigin();
}

/******************************************************************************/
void ImageViewManipulator::SetSpacing(const SpacingType& spacing)
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->SetSpacing(spacing);
}

/******************************************************************************/
SpacingType ImageViewManipulator::GetSpacing() const
{
  assert(!m_ViewSettings.IsNull());

  return m_ViewSettings->GetSpacing();
}

/******************************************************************************/
void ImageViewManipulator::SetNativeSpacing(const SpacingType& spacing)
{
  m_NativeSpacing = spacing;
}

/******************************************************************************/
void ImageViewManipulator::SetWkt(const std::string& wkt)
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->SetWkt(wkt);
}

/******************************************************************************/
void ImageViewManipulator::SetKeywordList(const DefaultImageType::ImageKeywordlistType& kwl)
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->SetKeywordList(kwl);
}

/******************************************************************************/
PointType ImageViewManipulator::GetCenter() const
{
  assert(!m_ViewSettings.IsNull());

  return m_ViewSettings->GetViewportCenter();
}

/******************************************************************************/
void ImageViewManipulator::SetupRenderingContext(AbstractImageViewRenderer::RenderingContext* const c) const
{
  assert(c == dynamic_cast<ImageViewRenderer::RenderingContext const*>(c));

  ImageViewRenderer::RenderingContext* const context = dynamic_cast<ImageViewRenderer::RenderingContext* const>(c);

  // Coverity-19840
  // {
  assert(context != NULL);
  // }

  context->m_RenderMode = m_IsMouseDragging ? AbstractImageViewRenderer::RenderingContext::RENDER_MODE_LIGHT : m_RenderMode;

#if USE_VIEW_SETTINGS_SIDE_EFFECT
#else  // USE_VIEW_SETTINGS_SIDE_EFFECT
  context->m_ViewSettings->SetOrigin(m_ViewSettings->GetOrigin());
  context->m_ViewSettings->SetSpacing(m_ViewSettings->GetSpacing());
  context->m_ViewSettings->SetViewportSize(m_ViewSettings->GetViewportSize());
  context->m_ViewSettings->SetWkt(m_ViewSettings->GetWkt());
  context->m_ViewSettings->SetKeywordList(m_ViewSettings->GetKeywordList());
  context->m_ViewSettings->SetUseProjection(m_ViewSettings->GetUseProjection());
  context->m_ViewSettings->SetGeometryChanged(m_ViewSettings->GetGeometryChanged());
#endif // USE_VIEW_SETTINGS_SIDE_EFFECT
}

/******************************************************************************/
void ImageViewManipulator::CenterOn(const PointType& point)
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->Center(point);

  emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), point);
}

/******************************************************************************/
void ImageViewManipulator::ZoomTo(double scale)
{
  // qDebug() << this << "::ZoomTo(" << scale << ")";

  assert(scale != 0.0);

  assert(!m_ViewSettings.IsNull());

  // Remember center of viewport.
  otb::ViewSettings::PointType center(m_ViewSettings->GetViewportCenter());

// Calculate spacing based on scale.
#if 0
  otb::ViewSettings::SpacingType spacing( m_ViewSettings->GetSpacing() );

  spacing[ 0 ] = ( spacing[ 0 ]>0.0 ? 1.0 : -1.0 ) / scale;
  spacing[ 1 ] = ( spacing[ 1 ]>0.0 ? 1.0 : -1.0 ) / scale;
#else
  otb::ViewSettings::SpacingType spacing(m_NativeSpacing);

  // Here, zoom to arbitrary scale-factor relative to
  // viewport spacing.
  //
  // If viewport spacing has previously been set to
  // image-spacing, it zooms to arbitrary scale-factor.
  //
  // This is especially useful to set user-arbitrary scale level
  // such as when editing scale-level in status-bar.
  spacing[0] /= scale;
  spacing[1] /= scale;
#endif

  // Change spacing and center.
  m_ViewSettings->SetSpacing(spacing);
  m_ViewSettings->Center(center);

  // Emit ROI changed.
  emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), center);

  // emit RenderingContextChanged(center,GetSpacing()[0]);
}

/******************************************************************************/
void ImageViewManipulator::ZoomIn()
{
  otb::ViewSettings::SizeType size(GetViewportSize());

  PointType point;

  Scale(QPoint(size[0] / 2, size[1] / 2), m_ZoomGranularity * MOUSE_WHEEL_STEP_DEGREES, &point);

  emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), point);
}

/******************************************************************************/
void ImageViewManipulator::ZoomOut()
{
  otb::ViewSettings::SizeType size(GetViewportSize());

  PointType point;

  Scale(QPoint(size[0] / 2, size[1] / 2), -m_ZoomGranularity * MOUSE_WHEEL_STEP_DEGREES, &point);

  emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), point);
}

/******************************************************************************/
const PointType& ImageViewManipulator::Transform(PointType& viewport, const QPoint& screen) const
{
  assert(!m_ViewSettings.IsNull());

  m_ViewSettings->ScreenToViewPortTransform(static_cast<double>(screen.x()), static_cast<double>(screen.y()), viewport[0], viewport[1]);

  return viewport;
}

/******************************************************************************/
void ImageViewManipulator::ResetViewport()
{
  assert(!m_ViewSettings.IsNull());

  otb::ViewSettings::SizeType size(m_ViewSettings->GetViewportSize());

  m_ViewSettings->Reset();

  m_ViewSettings->SetViewportSize(size);

  m_NativeSpacing.Fill(1.0);
  m_ZoomFactor = 1.0;

  emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), m_ViewSettings->GetViewportCenter());
}

/******************************************************************************/
void ImageViewManipulator::MousePressEvent(QMouseEvent* e)
{

  // qDebug() << this << "::MousePressEvent(" << e << ")";

  assert(e != NULL);

  switch (e->button())
  {
  case Qt::NoButton:
    break;

  case Qt::LeftButton:
    m_MousePressPosition = e->pos();
    m_MousePressOrigin   = m_ViewSettings->GetOrigin();
    m_IsMouseDragging    = true;
    break;

  case Qt::RightButton:
    emit ToggleLayerVisibilityRequested(false);
    break;

  case Qt::MidButton:
    break;

  case Qt::XButton1:
    break;

  case Qt::XButton2:
    break;

  default:
    assert(false && "Unhandled Qt::MouseButton.");
    break;
  }

  /*
    Qt::NoModifier	0x00000000	No modifier key is pressed.
    Qt::ShiftModifier	0x02000000	A Shift key on the keyboard is pressed.
    Qt::ControlModifier	0x04000000	A Ctrl key on the keyboard is pressed.
    Qt::AltModifier	0x08000000	An Alt key on the keyboard is pressed.
    Qt::MetaModifier	0x10000000	A Meta key on the keyboard is pressed.
    Qt::KeypadModifier	0x20000000	A keypad button is pressed.
    Qt::GroupSwitchModifier
  */
}

/******************************************************************************/
void ImageViewManipulator::MouseMoveEvent(QMouseEvent* e)
{
  // qDebug() << this << "::MouseMoveEvent(" << e << ")";

  assert(e != NULL);

  /*
  qDebug() << "------------------------------------------------";

  qDebug() << this << ":" << e;
  */

  Qt::MouseButtons      buttons   = e->buttons();
  Qt::KeyboardModifiers modifiers = e->modifiers();

  if (buttons == Qt::LeftButton && (modifiers == Qt::NoModifier || modifiers == Qt::ControlModifier))
  {
    // Cursor moves from press position to current position;
    // Image moves the same direction, so apply the negative translation.
    Translate(m_MousePressPosition - e->pos());

    m_MousePressPosition = e->pos();

    emit RefreshViewRequested();

    emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), m_ViewSettings->GetViewportCenter());
  }
}

/******************************************************************************/
void ImageViewManipulator::MouseReleaseEvent(QMouseEvent* e)
{
  // qDebug() << this << "::MouseReleaseEvent(" << e << ")";

  assert(e != NULL);

  /*
  qDebug() << this << ":" << e;
  */

  /*
  Qt::MouseButtons buttons = e->buttons();
  Qt::KeyboardModifiers modifiers = e->modifiers();
  */
  // PointType center;

  switch (e->button())
  {
  case Qt::NoButton:
    break;

  case Qt::LeftButton:
    m_MousePressPosition = QPoint();
    m_MousePressOrigin   = PointType();
    m_IsMouseDragging    = false;

    emit RefreshViewRequested();
    break;

  case Qt::RightButton:
    emit ToggleLayerVisibilityRequested(true);
    break;

  case Qt::MidButton:
    break;

  case Qt::XButton1:
    break;

  case Qt::XButton2:
    break;

  default:
    assert(false && "Unhandled Qt::MouseButton.");
    break;
  }
}

/******************************************************************************/
void ImageViewManipulator::MouseDoubleClickEvent(QMouseEvent* e)
{
  // qDebug() << this << "::MouseDoubleClickEvent(" << e << ")";

  assert(e != NULL);

  if (e->button() == Qt::LeftButton && e->modifiers() == Qt::NoModifier)
  {
    PointType center;

    assert(!m_ViewSettings.IsNull());

    const QPoint& p = e->pos();

    m_ViewSettings->ScreenToViewPortTransform(p.x(), p.y(), center[0], center[1]);

    CenterOn(center);
  }
}

/******************************************************************************/
void ImageViewManipulator
#if USE_VIEW_SETTINGS_SIDE_EFFECT
    ::ResizeEvent(QResizeEvent*)
#else  // USE_VIEW_SETTINGS_SIDE_EFFECT
    ::ResizeEvent(QResizeEvent* e)
#endif // USE_VIEW_SETTINGS_SIDE_EFFECT
{
// assert( e!=NULL );

// qDebug() << this << "::ResizeEvent(" << e << ")";

/*
qDebug() << m_ViewSettings.GetPointer();

otb::ViewSettings::SizeType size( m_ViewSettings->GetViewportSize() );

qDebug() << size[ 0 ] << "," << size[ 1 ] << "\t" << e->size();
*/

#if USE_VIEW_SETTINGS_SIDE_EFFECT
#else  // USE_VIEW_SETTINGS_SIDE_EFFECT
  assert(e != NULL);

  SetViewportSize(e->size().width(), e->size().height());
#endif // USE_VIEW_SETTINGS_SIDE_EFFECT
}

/******************************************************************************/
void ImageViewManipulator::WheelEvent(QWheelEvent* e)
{
  assert(e != NULL);

  Qt::MouseButtons      buttons   = e->buttons();
  Qt::KeyboardModifiers modifiers = e->modifiers();

  if (buttons != Qt::NoButton)
    return;

  // Delta is rotation distance in number of 8th of degrees (see
  // http://qt-project.org/doc/qt-4.8/qwheelevent.html#delta).
  assert(e->delta() != 0);
  int degrees = e->delta() / MOUSE_WHEEL_STEP_FACTOR;

  if (modifiers == Qt::ControlModifier)
    emit RotateLayersRequested(e->delta() / (MOUSE_WHEEL_STEP_FACTOR * MOUSE_WHEEL_STEP_DEGREES));
  //
  else if (modifiers == Qt::MetaModifier)
  {
    // qDebug() << "META+Wheel" << e->delta();

    emit ShiftAlphaRequested(static_cast<double>(m_AlphaGranularity * e->delta() / (MOUSE_WHEEL_STEP_FACTOR * MOUSE_WHEEL_STEP_DEGREES)) / 100.0);
  }
  else if (modifiers == (Qt::MetaModifier | Qt::ShiftModifier))
  {
    // qDebug() << "META+SHIFT+Wheel" << e->delta();

    emit UpdateGammaRequested(ImageViewManipulator::Factor(degrees, MOUSE_WHEEL_STEP_DEGREES));
  }
  //
  else if (modifiers == Qt::AltModifier)
  {
    // qDebug() << "ALT+Wheel" << e->delta();

    emit ResizeShaderRequested(ImageViewManipulator::Factor(degrees, MOUSE_WHEEL_STEP_DEGREES));
  }
  else if (modifiers == (Qt::AltModifier | Qt::ShiftModifier))
  {
    // qDebug() << "ALT+SHIFT+Wheel" << e->delta();

    emit ReparamShaderRequested(ImageViewManipulator::Factor(degrees, MOUSE_WHEEL_STEP_DEGREES));
  }
  //
  else if (modifiers == (Qt::ControlModifier | Qt::AltModifier))
  {
    // qDebug() << "CTRL+ALT+Wheel" << e->delta();

    emit ShiftDynamicsRequested(m_DynamicsShiftGranularity * static_cast<double>(e->delta() / (MOUSE_WHEEL_STEP_FACTOR * MOUSE_WHEEL_STEP_DEGREES)));
  }
  else if (modifiers == (Qt::ControlModifier | Qt::AltModifier | Qt::ShiftModifier))
  {
    // qDebug() << "CTRL+ALT+SHIFT+Wheel" << e->delta();

    emit ScaleDynamicsRequested(ImageViewManipulator::Factor(degrees, MOUSE_WHEEL_STEP_DEGREES));
  }
  //
  else if (modifiers == Qt::NoModifier)
  {
    if (m_Timer == NULL)
    {
      m_Timer = new QTimer();

      QObject::connect(m_Timer, SIGNAL(timeout()),
                       // to:
                       this, SLOT(OnTimeout()));
    }

    m_Timer->start(500);

    SetFastRenderMode(true);

    PointType point;

    Scale(e->pos(), degrees, &point);

    emit RefreshViewRequested();

    emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), point);
  }
}

/******************************************************************************/
void ImageViewManipulator::KeyPressEvent(QKeyEvent* e)
{
  assert(e != NULL);

  // qDebug() << this << "::KeyPressEvent(" << e << ")";

  QPoint vector(0, 0);
  int    steps = 0;

  int                   key       = e->key();
  Qt::KeyboardModifiers modifiers = e->modifiers();

  switch (key)
  {
  case Qt::Key_Up:
    vector.setY(-1);
    break;

  case Qt::Key_Down:
    vector.setY(+1);
    break;

  case Qt::Key_Left:
    vector.setX(-1);
    break;

  case Qt::Key_Right:
    vector.setX(+1);
    break;

  case Qt::Key_Plus:
    steps = m_ZoomGranularity;
    break;

  case Qt::Key_Minus:
    steps = -m_ZoomGranularity;
    break;

  case Qt::Key_PageUp:
    if (e->modifiers() == Qt::ShiftModifier)
      emit LayerToTopRequested();
    else
      emit RaiseLayerRequested();
    break;

  case Qt::Key_PageDown:
    if (e->modifiers() == Qt::ShiftModifier)
      emit LayerToBottomRequested();
    else
      emit LowerLayerRequested();
    break;

  case Qt::Key_Home:
    if (e->modifiers() == Qt::ShiftModifier)
      emit SelectFirstLayerRequested();
    else
      emit SelectPreviousLayerRequested();
    break;

  case Qt::Key_End:
    if (e->modifiers() == Qt::ShiftModifier)
      emit SelectLastLayerRequested();
    else
      emit SelectNextLayerRequested();
    break;

  case Qt::Key_Delete:
    if (modifiers.testFlag(Qt::ShiftModifier))
      emit DeleteAllRequested();
    else
      emit DeleteSelectedRequested();
    break;

  case Qt::Key_1:
    emit ZoomToFullResolutionRequested();
    break;

  case Qt::Key_2:
    emit ZoomToLayerExtentRequested();
    break;

  case Qt::Key_3:
    emit ZoomToFullExtentRequested();
    break;

  case Qt::Key_A:
    emit ApplyAllRequested();
    break;

  case Qt::Key_C:
    emit ShaderEffectRequested(EFFECT_CHESSBOARD);
    break;

  case Qt::Key_G:
    emit ShaderEffectRequested(EFFECT_GRADIENT);
    break;

  case Qt::Key_D:
    emit ShaderEffectRequested(EFFECT_LOCAL_CONTRAST);
    break;

  case Qt::Key_H:
    emit ShaderEffectRequested(EFFECT_SWIPE_H);
    break;

  case Qt::Key_N:
    emit ShaderEffectRequested(EFFECT_NORMAL);
    break;

  case Qt::Key_P:
    if (modifiers.testFlag(Qt::ControlModifier))
      emit TakeScreenshotRequested(modifiers.testFlag(Qt::ShiftModifier));
    else
      emit SetReferenceRequested();
    break;

  case Qt::Key_Q:
    emit ResetQuantilesRequested(modifiers.testFlag(Qt::ShiftModifier));
    break;

  case Qt::Key_S:
    emit ShaderEffectRequested(EFFECT_SPECTRAL_ANGLE);
    break;

  case Qt::Key_T:
    emit ShaderEffectRequested(EFFECT_LOCAL_TRANSLUCENCY);
    break;

  case Qt::Key_V:
    emit ShaderEffectRequested(EFFECT_SWIPE_V);
    break;

  default:
    break;
  }

  assert(!m_ViewSettings.IsNull());

  bool needsRefresh = false;

  //
  // Translate

  if (!vector.isNull())
  {
    otb::ViewSettings::SizeType size(m_ViewSettings->GetViewportSize());

    if (modifiers == Qt::NoModifier)
    {
      size[0] /= m_ScrollGranularity;
      size[1] /= m_ScrollGranularity;
    }
    else if (modifiers == Qt::ControlModifier)
    {
      size[0] /= m_ScrollGranularity * 2;
      size[1] /= m_ScrollGranularity * 2;
    }

    vector.rx() *= size[0];
    vector.ry() *= size[1];

    Translate(vector);

    needsRefresh = true;
  }

  //
  // Scale

  if (steps != 0)
  {
    // Qt::ControlModifier doest not work with keypard Qt::Key_Plus/Minus keys.

    otb::ViewSettings::SizeType size(m_ViewSettings->GetViewportSize());

    Scale(QPoint(size[0] / 2.0, size[1] / 2.0), steps * MOUSE_WHEEL_STEP_DEGREES);

    needsRefresh = true;
  }

  //
  // Refresh
  if (needsRefresh)
  {
    emit RefreshViewRequested();

    emit RoiChanged(GetOrigin(), GetViewportSize(), GetSpacing(), m_ViewSettings->GetViewportCenter());
  }
}

/******************************************************************************/
void ImageViewManipulator::KeyReleaseEvent(QKeyEvent*)
{
  // assert( e!=NULL );

  // qDebug() << this << "::KeyPressEvent(" << e << ")";
}

/******************************************************************************/
void ImageViewManipulator::Translate(const QPoint& vector)
{
  // qDebug() << this << "::Translate(" << vector << ")";

  m_ViewSettings->SetOrigin(ImageViewManipulator::Translate(vector, m_ViewSettings->GetOrigin(), m_ViewSettings->GetSpacing()));
}

/******************************************************************************/
PointType ImageViewManipulator::Translate(const QPoint& vector, const PointType& origin, const SpacingType& spacing)
{
  // qDebug() << this << "::Translate(...)";

  otb::ViewSettings::PointType origin2(origin);

  origin2[0] += static_cast<double>(vector.x()) * spacing[0];
  origin2[1] += static_cast<double>(vector.y()) * spacing[1];

  /*
    qDebug()
    << "(" << m_MousePressOrigin[ 0 ] << "," << m_MousePressOrigin[ 1 ] << ")"
    << "(" << spacing[ 0 ] << "," << spacing[ 1 ] << ")"
    << "(" << origin[ 0 ] << "," << origin[ 1 ] << ")";
  */

  return origin2;
}

/******************************************************************************/
void ImageViewManipulator::Scale(const QPoint& center, int degrees, PointType* centerPoint)
{
  assert(degrees != 0);

  if (degrees == 0)
    return;

  otb::ViewSettings::PointType point;

  Transform(point, center);

  if (centerPoint != NULL)
    *centerPoint = point;

  // See http://qt-project.org/doc/qt-4.8/qwheelevent.html#delta .
  assert(m_ZoomGranularity != 0);

  int granularity = m_ZoomGranularity;

  if (granularity == 0)
    granularity = 1;

  double factor = pow(2.0, -static_cast<double>(degrees) / static_cast<double>(granularity * MOUSE_WHEEL_STEP_DEGREES));

  m_ZoomFactor *= factor;

  /*
  qDebug()
    << "(" << point[ 0 ] << "," << point[ 1 ] << ")"
    << "g:" << granularity
    << "d:" << degrees
    << "s:" << (static_cast< double >( degrees ) / 15.0)
    << "f:" << factor
    << "z:" << m_ZoomFactor;
  */

  m_ViewSettings->Zoom(point, factor);
}

/*****************************************************************************/
ZoomType ImageViewManipulator::GetFixedZoomType() const
{
  return ZOOM_TYPE_NONE;
}

/*****************************************************************************/
/* SLOTS                                                                     */
/*****************************************************************************/
void ImageViewManipulator::OnTimeout()
{
  assert(m_Timer != NULL);

  SetFastRenderMode(false);

  emit RefreshViewRequested();

  delete m_Timer;
  m_Timer = NULL;
}

} // end namespace 'mvd'
