// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkAbstractWidget.h"
#include "vtkCallbackCommand.h"
#include "vtkCommand.h"
#include "vtkEvent.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkWidgetCallbackMapper.h"
#include "vtkWidgetEvent.h"
#include "vtkWidgetEventTranslator.h"
#include "vtkWidgetRepresentation.h"

//------------------------------------------------------------------------------
VTK_ABI_NAMESPACE_BEGIN
vtkAbstractWidget::vtkAbstractWidget()
{
  // Setup event processing
  this->EventCallbackCommand->SetCallback(vtkAbstractWidget::ProcessEventsHandler);

  // There is no parent to this widget currently
  this->Parent = nullptr;

  // Set up the geometry
  this->WidgetRep = nullptr;

  // Set priority higher than interactor styles
  this->Priority = 0.5;

  // Does this widget manage a cursor?
  this->ManagesCursor = 1;

  // Does this widget respond to interaction?
  this->ProcessEvents = 1;

  // Okay, set up the event translations for the subclasses.
  this->EventTranslator = vtkWidgetEventTranslator::New();
  this->CallbackMapper = vtkWidgetCallbackMapper::New();
  this->CallbackMapper->SetEventTranslator(this->EventTranslator);
}

//------------------------------------------------------------------------------
vtkAbstractWidget::~vtkAbstractWidget()
{
  if (this->WidgetRep)
  {
    // Remove the representation from the renderer.
    vtkRenderer* ren = this->WidgetRep->GetRenderer();
    if (ren)
    {
      ren->RemoveViewProp(this->WidgetRep);
    }
    this->WidgetRep->Delete();
    this->WidgetRep = nullptr;
  }

  this->EventTranslator->Delete();
  this->CallbackMapper->Delete();
  this->SetEnabled(0);
}

//------------------------------------------------------------------------------
void vtkAbstractWidget::SetWidgetRepresentation(vtkWidgetRepresentation* r)
{
  if (r != this->WidgetRep)
  {
    int enabled = 0;
    if (this->Enabled)
    {
      enabled = 1;
      this->SetEnabled(0);
    }

    if (this->WidgetRep)
    {
      this->WidgetRep->Delete();
    }
    this->WidgetRep = r;
    if (this->WidgetRep)
    {
      this->WidgetRep->Register(this);
    }
    this->Modified();

    if (enabled)
    {
      this->SetEnabled(1);
    }
  }
}

//------------------------------------------------------------------------------
void vtkAbstractWidget::SetEnabled(int enabling)
{
  if (enabling) //----------------
  {
    vtkDebugMacro(<< "Enabling widget");

    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;
    if (!this->WidgetRep)
    {
      this->CreateDefaultRepresentation();
    }
    this->WidgetRep->SetRenderer(this->CurrentRenderer);
    this->WidgetRep->RegisterPickers();

    // 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());
    }

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

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

  else // disabling------------------
  {
    vtkDebugMacro(<< "Disabling widget");

    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);
    }

    if (this->CurrentRenderer)
    {
      this->CurrentRenderer->RemoveViewProp(this->WidgetRep);
    }

    this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
    this->SetCurrentRenderer(nullptr);
    if (this->WidgetRep)
    {
      this->WidgetRep->UnRegisterPickers();
    }
  }

  // We no longer call render when enabled state changes. It's the applications
  // responsibility to explicitly call render after changing enable state.
  //// Should only render if there is no parent
  // if ( this->Interactor && !this->Parent )
  //  {
  //  this->Interactor->Render();
  //  }
}

