// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkWidgetCallbackMapper.h"
#include "vtkAbstractWidget.h"
#include "vtkCommand.h"
#include "vtkEventData.h"
#include "vtkObjectFactory.h"
#include "vtkWidgetEventTranslator.h"
#include <map>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkWidgetCallbackMapper);

// Callbacks are stored as a pair of (Object,Method) in the map.
struct vtkCallbackPair
{
  vtkCallbackPair()
    : Widget(nullptr)
    , Callback(nullptr)
  {
  } // map requires empty constructor
  vtkCallbackPair(vtkAbstractWidget* w, vtkWidgetCallbackMapper::CallbackType f)
    : Widget(w)
    , Callback(f)
  {
  }

  vtkAbstractWidget* Widget;
  vtkWidgetCallbackMapper::CallbackType Callback;
};

// The map tracks the correspondence between widget events and callbacks
class vtkCallbackMap : public std::map<unsigned long, vtkCallbackPair>
{
public:
  typedef vtkCallbackMap CallbackMapType;
  typedef std::map<unsigned long, vtkCallbackPair>::iterator CallbackMapIterator;
};

//------------------------------------------------------------------------------
vtkWidgetCallbackMapper::vtkWidgetCallbackMapper()
{
  this->CallbackMap = new vtkCallbackMap;
  this->EventTranslator = nullptr;
}

//------------------------------------------------------------------------------
vtkWidgetCallbackMapper::~vtkWidgetCallbackMapper()
{
  delete this->CallbackMap;
  if (this->EventTranslator)
  {
    this->EventTranslator->Delete();
  }
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::SetEventTranslator(vtkWidgetEventTranslator* t)
{
  if (this->EventTranslator != t)
  {
    if (this->EventTranslator)
    {
      this->EventTranslator->Delete();
    }
    this->EventTranslator = t;
    if (this->EventTranslator)
    {
      this->EventTranslator->Register(this);
    }

    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::SetCallbackMethod(
  unsigned long VTKEvent, unsigned long widgetEvent, vtkAbstractWidget* w, CallbackType f)
{
  this->EventTranslator->SetTranslation(VTKEvent, widgetEvent);
  this->SetCallbackMethod(widgetEvent, w, f);
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::SetCallbackMethod(unsigned long VTKEvent, int modifier, char keyCode,
  int repeatCount, const char* keySym, unsigned long widgetEvent, vtkAbstractWidget* w,
  CallbackType f)
{
  this->EventTranslator->SetTranslation(
    VTKEvent, modifier, keyCode, repeatCount, keySym, widgetEvent);
  this->SetCallbackMethod(widgetEvent, w, f);
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::SetCallbackMethod(unsigned long VTKEvent, vtkEventData* edata,
  unsigned long widgetEvent, vtkAbstractWidget* w, CallbackType f)
{
  // make sure the type is set
  edata->SetType(VTKEvent);

  this->EventTranslator->SetTranslation(VTKEvent, edata, widgetEvent);
  this->SetCallbackMethod(widgetEvent, w, f);
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::SetCallbackMethod(
  unsigned long widgetEvent, vtkAbstractWidget* w, CallbackType f)
{
  (*this->CallbackMap)[widgetEvent] = vtkCallbackPair(w, f);
}

//------------------------------------------------------------------------------
void vtkWidgetCallbackMapper::InvokeCallback(unsigned long widgetEvent)
{
  vtkCallbackMap::CallbackMapIterator iter = this->CallbackMap->find(widgetEvent);
  if (iter != this->CallbackMap->end())
  {
    vtkAbstractWidget* w = (*iter).second.Widget;
    CallbackType f = (*iter).second.Callback;
    (*f)(w);
  }
}

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

  os << indent << "Event Translator: ";
  if (this->EventTranslator)
  {
    os << this->EventTranslator << "\n";
  }
  else
  {
    os << "(none)\n";
  }
}
VTK_ABI_NAMESPACE_END
