// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkTexturedButtonRepresentation2D.h"
#include "vtkBalloonRepresentation.h"
#include "vtkCamera.h"
#include "vtkCoordinate.h"
#include "vtkImageData.h"
#include "vtkInteractorObserver.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkProperty2D.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include <map>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkTexturedButtonRepresentation2D);

vtkCxxSetObjectMacro(vtkTexturedButtonRepresentation2D, Property, vtkProperty2D);
vtkCxxSetObjectMacro(vtkTexturedButtonRepresentation2D, HoveringProperty, vtkProperty2D);
vtkCxxSetObjectMacro(vtkTexturedButtonRepresentation2D, SelectingProperty, vtkProperty2D);

// Map of textures
class vtkTextureArray : public std::map<int, vtkSmartPointer<vtkImageData>>
{
};
typedef std::map<int, vtkSmartPointer<vtkImageData>>::iterator vtkTextureArrayIterator;

//------------------------------------------------------------------------------
vtkTexturedButtonRepresentation2D::vtkTexturedButtonRepresentation2D()
{
  // Configure the balloon
  this->Balloon = vtkBalloonRepresentation::New();
  this->Balloon->SetOffset(0, 0);

  // Set up the initial properties
  this->CreateDefaultProperties();

  // List of textures
  this->TextureArray = new vtkTextureArray;

  // Anchor point assuming that the button is anchored in 3D
  // If nullptr, then the placement occurs in display space
  this->Anchor = nullptr;
}