//------------------------------------------------------------------------------
void vtkAbstractWidget::ProcessEventsHandler(
  vtkObject* vtkNotUsed(object), unsigned long vtkEvent, void* clientdata, void* calldata)
{
  vtkAbstractWidget* self = reinterpret_cast<vtkAbstractWidget*>(clientdata);

  // if ProcessEvents is Off, we ignore all interaction events.
  if (!self->GetProcessEvents())
  {
    return;
  }

  // if the event has data then get the translation using the
  // event data
  unsigned long widgetEvent = vtkWidgetEvent::NoEvent;
  if (calldata && vtkCommand::EventHasData(vtkEvent))
  {
    widgetEvent =
      self->EventTranslator->GetTranslation(vtkEvent, static_cast<vtkEventData*>(calldata));
  }
  else
  {
    int modifier = vtkEvent::GetModifier(self->Interactor);

    // If neither the ctrl nor the shift keys are pressed, give
    // NoModifier a preference over AnyModifier.
    if (modifier == vtkEvent::AnyModifier)
    {
      widgetEvent = self->EventTranslator->GetTranslation(vtkEvent, vtkEvent::NoModifier,
        self->Interactor->GetKeyCode(), self->Interactor->GetRepeatCount(),
        self->Interactor->GetKeySym());
    }

    if (widgetEvent == vtkWidgetEvent::NoEvent)
    {
      widgetEvent =
        self->EventTranslator->GetTranslation(vtkEvent, modifier, self->Interactor->GetKeyCode(),
          self->Interactor->GetRepeatCount(), self->Interactor->GetKeySym());
    }
  }

  // Save the call data for widgets if needed
  self->CallData = calldata;

  // Invoke the widget callback
  if (widgetEvent != vtkWidgetEvent::NoEvent)
  {
    self->CallbackMapper->InvokeCallback(widgetEvent);
  }
}

//------------------------------------------------------------------------------
void vtkAbstractWidget::Render()
{
  if (!this->Parent && this->Interactor)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
void vtkAbstractWidget::SetPriority(float f)
{
  if (f != this->Priority)
  {
    this->Superclass::SetPriority(f);

    // We are going to re-add all the events to the interactor. The
    // interactor observer maintains a sorted list of command-observers, sorted
    // by priority. The sorting happens only during insertion of a command-
    // observer into the list. Yeah.. Look at the documentation of SetPriority
    // in vtkInteractorObserver. That documentation recommends setting the
    // interactor to nullptr and back again. We won't do that because it will
    // cause two unnecessary re-renders, (cause we'd have had to enable and
    // disable the widgets).
    if (this->Enabled)
    {
      if (this->Interactor)
      {
        this->Interactor->RemoveObserver(this->CharObserverTag);
        this->Interactor->RemoveObserver(this->DeleteObserverTag);
        this->CharObserverTag = this->Interactor->AddObserver(
          vtkCommand::CharEvent, this->KeyPressCallbackCommand, this->Priority);
        this->DeleteObserverTag = this->Interactor->AddObserver(
          vtkCommand::DeleteEvent, this->KeyPressCallbackCommand, this->Priority);
      }

      if (!this->Parent)
      {
        if (this->Interactor)
        {
          this->Interactor->RemoveObserver(this->EventCallbackCommand);
        }
      }
      else
      {
        this->Parent->RemoveObserver(this->EventCallbackCommand);
      }

      if (!this->Parent)
      {
        if (this->Interactor)
        {
          this->EventTranslator->AddEventsToInteractor(
            this->Interactor, this->EventCallbackCommand, this->Priority);
        }
      }
      else
      {
        this->EventTranslator->AddEventsToParent(
          this->Parent, this->EventCallbackCommand, this->Priority);
      }
    }
  }
}

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

  os << indent << "ProcessEvents: " << (this->ProcessEvents ? "On" : "Off") << "\n";

  if (this->WidgetRep)
  {
    os << indent << "Widget Representation: " << this->WidgetRep << "\n";
  }
  else
  {
    os << indent << "Widget Representation: (none)\n";
  }

  os << indent << "Manages Cursor: " << (this->ManagesCursor ? "On" : "Off") << "\n";

  os << indent << "Parent: " << this->Parent << "\n";
}
VTK_ABI_NAMESPACE_END
