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

  Program:   Visualization Toolkit
  Module:    vtkResliceCursorWidget.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkResliceCursorWidget.h"
#include "vtkCallbackCommand.h"
#include "vtkCommand.h"
#include "vtkEvent.h"
#include "vtkImageData.h"
#include "vtkImageMapToWindowLevelColors.h"
#include "vtkInteractorStyleImage.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkResliceCursor.h"
#include "vtkResliceCursorLineRepresentation.h"
#include "vtkWidgetCallbackMapper.h"
#include "vtkWidgetEvent.h"
#include "vtkWidgetEventTranslator.h"

vtkStandardNewMacro(vtkResliceCursorWidget);

//----------------------------------------------------------------------------
vtkResliceCursorWidget::vtkResliceCursorWidget()
{
  // Set the initial state
  this->WidgetState = vtkResliceCursorWidget::Start;

  this->ModifierActive = 0;

  // Okay, define the events for this widget
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
                                          vtkEvent::NoModifier, 0, 0, nullptr,
                                          vtkWidgetEvent::Select,
                                          this, vtkResliceCursorWidget::SelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
                                          vtkEvent::ControlModifier, 0, 0, nullptr,
                                          vtkWidgetEvent::Rotate,
                                          this, vtkResliceCursorWidget::RotateAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent,
                                          vtkWidgetEvent::EndSelect,
                                          this, vtkResliceCursorWidget::EndSelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::RightButtonPressEvent,
                                          vtkWidgetEvent::Resize,
                                          this, vtkResliceCursorWidget::ResizeThicknessAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::RightButtonReleaseEvent,
                                          vtkWidgetEvent::EndResize,
                                          this, vtkResliceCursorWidget::EndSelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::MouseMoveEvent,
                                          vtkWidgetEvent::Move,
                                          this, vtkResliceCursorWidget::MoveAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::KeyPressEvent,
                vtkEvent::NoModifier, 111, 1, "o",
                vtkWidgetEvent::Reset,
                this, vtkResliceCursorWidget::ResetResliceCursorAction);

  this->ManageWindowLevel = 1;
}

//----------------------------------------------------------------------------
vtkResliceCursorWidget::~vtkResliceCursorWidget() = default;

//----------------------------------------------------------------------
void vtkResliceCursorWidget::SetEnabled(int enabling)
{
  this->Superclass::SetEnabled(enabling);
}

