// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkSeedWidget.h"

#include "vtkCallbackCommand.h"
#include "vtkCommand.h"
#include "vtkCoordinate.h"
#include "vtkEvent.h"
#include "vtkHandleRepresentation.h"
#include "vtkHandleWidget.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSeedRepresentation.h"
#include "vtkWidgetCallbackMapper.h"
#include "vtkWidgetEvent.h"
#include <iterator>
#include <list>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkSeedWidget);

// The vtkSeedList is a PIMPLed list<T>.
class vtkSeedList : public std::list<vtkHandleWidget*>
{
};
typedef std::list<vtkHandleWidget*>::iterator vtkSeedListIterator;

//------------------------------------------------------------------------------
vtkSeedWidget::vtkSeedWidget()
{
  this->ManagesCursor = 1;
  this->WidgetState = vtkSeedWidget::Start;

  // The widgets for moving the seeds.
  this->Seeds = new vtkSeedList;

  // These are the event callbacks supported by this widget
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
    vtkWidgetEvent::AddPoint, this, vtkSeedWidget::AddPointAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::RightButtonPressEvent,
    vtkWidgetEvent::Completed, this, vtkSeedWidget::CompletedAction);
  this->CallbackMapper->SetCallbackMethod(
    vtkCommand::MouseMoveEvent, vtkWidgetEvent::Move, this, vtkSeedWidget::MoveAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent,
    vtkWidgetEvent::EndSelect, this, vtkSeedWidget::EndSelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::KeyPressEvent, vtkEvent::NoModifier, 127, 1,
    "Delete", vtkWidgetEvent::Delete, this, vtkSeedWidget::DeleteAction);
  this->Defining = 1;
}

//------------------------------------------------------------------------------
void vtkSeedWidget::DeleteSeed(int i)
{
  if (this->Seeds->size() <= static_cast<size_t>(i))
  {
    return;
  }

  vtkSeedRepresentation* rep = static_cast<vtkSeedRepresentation*>(this->WidgetRep);
  if (rep)
  {
    rep->RemoveHandle(i);
  }

  vtkSeedListIterator iter = this->Seeds->begin();
  std::advance(iter, i);
  (*iter)->SetEnabled(0);
  (*iter)->RemoveObservers(vtkCommand::StartInteractionEvent);
  (*iter)->RemoveObservers(vtkCommand::InteractionEvent);
  (*iter)->RemoveObservers(vtkCommand::EndInteractionEvent);
  vtkHandleWidget* w = (*iter);
  this->Seeds->erase(iter);
  w->Delete();
}

//------------------------------------------------------------------------------
vtkSeedWidget::~vtkSeedWidget()
{
  // Loop over all seeds releasing their observers and deleting them
  while (!this->Seeds->empty())
  {
    this->DeleteSeed(static_cast<int>(this->Seeds->size()) - 1);
  }
  delete this->Seeds;
}

//------------------------------------------------------------------------------
vtkHandleWidget* vtkSeedWidget::GetSeed(int i)
{
  if (this->Seeds->size() <= static_cast<size_t>(i))
  {
    return nullptr;
  }
  vtkSeedListIterator iter = this->Seeds->begin();
  std::advance(iter, i);
  return *iter;
}

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

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

  vtkSeedListIterator iter;
  for (iter = this->Seeds->begin(); iter != this->Seeds->end(); ++iter)
  {
    (*iter)->SetEnabled(enabling);
  }

  if (!enabling)
  {
    this->RequestCursorShape(VTK_CURSOR_DEFAULT);
    this->WidgetState = vtkSeedWidget::Start;
  }

  this->Render();
}