//------------------------------------------------------------------------------
vtkTexturedButtonRepresentation2D::~vtkTexturedButtonRepresentation2D()
{
  this->Balloon->Delete();

  if (this->Property)
  {
    this->Property->Delete();
    this->Property = nullptr;
  }

  if (this->HoveringProperty)
  {
    this->HoveringProperty->Delete();
    this->HoveringProperty = nullptr;
  }

  if (this->SelectingProperty)
  {
    this->SelectingProperty->Delete();
    this->SelectingProperty = nullptr;
  }

  delete this->TextureArray;

  if (this->Anchor)
  {
    this->Anchor->Delete();
  }
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::SetButtonTexture(int i, vtkImageData* image)
{
  i = std::max(i, 0);
  i = std::min(i, this->NumberOfStates - 1);

  (*this->TextureArray)[i] = image;
}

//------------------------------------------------------------------------------
vtkImageData* vtkTexturedButtonRepresentation2D::GetButtonTexture(int i)
{
  i = std::max(i, 0);
  i = std::min(i, this->NumberOfStates - 1);

  vtkTextureArrayIterator iter = this->TextureArray->find(i);
  if (iter != this->TextureArray->end())
  {
    return (*iter).second;
  }
  else
  {
    return nullptr;
  }
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::PlaceWidget(double bds[6])
{
  int i;
  double bounds[6], center[3];

  this->AdjustBounds(bds, bounds, center);
  for (i = 0; i < 6; i++)
  {
    this->InitialBounds[i] = bounds[i];
  }
  this->InitialLength = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) +
    (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
    (bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));

  if (this->Anchor)
  { // no longer in world space
    this->Anchor->Delete();
    this->Anchor = nullptr;
  }

  double e[2];
  e[0] = bounds[0];
  e[1] = bounds[2];
  this->Balloon->StartWidgetInteraction(e);
  this->Balloon->SetImageSize(
    static_cast<int>(bounds[1] - bounds[0]), static_cast<int>(bounds[3] - bounds[2]));
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::PlaceWidget(double anchor[3], int size[2])
{
  if (!this->Anchor)
  {
    this->Anchor = vtkCoordinate::New();
    this->Anchor->SetCoordinateSystemToWorld();
  }

  this->Anchor->SetValue(anchor);

  double e[2];
  e[0] = e[1] = 0.0;
  if (this->Renderer)
  {
    double* p = this->Anchor->GetComputedDoubleDisplayValue(this->Renderer);
    this->Balloon->SetRenderer(this->Renderer);
    this->Balloon->StartWidgetInteraction(p);
    e[0] = p[0];
    e[1] = p[1];
  }
  else
  {
    this->Balloon->StartWidgetInteraction(e);
  }

  this->Balloon->SetImageSize(size);

  this->InitialBounds[0] = e[0];
  this->InitialBounds[1] = e[0] + size[0];
  this->InitialBounds[2] = e[1];
  this->InitialBounds[3] = e[1] + size[1];
  this->InitialBounds[4] = this->InitialBounds[5] = 0.0;

  double* bounds = this->InitialBounds;
  this->InitialLength = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) +
    (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
    (bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));
}

//------------------------------------------------------------------------------
int vtkTexturedButtonRepresentation2D ::ComputeInteractionState(
  int X, int Y, int vtkNotUsed(modify))
{
  this->Balloon->SetRenderer(this->GetRenderer());
  if (this->Balloon->ComputeInteractionState(X, Y) == vtkBalloonRepresentation::OnImage)
  {
    this->InteractionState = vtkButtonRepresentation::Inside;
  }
  else
  {
    this->InteractionState = vtkButtonRepresentation::Outside;
  }

  return this->InteractionState;
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::Highlight(int highlight)
{
  this->Superclass::Highlight(highlight);

  vtkProperty2D* initialProperty = this->Balloon->GetImageProperty();
  vtkProperty2D* selectedProperty;

  if (highlight == vtkButtonRepresentation::HighlightHovering)
  {
    this->Balloon->SetImageProperty(this->HoveringProperty);
    selectedProperty = this->HoveringProperty;
  }
  else if (highlight == vtkButtonRepresentation::HighlightSelecting)
  {
    this->Balloon->SetImageProperty(this->SelectingProperty);
    selectedProperty = this->SelectingProperty;
  }
  else // if ( highlight == vtkButtonRepresentation::HighlightNormal )
  {
    this->Balloon->SetImageProperty(this->Property);
    selectedProperty = this->Property;
  }

  if (selectedProperty != initialProperty)
  {
    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::CreateDefaultProperties()
{
  this->Property = vtkProperty2D::New();
  this->Property->SetColor(0.9, 0.9, 0.9);

  this->HoveringProperty = vtkProperty2D::New();
  this->HoveringProperty->SetColor(1, 1, 1);

  this->SelectingProperty = vtkProperty2D::New();
  this->SelectingProperty->SetColor(0.5, 0.5, 0.5);
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::BuildRepresentation()
{
  // The net effect is to resize the handle
  if (this->GetMTime() > this->BuildTime ||
    (this->Renderer && this->Renderer->GetActiveCamera()->GetMTime() > this->BuildTime) ||
    (this->Renderer && this->Renderer->GetVTKWindow() &&
      this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime))
  {
    this->Balloon->SetRenderer(this->Renderer);

    // Setup the texture
    vtkTextureArrayIterator iter = this->TextureArray->find(this->State);
    if (iter != this->TextureArray->end())
    {
      this->Balloon->SetBalloonImage((*iter).second);
    }
    else
    {
      this->Balloon->SetBalloonImage(nullptr);
    }

    // Update the position if anchored in world coordinates
    if (this->Anchor)
    {
      double* p = this->Anchor->GetComputedDoubleDisplayValue(this->Renderer);
      this->Balloon->StartWidgetInteraction(p);
      this->Balloon->Modified();
    }

    this->BuildTime.Modified();
  }
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::ShallowCopy(vtkProp* prop)
{
  vtkTexturedButtonRepresentation2D* rep = vtkTexturedButtonRepresentation2D::SafeDownCast(prop);
  if (rep)
  {
    this->Property->DeepCopy(rep->Property);
    this->HoveringProperty->DeepCopy(rep->HoveringProperty);
    this->SelectingProperty->DeepCopy(rep->SelectingProperty);

    vtkTextureArrayIterator iter;
    for (iter = rep->TextureArray->begin(); iter != rep->TextureArray->end(); ++iter)
    {
      (*this->TextureArray)[(*iter).first] = (*iter).second;
    }
  }
  this->Superclass::ShallowCopy(prop);
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::ReleaseGraphicsResources(vtkWindow* win)
{
  this->Balloon->ReleaseGraphicsResources(win);
}

//------------------------------------------------------------------------------
int vtkTexturedButtonRepresentation2D::RenderOverlay(vtkViewport* viewport)
{
  this->BuildRepresentation();

  return this->Balloon->RenderOverlay(viewport);
}

//------------------------------------------------------------------------------
vtkTypeBool vtkTexturedButtonRepresentation2D::HasTranslucentPolygonalGeometry()
{
  this->BuildRepresentation();

  return this->Balloon->HasTranslucentPolygonalGeometry();
}

//------------------------------------------------------------------------------
double* vtkTexturedButtonRepresentation2D::GetBounds()
{
  return nullptr;
}

//------------------------------------------------------------------------------
void vtkTexturedButtonRepresentation2D::GetActors(vtkPropCollection* pc)
{
  this->Balloon->GetActors(pc);
}

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

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

  if (this->HoveringProperty)
  {
    os << indent << "Hovering Property: " << this->HoveringProperty << "\n";
  }
  else
  {
    os << indent << "Hovering Property: (none)\n";
  }

  if (this->SelectingProperty)
  {
    os << indent << "Selecting Property: " << this->SelectingProperty << "\n";
  }
  else
  {
    os << indent << "Selecting Property: (none)\n";
  }
}
VTK_ABI_NAMESPACE_END