//----------------------------------------------------------------------
void vtkResliceCursorWidget::CreateDefaultRepresentation()
{
  if ( ! this->WidgetRep )
  {
    this->WidgetRep = vtkResliceCursorLineRepresentation::New();
  }
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::SetCursor(int cState)
{
  switch (cState)
  {
    case vtkResliceCursorRepresentation::OnAxis1:
    case vtkResliceCursorRepresentation::OnAxis2:
      this->RequestCursorShape(VTK_CURSOR_HAND);
      break;
    case vtkResliceCursorRepresentation::OnCenter:
      if (vtkEvent::GetModifier(this->Interactor) != vtkEvent::ControlModifier)
      {
        this->RequestCursorShape(VTK_CURSOR_SIZEALL);
      }
      break;
    case vtkResliceCursorRepresentation::Outside:
    default:
      this->RequestCursorShape(VTK_CURSOR_DEFAULT);
  }
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::ResizeThicknessAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = reinterpret_cast<vtkResliceCursorWidget*>(w);
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(self->WidgetRep);

  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  rep->ComputeInteractionState(X, Y, self->ModifierActive);

  if ( self->WidgetRep->GetInteractionState()
      == vtkResliceCursorRepresentation::Outside ||
      rep->GetResliceCursor()->GetThickMode() == 0 )
  {
    return;
  }

  rep->SetManipulationMode(vtkResliceCursorRepresentation::ResizeThickness);

  self->GrabFocus(self->EventCallbackCommand);
  double eventPos[2];
  eventPos[0] = static_cast<double>(X);
  eventPos[1] = static_cast<double>(Y);
  self->WidgetRep->StartWidgetInteraction(eventPos);

  // We are definitely selected
  self->WidgetState = vtkResliceCursorWidget::Active;
  self->SetCursor(self->WidgetRep->GetInteractionState());

  // Highlight as necessary
  self->WidgetRep->Highlight(1);

  self->EventCallbackCommand->SetAbortFlag(1);
  self->StartInteraction();
  self->InvokeEvent(vtkCommand::StartInteractionEvent,nullptr);
  self->Render();

  self->InvokeAnEvent();

  // Show the thickness in "mm"
  rep->ActivateText(1);
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::EndResizeThicknessAction(vtkAbstractWidget *)
{
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::SelectAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = reinterpret_cast<vtkResliceCursorWidget*>(w);
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(self->WidgetRep);

  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  self->ModifierActive = vtkEvent::GetModifier(self->Interactor);
  rep->ComputeInteractionState(X, Y, self->ModifierActive);

  if ( self->WidgetRep->GetInteractionState()
      == vtkResliceCursorRepresentation::Outside )
  {
    if (self->GetManageWindowLevel() && rep->GetShowReslicedImage())
    {
      self->StartWindowLevel();
    }
    else
    {
      rep->SetManipulationMode(vtkResliceCursorRepresentation::None);
      return;
    }
  }
  else
  {
    rep->SetManipulationMode(vtkResliceCursorRepresentation::PanAndRotate);
  }

  if (rep->GetManipulationMode() == vtkResliceCursorRepresentation::None)
  {
    return;
  }

  self->GrabFocus(self->EventCallbackCommand);
  double eventPos[2];
  eventPos[0] = static_cast<double>(X);
  eventPos[1] = static_cast<double>(Y);
  self->WidgetRep->StartWidgetInteraction(eventPos);

  // We are definitely selected
  self->WidgetState = vtkResliceCursorWidget::Active;
  self->SetCursor(self->WidgetRep->GetInteractionState());

  // Highlight as necessary
  self->WidgetRep->Highlight(1);

  self->EventCallbackCommand->SetAbortFlag(1);
  self->StartInteraction();
  self->InvokeEvent(vtkCommand::StartInteractionEvent,nullptr);
  self->Render();

  self->InvokeAnEvent();
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::RotateAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = reinterpret_cast<vtkResliceCursorWidget*>(w);
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(self->WidgetRep);

  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  self->ModifierActive = vtkEvent::GetModifier(self->Interactor);
  rep->ComputeInteractionState(X, Y, self->ModifierActive);

  if ( self->WidgetRep->GetInteractionState()
      == vtkResliceCursorRepresentation::Outside )
  {
    return;
  }

  rep->SetManipulationMode(vtkResliceCursorRepresentation::RotateBothAxes);

  self->GrabFocus(self->EventCallbackCommand);
  double eventPos[2];
  eventPos[0] = static_cast<double>(X);
  eventPos[1] = static_cast<double>(Y);
  self->WidgetRep->StartWidgetInteraction(eventPos);

  // We are definitely selected
  self->WidgetState = vtkResliceCursorWidget::Active;
  self->SetCursor(self->WidgetRep->GetInteractionState());

  // Highlight as necessary
  self->WidgetRep->Highlight(1);

  self->EventCallbackCommand->SetAbortFlag(1);
  self->StartInteraction();
  self->InvokeEvent(vtkCommand::StartInteractionEvent,nullptr);
  self->Render();

  self->InvokeAnEvent();
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::MoveAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = reinterpret_cast<vtkResliceCursorWidget*>(w);
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(self->WidgetRep);

  // compute some info we need for all cases
  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  // Set the cursor appropriately
  if ( self->WidgetState == vtkResliceCursorWidget::Start )
  {
    self->ModifierActive = vtkEvent::GetModifier(self->Interactor);
    int state = self->WidgetRep->GetInteractionState();

    rep->ComputeInteractionState(X, Y, self->ModifierActive );

    self->SetCursor(self->WidgetRep->GetInteractionState());

    if ( state != self->WidgetRep->GetInteractionState() )
    {
      self->Render();
    }
    return;
  }

  // Okay, adjust the representation

  double eventPosition[2];
  eventPosition[0] = static_cast<double>(X);
  eventPosition[1] = static_cast<double>(Y);
  self->WidgetRep->WidgetInteraction(eventPosition);

  // Got this event, we are finished
  self->EventCallbackCommand->SetAbortFlag(1);
  self->InvokeEvent(vtkCommand::InteractionEvent,nullptr);
  self->Render();

  self->InvokeAnEvent();
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::EndSelectAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = static_cast<vtkResliceCursorWidget*>(w);
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(self->WidgetRep);

  if ( self->WidgetState != vtkResliceCursorWidget::Active )
  {
    return;
  }

  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  double eventPos[2];
  eventPos[0] = static_cast<double>(X);
  eventPos[1] = static_cast<double>(Y);

  self->WidgetRep->EndWidgetInteraction(eventPos);

  // return to initial state
  self->WidgetState = vtkResliceCursorWidget::Start;
  self->ModifierActive = 0;

  // Highlight as necessary
  self->WidgetRep->Highlight(0);

  // Remove any text displays. We are no longer active.
  rep->ActivateText(0);

  // stop adjusting
  self->ReleaseFocus();
  self->EventCallbackCommand->SetAbortFlag(1);
  self->EndInteraction();
  self->InvokeEvent(vtkCommand::EndInteractionEvent,nullptr);
  self->WidgetState = vtkResliceCursorWidget::Start;
  rep->SetManipulationMode(vtkResliceCursorRepresentation::None);

  self->Render();

  self->InvokeAnEvent();
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::ResetResliceCursorAction(vtkAbstractWidget *w)
{
  vtkResliceCursorWidget *self = reinterpret_cast<vtkResliceCursorWidget*>(w);
  self->ResetResliceCursor();

  // Render in response to changes
  self->Render();

  // Invoke a reslice cursor event
  self->InvokeEvent( vtkResliceCursorWidget::ResetCursorEvent, nullptr );
}

//-------------------------------------------------------------------------
void vtkResliceCursorWidget::ResetResliceCursor()
{
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(this->WidgetRep);

  if (!rep->GetResliceCursor())
  {
    return; // nothing to reset
  }

  // Reset the reslice cursor
  rep->GetResliceCursor()->Reset();
  rep->InitializeReslicePlane();
}

//----------------------------------------------------------------------------
void vtkResliceCursorWidget::StartWindowLevel()
{
  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(this->WidgetRep);

  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];

  // Okay, make sure that the pick is in the current renderer
  if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
  {
    rep->SetManipulationMode(vtkResliceCursorRepresentation::None);
    return;
  }

  rep->SetManipulationMode(vtkResliceCursorRepresentation::WindowLevelling);

  rep->ActivateText(1);
  rep->ManageTextDisplay();
}

//----------------------------------------------------------------------------
void vtkResliceCursorWidget::InvokeAnEvent()
{
  // We invoke the appropriate event. In cases where the cursor is moved
  // around, or rotated, also have the reslice cursor invoke an event.

  vtkResliceCursorRepresentation *rep =
    reinterpret_cast<vtkResliceCursorRepresentation*>(this->WidgetRep);
  if (rep)
  {
    int mode = rep->GetManipulationMode();
    if (mode == vtkResliceCursorRepresentation::WindowLevelling)
    {
      this->InvokeEvent(WindowLevelEvent,nullptr);
    }
    else if (mode == vtkResliceCursorRepresentation::PanAndRotate)
    {
      this->InvokeEvent(ResliceAxesChangedEvent,nullptr);
      rep->GetResliceCursor()->InvokeEvent(ResliceAxesChangedEvent,nullptr);
    }
    else if (mode == vtkResliceCursorRepresentation::RotateBothAxes)
    {
      this->InvokeEvent(ResliceAxesChangedEvent,nullptr);
      rep->GetResliceCursor()->InvokeEvent(ResliceAxesChangedEvent,nullptr);
    }
    else if (mode == vtkResliceCursorRepresentation::ResizeThickness)
    {
      this->InvokeEvent(ResliceThicknessChangedEvent,nullptr);
      rep->GetResliceCursor()->InvokeEvent(ResliceAxesChangedEvent,nullptr);
    }
  }
}

//----------------------------------------------------------------------------
void vtkResliceCursorWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  //Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
  this->Superclass::PrintSelf(os,indent);

  os << indent << "ManageWindowLevel: " << this->ManageWindowLevel << endl;
  // this->ModifierActive;
  // this->WidgetState;

}