// The following methods are the callbacks that the seed widget responds to.
//------------------------------------------------------------------------------
void vtkSeedWidget::AddPointAction(vtkAbstractWidget* w)
{
  vtkSeedWidget* self = reinterpret_cast<vtkSeedWidget*>(w);

  // Need to distinguish between placing handles and manipulating handles
  if (self->WidgetState == vtkSeedWidget::MovingSeed)
  {
    return;
  }

  self->InvokeEvent(vtkCommand::MouseMoveEvent, nullptr);

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

  // When a seed is placed, a new handle widget must be created and enabled.
  int state = self->WidgetRep->ComputeInteractionState(X, Y);
  if (state == vtkSeedRepresentation::NearSeed)
  {
    self->WidgetState = vtkSeedWidget::MovingSeed;

    // Invoke an event on ourself for the handles
    self->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
    self->Superclass::StartInteraction();
    vtkSeedRepresentation* rep = static_cast<vtkSeedRepresentation*>(self->WidgetRep);
    int seedIdx = rep->GetActiveHandle();
    self->InvokeEvent(vtkCommand::StartInteractionEvent, &seedIdx);

    self->EventCallbackCommand->SetAbortFlag(1);
    self->Render();
  }

  else if (self->WidgetState != vtkSeedWidget::PlacedSeeds)
  {
    // we are placing a new seed. Just make sure we aren't in a mode which
    // dictates we've placed all seeds.

    self->WidgetState = vtkSeedWidget::PlacingSeeds;
    double e[3];
    e[2] = 0.0;
    e[0] = static_cast<double>(X);
    e[1] = static_cast<double>(Y);

    vtkSeedRepresentation* rep = reinterpret_cast<vtkSeedRepresentation*>(self->WidgetRep);
    // if the handle representation is constrained, check to see if
    // the position follows the constraint.
    if (!rep->GetHandleRepresentation()->CheckConstraint(self->GetCurrentRenderer(), e))
    {
      return;
    }
    int currentHandleNumber = rep->CreateHandle(e);
    vtkHandleWidget* currentHandle = self->CreateNewHandle();
    rep->SetSeedDisplayPosition(currentHandleNumber, e);
    currentHandle->SetEnabled(1);
    self->InvokeEvent(vtkCommand::PlacePointEvent, &(currentHandleNumber));
    self->InvokeEvent(vtkCommand::InteractionEvent, &(currentHandleNumber));

    self->EventCallbackCommand->SetAbortFlag(1);
    self->Render();
  }
}

//------------------------------------------------------------------------------
void vtkSeedWidget::CompletedAction(vtkAbstractWidget* w)
{
  vtkSeedWidget* self = reinterpret_cast<vtkSeedWidget*>(w);

  // Do something only if we are in the middle of placing the seeds
  if (self->WidgetState == vtkSeedWidget::PlacingSeeds)
  {
    self->CompleteInteraction();
  }
}

//------------------------------------------------------------------------------
void vtkSeedWidget::CompleteInteraction()
{
  this->WidgetState = vtkSeedWidget::PlacedSeeds;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Defining = 0;
}

//------------------------------------------------------------------------------
void vtkSeedWidget::RestartInteraction()
{
  this->WidgetState = vtkSeedWidget::Start;
  this->Defining = 1;
}

//------------------------------------------------------------------------------
void vtkSeedWidget::MoveAction(vtkAbstractWidget* w)
{
  vtkSeedWidget* self = reinterpret_cast<vtkSeedWidget*>(w);

  self->InvokeEvent(vtkCommand::MouseMoveEvent, nullptr);

  // set the cursor shape to a hand if we are near a seed.
  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];
  int state = self->WidgetRep->ComputeInteractionState(X, Y);

  // Change the cursor shape to a hand and invoke an interaction event if we
  // are near the seed
  if (state == vtkSeedRepresentation::NearSeed)
  {
    self->RequestCursorShape(VTK_CURSOR_HAND);

    vtkSeedRepresentation* rep = static_cast<vtkSeedRepresentation*>(self->WidgetRep);
    int seedIdx = rep->GetActiveHandle();
    self->InvokeEvent(vtkCommand::InteractionEvent, &seedIdx);

    self->EventCallbackCommand->SetAbortFlag(1);
  }
  else
  {
    self->RequestCursorShape(VTK_CURSOR_DEFAULT);
  }

  self->Render();
}

