// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkAngleWidget.h"
#include "vtkAngleRepresentation2D.h"
#include "vtkCallbackCommand.h"
#include "vtkCoordinate.h"
#include "vtkHandleRepresentation.h"
#include "vtkHandleWidget.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkWidgetCallbackMapper.h"
#include "vtkWidgetEvent.h"
#include "vtkWidgetEventTranslator.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkAngleWidget);

// The angle widget observes the handles.
// Here we create the command/observer classes to respond to the
// slider widgets.
class vtkAngleWidgetCallback : public vtkCommand
{
public:
  static vtkAngleWidgetCallback* New() { return new vtkAngleWidgetCallback; }
  void Execute(vtkObject*, unsigned long eventId, void*) override
  {
    switch (eventId)
    {
      case vtkCommand::StartInteractionEvent:
        this->AngleWidget->StartAngleInteraction(this->HandleNumber);
        break;
      case vtkCommand::InteractionEvent:
        this->AngleWidget->AngleInteraction(this->HandleNumber);
        break;
      case vtkCommand::EndInteractionEvent:
        this->AngleWidget->EndAngleInteraction(this->HandleNumber);
        break;
    }
  }
  int HandleNumber;
  vtkAngleWidget* AngleWidget;
};

//------------------------------------------------------------------------------
vtkAngleWidget::vtkAngleWidget()
{
  this->ManagesCursor = 0;

  this->WidgetState = vtkAngleWidget::Start;
  this->CurrentHandle = 0;

  // The widgets for moving the end points. They observe this widget (i.e.,
  // this widget is the parent to the handles).
  this->Point1Widget = vtkHandleWidget::New();
  this->Point1Widget->SetParent(this);
  this->CenterWidget = vtkHandleWidget::New();
  this->CenterWidget->SetParent(this);
  this->Point2Widget = vtkHandleWidget::New();
  this->Point2Widget->SetParent(this);

  // Set up the callbacks on the two handles
  this->AngleWidgetCallback1 = vtkAngleWidgetCallback::New();
  this->AngleWidgetCallback1->HandleNumber = 0;
  this->AngleWidgetCallback1->AngleWidget = this;
  this->Point1Widget->AddObserver(
    vtkCommand::StartInteractionEvent, this->AngleWidgetCallback1, this->Priority);
  this->Point1Widget->AddObserver(
    vtkCommand::InteractionEvent, this->AngleWidgetCallback1, this->Priority);
  this->Point1Widget->AddObserver(
    vtkCommand::EndInteractionEvent, this->AngleWidgetCallback1, this->Priority);

  // Set up the callbacks on the two handles
  this->AngleWidgetCenterCallback = vtkAngleWidgetCallback::New();
  this->AngleWidgetCenterCallback->HandleNumber = 1;
  this->AngleWidgetCenterCallback->AngleWidget = this;
  this->CenterWidget->AddObserver(
    vtkCommand::StartInteractionEvent, this->AngleWidgetCenterCallback, this->Priority);
  this->CenterWidget->AddObserver(
    vtkCommand::InteractionEvent, this->AngleWidgetCenterCallback, this->Priority);
  this->CenterWidget->AddObserver(
    vtkCommand::EndInteractionEvent, this->AngleWidgetCenterCallback, this->Priority);

  this->AngleWidgetCallback2 = vtkAngleWidgetCallback::New();
  this->AngleWidgetCallback2->HandleNumber = 2;
  this->AngleWidgetCallback2->AngleWidget = this;
  this->Point2Widget->AddObserver(
    vtkCommand::StartInteractionEvent, this->AngleWidgetCallback2, this->Priority);
  this->Point2Widget->AddObserver(
    vtkCommand::InteractionEvent, this->AngleWidgetCallback2, this->Priority);
  this->Point2Widget->AddObserver(
    vtkCommand::EndInteractionEvent, this->AngleWidgetCallback2, this->Priority);

  // These are the event callbacks supported by this widget
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
    vtkWidgetEvent::AddPoint, this, vtkAngleWidget::AddPointAction);
  this->CallbackMapper->SetCallbackMethod(
    vtkCommand::MouseMoveEvent, vtkWidgetEvent::Move, this, vtkAngleWidget::MoveAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent,
    vtkWidgetEvent::EndSelect, this, vtkAngleWidget::EndSelectAction);
}

