/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include <mitkVtkEventProvider.h>
#include <mitkVtkEventAdapter.h>
#include <mitkLog.h>

#include <vtkCallbackCommand.h>
#include <vtkInteractorStyle.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindowInteractor.h>

#include <mitkInteractionEvent.h>

#define VTKEVENTPROVIDER_INFO MITK_INFO("mitk.core.vtkeventprovider")
#define VTKEVENTPROVIDER_WARN MITK_WARN("mitk.core.vtkeventprovider")
#define VTKEVENTPROVIDER_ERROR MITK_ERROR("mitk.core.vtkeventprovider")
#define VTKEVENTPROVIDER_DEBUG MITK_DEBUG("mitk.core.vtkeventprovider")

namespace mitk
{
  vtkStandardNewMacro(vtkEventProvider);
}
//----------------------------------------------------------------------------
mitk::vtkEventProvider::vtkEventProvider()
{
  // priority of the observer/command; we want MITK events processed in the very beginning
  this->Priority = 99999.99;

  // take over the processing of delete and keypress events from the superclass
  this->EventCallbackCommand->SetCallback(vtkEventProvider::ProcessEvents);

  // Set/Get the passive observer flag. If this is set to true, this
  // indicates that this command does not change the state of the
  // system in any way. Passive observers are processed first, and
  // are not called even when another command has focus.
  this->EventCallbackCommand->SetPassiveObserver(1); // get events first

  // mouse move
  AddInteractionEvent(vtkCommand::MouseMoveEvent);
  // mouse press
  AddInteractionEvent(vtkCommand::LeftButtonPressEvent);
  AddInteractionEvent(vtkCommand::MiddleButtonPressEvent);
  AddInteractionEvent(vtkCommand::RightButtonPressEvent);
  // mouse release
  AddInteractionEvent(vtkCommand::LeftButtonReleaseEvent);
  AddInteractionEvent(vtkCommand::MiddleButtonReleaseEvent);
  AddInteractionEvent(vtkCommand::RightButtonReleaseEvent);
  // wheel event
  AddInteractionEvent(vtkCommand::MouseWheelBackwardEvent);
  AddInteractionEvent(vtkCommand::MouseWheelForwardEvent);
  // key press event
  AddInteractionEvent(vtkCommand::KeyPressEvent);
}

mitk::vtkEventProvider::~vtkEventProvider()
{
  this->SetInteractor(nullptr);
}

void mitk::vtkEventProvider::SetMitkRenderWindow(mitk::RenderWindow *renWin)
{
  m_RenderWindow = renWin;
}

mitk::RenderWindow *mitk::vtkEventProvider::GetRenderWindow()
{
  return m_RenderWindow;
}

void mitk::vtkEventProvider::SetEnabled(int enabling)
{
  if (!this->Interactor)
  {
    VTKEVENTPROVIDER_ERROR << "The interactor must be set prior to enabling/disabling widget";
    return;
  }

  if (enabling) //----------------------------------------------------------
  {
    VTKEVENTPROVIDER_DEBUG << "Enabling widget";

    if (this->Enabled) // already enabled, just return
    {
      return;
    }

    this->Enabled = 1;

    // listen to all event types specified in m_InteractionEventsVector
    vtkRenderWindowInteractor *i = this->Interactor;

    InteractionEventsVectorType::iterator it;
    for (it = m_InteractionEventsVector.begin(); it != m_InteractionEventsVector.end(); ++it)
    {
      // add observer to interactorStyle
      i->GetInteractorStyle()->AddObserver((vtkCommand::EventIds)(*it), this->EventCallbackCommand, this->Priority);
    }

    this->InvokeEvent(vtkCommand::EnableEvent, nullptr);
  }

  else // disabling-----------------------------------------------------------
  {
    VTKEVENTPROVIDER_DEBUG << "Disabling widget";

    if (!this->Enabled) // already disabled, just return
    {
      return;
    }

    this->Enabled = 0;

    // don't listen for events any more
    this->Interactor->RemoveObserver(this->EventCallbackCommand);
    // this->Interactor->HandleEventLoop = 0;

    this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
  }
}

//----------------------------------------------------------------------------
// This adds the keypress event observer and the delete event observer
void mitk::vtkEventProvider::SetInteractor(vtkRenderWindowInteractor *i)
{
  if (i == this->Interactor)
  {
    return;
  }
  // if we already have an Interactor then stop observing it
  if (this->Interactor)
    this->SetEnabled(0); // disable the old interactor

  this->Interactor = i;

  this->Modified();
}

//----------------------------------------------------------------------------
void mitk::vtkEventProvider::ProcessEvents(vtkObject *object,
                                           unsigned long event,
                                           void *clientData,
                                           void *vtkNotUsed(callData))
{
  auto *self = reinterpret_cast<vtkEventProvider *>(clientData);
  vtkRenderWindowInteractor *rwi = static_cast<vtkInteractorStyle *>(object)->GetInteractor();

  // base renderer
  mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(self->GetRenderWindow()->GetVtkRenderWindow());
  switch (event)
  {
    // key press
    case vtkCommand::KeyPressEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "key press event";
      InteractionEvent::Pointer adaptedEvent =
        VtkEventAdapter::AdaptInteractionKeyEvent(baseRenderer, event, rwi).GetPointer();
      self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer());
      break;
    }

    // mouse events
    case vtkCommand::MouseMoveEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse move event";
      InteractionEvent::Pointer adaptedEvent =
        VtkEventAdapter::AdaptMouseMoveEvent(baseRenderer, event, rwi).GetPointer();
      self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer());
      break;
    }

    case vtkCommand::LeftButtonPressEvent:
    case vtkCommand::MiddleButtonPressEvent:
    case vtkCommand::RightButtonPressEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse press event";
      InteractionEvent::Pointer adaptedEvent =
        VtkEventAdapter::AdaptMousePressEvent(baseRenderer, event, rwi).GetPointer();
      self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer());
      break;
    }

    case vtkCommand::LeftButtonReleaseEvent:
    case vtkCommand::MiddleButtonReleaseEvent:
    case vtkCommand::RightButtonReleaseEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse release event";
      InteractionEvent::Pointer adaptedEvent =
        VtkEventAdapter::AdaptMouseReleaseEvent(baseRenderer, event, rwi).GetPointer();
      self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer());
      break;
    }

    // mouse WHEEL
    case vtkCommand::MouseWheelForwardEvent:
    case vtkCommand::MouseWheelBackwardEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse wheel event";
      InteractionEvent::Pointer adaptedEvent =
        VtkEventAdapter::AdaptMouseWheelEvent(baseRenderer, event, rwi).GetPointer();
      self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer());
      break;
    }

    default:
      VTKEVENTPROVIDER_INFO << "VTK event not mapped properly.";
      break;
  }
}

void mitk::vtkEventProvider::RemoveInteractionEvent(unsigned long ievent)
{
  InteractionEventsVectorType::iterator it;
  if (m_InteractionEventsVector.size() > 0)
  {
    it = std::find(m_InteractionEventsVector.begin(), m_InteractionEventsVector.end(), ievent);
    if (it != m_InteractionEventsVector.end())
    {
      m_InteractionEventsVector.erase(it);
      return;
    }
  }
}

void mitk::vtkEventProvider::AddInteractionEvent(unsigned long ievent)
{
  // Remove event if it already exists
  RemoveInteractionEvent(ievent);

  m_InteractionEventsVector.push_back(ievent);
}