//------------------------------------------------------------------------------
void vtkSeedWidget::EndSelectAction(vtkAbstractWidget* w)
{
  vtkSeedWidget* self = reinterpret_cast<vtkSeedWidget*>(w);

  // Do nothing if outside
  if (self->WidgetState != vtkSeedWidget::MovingSeed)
  {
    return;
  }

  // Revert back to the mode we were in prior to selection.
  self->WidgetState = self->Defining ? vtkSeedWidget::PlacingSeeds : vtkSeedWidget::PlacedSeeds;

  // Invoke event for seed handle
  self->InvokeEvent(vtkCommand::LeftButtonReleaseEvent, nullptr);
  self->EventCallbackCommand->SetAbortFlag(1);
  self->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
  self->Superclass::EndInteraction();
  self->Render();
}

//------------------------------------------------------------------------------
void vtkSeedWidget::DeleteAction(vtkAbstractWidget* w)
{
  vtkSeedWidget* self = reinterpret_cast<vtkSeedWidget*>(w);

  // Do nothing if outside
  if (self->WidgetState != vtkSeedWidget::PlacingSeeds)
  {
    return;
  }

  // Remove last seed
  vtkSeedRepresentation* rep = reinterpret_cast<vtkSeedRepresentation*>(self->WidgetRep);
  int removeId = rep->GetActiveHandle();
  removeId = removeId != -1 ? removeId : static_cast<int>(self->Seeds->size()) - 1;
  // Invoke event for seed handle before actually deleting
  self->InvokeEvent(vtkCommand::DeletePointEvent, &(removeId));

  self->DeleteSeed(removeId);
  // Got this event, abort processing if it
  self->EventCallbackCommand->SetAbortFlag(1);
  self->Render();
}

//------------------------------------------------------------------------------
void vtkSeedWidget::SetProcessEvents(vtkTypeBool pe)
{
  this->Superclass::SetProcessEvents(pe);

  vtkSeedListIterator iter = this->Seeds->begin();
  for (; iter != this->Seeds->end(); ++iter)
  {
    (*iter)->SetProcessEvents(pe);
  }
}

//------------------------------------------------------------------------------
void vtkSeedWidget::SetInteractor(vtkRenderWindowInteractor* rwi)
{
  vtkSeedListIterator iter = this->Seeds->begin();
  for (; iter != this->Seeds->end(); ++iter)
  {
    (*iter)->SetInteractor(rwi);
  }
  this->Superclass::SetInteractor(rwi);
}

//------------------------------------------------------------------------------
void vtkSeedWidget::SetCurrentRenderer(vtkRenderer* ren)
{
  this->Superclass::SetCurrentRenderer(ren);
  vtkSeedListIterator iter = this->Seeds->begin();
  for (; iter != this->Seeds->end(); ++iter)
  {
    if (!ren)
    {
      // Disable widget if it's being removed from the renderer
      (*iter)->EnabledOff();
    }
    (*iter)->SetCurrentRenderer(ren);
  }
}

//------------------------------------------------------------------------------
// Programmatically create a new handle.
vtkHandleWidget* vtkSeedWidget::CreateNewHandle()
{
  vtkSeedRepresentation* rep = vtkSeedRepresentation::SafeDownCast(this->WidgetRep);
  if (!rep)
  {
    vtkErrorMacro(<< "Please set, or create a default seed representation "
                  << "before adding requesting creation of a new handle.");
    return nullptr;
  }

  // Create the handle widget or reuse an old one
  int currentHandleNumber = static_cast<int>(this->Seeds->size());
  vtkHandleWidget* widget = vtkHandleWidget::New();

  // Configure the handle widget
  widget->SetParent(this);
  widget->SetInteractor(this->Interactor);
  vtkHandleRepresentation* handleRep = rep->GetHandleRepresentation(currentHandleNumber);
  if (!handleRep)
  {
    widget->Delete();
    return nullptr;
  }
  else
  {
    handleRep->SetRenderer(this->CurrentRenderer);
    widget->SetRepresentation(handleRep);

    // Now place the widget into the list of handle widgets (if not already there)
    this->Seeds->push_back(widget);
    return widget;
  }
}

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

  os << indent << "WidgetState: " << this->WidgetState << endl;
}
VTK_ABI_NAMESPACE_END