//------------------------------------------------------------------------------
vtkAngleWidget::~vtkAngleWidget()
{
  this->Point1Widget->RemoveObserver(this->AngleWidgetCallback1);
  this->Point1Widget->Delete();
  this->AngleWidgetCallback1->Delete();

  this->CenterWidget->RemoveObserver(this->AngleWidgetCenterCallback);
  this->CenterWidget->Delete();
  this->AngleWidgetCenterCallback->Delete();

  this->Point2Widget->RemoveObserver(this->AngleWidgetCallback2);
  this->Point2Widget->Delete();
  this->AngleWidgetCallback2->Delete();
}

//------------------------------------------------------------------------------
void vtkAngleWidget::CreateDefaultRepresentation()
{
  if (!this->WidgetRep)
  {
    this->WidgetRep = vtkAngleRepresentation2D::New();
  }
  reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->InstantiateHandleRepresentation();
}

//------------------------------------------------------------------------------
void vtkAngleWidget::SetEnabled(int enabling)
{
  // The handle widgets are not actually enabled until they are placed.
  // The handle widgets take their representation from the vtkAngleRepresentation.
  if (enabling)
  {
    if (this->WidgetState == vtkAngleWidget::Start)
    {
      if (this->WidgetRep)
      {
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->Ray1VisibilityOff();
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->Ray2VisibilityOff();
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->ArcVisibilityOff();
      }
    }
    else
    {
      if (this->WidgetRep)
      {
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->Ray1VisibilityOn();
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->Ray2VisibilityOn();
        reinterpret_cast<vtkAngleRepresentation*>(this->WidgetRep)->ArcVisibilityOn();
      }

      // The interactor must be set prior to enabling the widget.
      if (this->Interactor)
      {
        this->Point1Widget->SetInteractor(this->Interactor);
        this->CenterWidget->SetInteractor(this->Interactor);
        this->Point2Widget->SetInteractor(this->Interactor);
      }

      this->Point1Widget->SetEnabled(1);
      this->CenterWidget->SetEnabled(1);
      this->Point2Widget->SetEnabled(1);
    }
  }

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

    if (!this->Interactor)
    {
      vtkErrorMacro(<< "The interactor must be set prior to enabling the widget");
      return;
    }

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

    if (!this->CurrentRenderer)
    {
      this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(X, Y));

      if (this->CurrentRenderer == nullptr)
      {
        return;
      }
    }

    // We're ready to enable
    this->Enabled = 1;
    this->CreateDefaultRepresentation();
    this->WidgetRep->SetRenderer(this->CurrentRenderer);

    // listen for the events found in the EventTranslator
    if (!this->Parent)
    {
      this->EventTranslator->AddEventsToInteractor(
        this->Interactor, this->EventCallbackCommand, this->Priority);
    }
    else
    {
      this->EventTranslator->AddEventsToParent(
        this->Parent, this->EventCallbackCommand, this->Priority);
    }

    if (this->ManagesCursor)
    {
      this->WidgetRep->ComputeInteractionState(X, Y);
      this->SetCursor(this->WidgetRep->GetInteractionState());
    }

    vtkAngleRepresentation* rep = static_cast<vtkAngleRepresentation*>(this->WidgetRep);

    // Set the renderer, representation and interactor on the child widgets.
    if (this->Point1Widget)
    {
      this->Point1Widget->SetRepresentation(rep->GetPoint1Representation());
      this->Point1Widget->SetInteractor(this->Interactor);
      this->Point1Widget->GetRepresentation()->SetRenderer(this->CurrentRenderer);
    }

    if (this->CenterWidget)
    {
      this->CenterWidget->SetRepresentation(rep->GetCenterRepresentation());
      this->CenterWidget->SetInteractor(this->Interactor);
      this->CenterWidget->GetRepresentation()->SetRenderer(this->CurrentRenderer);
    }

    if (this->Point2Widget)
    {
      this->Point2Widget->SetRepresentation(rep->GetPoint2Representation());
      this->Point2Widget->SetInteractor(this->Interactor);
      this->Point2Widget->GetRepresentation()->SetRenderer(this->CurrentRenderer);
    }

    if (rep)
    {
      rep->SetRay1Visibility(this->WidgetState != vtkAngleWidget::Start ? 1 : 0);
      rep->SetRay2Visibility(this->WidgetState != vtkAngleWidget::Start ? 1 : 0);
      rep->SetArcVisibility(this->WidgetState != vtkAngleWidget::Start ? 1 : 0);
    }

    if (this->WidgetState != vtkAngleWidget::Start)
    {
      if (this->Point1Widget)
      {
        this->Point1Widget->SetEnabled(1);
      }
      if (this->CenterWidget)
      {
        this->CenterWidget->SetEnabled(1);
      }
      if (this->Point2Widget)
      {
        this->Point2Widget->SetEnabled(1);
      }
    }

    this->WidgetRep->BuildRepresentation();
    this->CurrentRenderer->AddViewProp(this->WidgetRep);

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

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

    this->Enabled = 0;

    // don't listen for events any more
    if (!this->Parent)
    {
      this->Interactor->RemoveObserver(this->EventCallbackCommand);
    }
    else
    {
      this->Parent->RemoveObserver(this->EventCallbackCommand);
    }

    this->CurrentRenderer->RemoveViewProp(this->WidgetRep);

    if (vtkAngleRepresentation* rep = static_cast<vtkAngleRepresentation*>(this->WidgetRep))
    {
      rep->Ray1VisibilityOff();
      rep->Ray2VisibilityOff();
      rep->ArcVisibilityOff();
    }

    if (this->Point1Widget)
    {
      this->Point1Widget->SetEnabled(0);
    }

    if (this->CenterWidget)
    {
      this->CenterWidget->SetEnabled(0);
    }

    if (this->Point2Widget)
    {
      this->Point2Widget->SetEnabled(0);
    }

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

  // Should only render if there is no parent
  if (this->Interactor && !this->Parent)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
