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

  Program:   Visualization Toolkit
  Module:    vtkAbstractWidget.cxx,v

  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 "vtkAbstractWidget.h"
#include "vtkWidgetRepresentation.h"
#include "vtkCommand.h"
#include "vtkCallbackCommand.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkWidgetEventTranslator.h"
#include "vtkWidgetCallbackMapper.h"
#include "vtkRenderer.h"
#include "vtkEvent.h"
#include "vtkWidgetEvent.h"

//----------------------------------------------------------------------
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
  // resposibility 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 AnyModifer.
    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 prioirty. 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";
}