vtkTypeBool vtkAngleWidget::IsAngleValid()
{
  if (this->WidgetState == vtkAngleWidget::Manipulate ||
    (this->WidgetState == vtkAngleWidget::Define && this->CurrentHandle == 2))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

// The following methods are the callbacks that the angle widget responds to.
//------------------------------------------------------------------------------
void vtkAngleWidget::AddPointAction(vtkAbstractWidget* w)
{
  vtkAngleWidget* self = reinterpret_cast<vtkAngleWidget*>(w);
  int X = self->Interactor->GetEventPosition()[0];
  int Y = self->Interactor->GetEventPosition()[1];

  // If we are placing the first point it's easy
  if (self->WidgetState == vtkAngleWidget::Start)
  {
    self->GrabFocus(self->EventCallbackCommand);
    self->WidgetState = vtkAngleWidget::Define;
    self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    double e[2];
    e[0] = static_cast<double>(X);
    e[1] = static_cast<double>(Y);
    reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->StartWidgetInteraction(e);
    self->CurrentHandle = 0;
    self->InvokeEvent(vtkCommand::PlacePointEvent, &(self->CurrentHandle));
    reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->Ray1VisibilityOn();
    self->Point1Widget->SetEnabled(1);
    self->CurrentHandle++;
  }

  // If defining we are placing the second or third point
  else if (self->WidgetState == vtkAngleWidget::Define)
  {
    self->InvokeEvent(vtkCommand::PlacePointEvent, &(self->CurrentHandle));
    if (self->CurrentHandle == 1)
    {
      double e[2];
      e[0] = static_cast<double>(X);
      e[1] = static_cast<double>(Y);
      reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->CenterWidgetInteraction(e);
      self->CurrentHandle++;
      self->CenterWidget->SetEnabled(1);
      reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->Ray2VisibilityOn();
      reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->ArcVisibilityOn();
    }
    else if (self->CurrentHandle == 2)
    {
      self->WidgetState = vtkAngleWidget::Manipulate;
      self->Point2Widget->SetEnabled(1);
      self->CurrentHandle = (-1);
      self->ReleaseFocus();
      self->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
    }
  }

  // Maybe we are trying to manipulate the widget handles
  else // if ( self->WidgetState == vtkAngleWidget::Manipulate )
  {
    int state = self->WidgetRep->ComputeInteractionState(X, Y);
    if (state == vtkAngleRepresentation::Outside)
    {
      self->CurrentHandle = (-1);
      return;
    }

    self->GrabFocus(self->EventCallbackCommand);
    if (state == vtkAngleRepresentation::NearP1)
    {
      self->CurrentHandle = 0;
    }
    else if (state == vtkAngleRepresentation::NearCenter)
    {
      self->CurrentHandle = 1;
    }
    else if (state == vtkAngleRepresentation::NearP2)
    {
      self->CurrentHandle = 2;
    }
    self->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
  }

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

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

  // Do nothing if outside
  if (self->WidgetState == vtkAngleWidget::Start)
  {
    return;
  }

  // Delegate the event consistent with the state
  if (self->WidgetState == vtkAngleWidget::Define)
  {
    int X = self->Interactor->GetEventPosition()[0];
    int Y = self->Interactor->GetEventPosition()[1];
    double e[2];
    e[0] = static_cast<double>(X);
    e[1] = static_cast<double>(Y);
    if (self->CurrentHandle == 1)
    {
      reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->CenterWidgetInteraction(e);
    }
    else
    {
      reinterpret_cast<vtkAngleRepresentation*>(self->WidgetRep)->WidgetInteraction(e);
    }
    self->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
    self->EventCallbackCommand->SetAbortFlag(1);
  }

  else // must be moving a handle, invoke a event for the handle widgets
  {
    self->InvokeEvent(vtkCommand::MouseMoveEvent, nullptr);
  }

  self->WidgetRep->BuildRepresentation();
  self->Render();
}

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

  // Do nothing if outside
  if (self->WidgetState == vtkAngleWidget::Start || self->WidgetState == vtkAngleWidget::Define ||
    self->CurrentHandle < 0)
  {
    return;
  }

  self->ReleaseFocus();
  self->InvokeEvent(vtkCommand::LeftButtonReleaseEvent, nullptr);
  self->CurrentHandle = (-1);
  self->WidgetRep->BuildRepresentation();
  self->EventCallbackCommand->SetAbortFlag(1);
  self->Render();
}

// These are callbacks that are active when the user is manipulating the
// handles of the angle widget.
//------------------------------------------------------------------------------
void vtkAngleWidget::StartAngleInteraction(int)
{
  this->Superclass::StartInteraction();
  this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
}

//------------------------------------------------------------------------------
void vtkAngleWidget::AngleInteraction(int)
{
  this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
}

//------------------------------------------------------------------------------
void vtkAngleWidget::EndAngleInteraction(int)
{
  this->Superclass::EndInteraction();

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

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

  // Pass pe flag to component widgets.
  this->Point1Widget->SetProcessEvents(pe);
  this->CenterWidget->SetProcessEvents(pe);
  this->Point2Widget->SetProcessEvents(pe);
}

//------------------------------------------------------------------------------
void vtkAngleWidget::SetWidgetStateToStart()
{
  this->WidgetState = vtkAngleWidget::Start;
  this->CurrentHandle = -1;
  this->ReleaseFocus();
  this->GetRepresentation()->BuildRepresentation(); // update this->Angle
  this->SetEnabled(this->GetEnabled());             // show/hide the handles properly
}

//------------------------------------------------------------------------------
void vtkAngleWidget::SetWidgetStateToManipulate()
{
  this->WidgetState = vtkAngleWidget::Manipulate;
  this->CurrentHandle = -1;
  this->ReleaseFocus();
  this->GetRepresentation()->BuildRepresentation(); // update this->Angle
  this->SetEnabled(this->GetEnabled());             // show/hide the handles properly
}

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