// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkLogoRepresentation.h"
#include "vtkCallbackCommand.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkPropCollection.h"
#include "vtkProperty2D.h"
#include "vtkRenderer.h"
#include "vtkTexture.h"
#include "vtkTexturedActor2D.h"
#include "vtkWindow.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkLogoRepresentation);

vtkCxxSetObjectMacro(vtkLogoRepresentation, Image, vtkImageData);
vtkCxxSetObjectMacro(vtkLogoRepresentation, ImageProperty, vtkProperty2D);

//------------------------------------------------------------------------------
vtkLogoRepresentation::vtkLogoRepresentation()
{
  // Initialize the data members
  this->Image = nullptr;
  this->ImageProperty = vtkProperty2D::New();

  // Setup the pipeline
  this->Texture = vtkTexture::New();
  this->TexturePolyData = vtkPolyData::New();
  this->TexturePoints = vtkPoints::New();
  this->TexturePoints->SetNumberOfPoints(4);
  this->TexturePolyData->SetPoints(this->TexturePoints);
  vtkCellArray* polys = vtkCellArray::New();
  polys->InsertNextCell(4);
  polys->InsertCellPoint(0);
  polys->InsertCellPoint(1);
  polys->InsertCellPoint(2);
  polys->InsertCellPoint(3);
  this->TexturePolyData->SetPolys(polys);
  polys->Delete();
  vtkFloatArray* tc = vtkFloatArray::New();
  tc->SetNumberOfComponents(2);
  tc->SetNumberOfTuples(4);
  tc->InsertComponent(0, 0, 0.0);
  tc->InsertComponent(0, 1, 0.0);
  tc->InsertComponent(1, 0, 1.0);
  tc->InsertComponent(1, 1, 0.0);
  tc->InsertComponent(2, 0, 1.0);
  tc->InsertComponent(2, 1, 1.0);
  tc->InsertComponent(3, 0, 0.0);
  tc->InsertComponent(3, 1, 1.0);
  this->TexturePolyData->GetPointData()->SetTCoords(tc);
  tc->Delete();
  this->TextureMapper = vtkPolyDataMapper2D::New();
  this->TextureMapper->SetInputData(this->TexturePolyData);
  this->TextureActor = vtkTexturedActor2D::New();
  this->TextureActor->SetMapper(this->TextureMapper);
  this->TextureActor->SetTexture(this->Texture);
  this->ImageProperty->SetOpacity(0.25);
  this->TextureActor->SetProperty(this->ImageProperty);

  // Set up parameters from the superclass
  double size[2];
  this->GetSize(size);
  this->Position2Coordinate->SetValue(0.04 * size[0], 0.04 * size[1]);
  this->ProportionalResize = 1;
  this->Moving = 1;
  this->SetShowBorderToActive();
  this->PositionCoordinate->SetValue(0.9, 0.025);
  this->Position2Coordinate->SetValue(0.075, 0.075);
}

//------------------------------------------------------------------------------
vtkLogoRepresentation::~vtkLogoRepresentation()
{
  if (this->Image)
  {
    this->Image->Delete();
  }
  this->ImageProperty->Delete();
  this->Texture->Delete();
  this->TexturePoints->Delete();
  this->TexturePolyData->Delete();
  this->TextureMapper->Delete();
  this->TextureActor->Delete();
}

//------------------------------------------------------------------------------
void vtkLogoRepresentation::AdjustImageSize(double o[2], double borderSize[2], double imageSize[2])
{
  // Scale the image to fit with in the border.
  // Also update the origin so the image is centered.
  double r0 = borderSize[0] / imageSize[0];
  double r1 = borderSize[1] / imageSize[1];
  if (r0 > r1)
  {
    imageSize[0] *= r1;
    imageSize[1] *= r1;
  }
  else
  {
    imageSize[0] *= r0;
    imageSize[1] *= r0;
  }

  if (imageSize[0] < borderSize[0])
  {
    o[0] += (borderSize[0] - imageSize[0]) / 2.0;
  }
  if (imageSize[1] < borderSize[1])
  {
    o[1] += (borderSize[1] - imageSize[1]) / 2.0;
  }
}

//------------------------------------------------------------------------------
void vtkLogoRepresentation::BuildRepresentation()
{
  if (this->GetMTime() > this->BuildTime ||
    (this->Renderer && this->Renderer->GetVTKWindow() &&
      this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime))
  {

    // Determine and adjust the size of the image
    if (this->Image)
    {
      double imageSize[2], borderSize[2], o[2];
      imageSize[0] = 0.0;
      imageSize[1] = 0.0;
      // this->Image->Update();
      if (this->Image->GetDataDimension() == 2)
      {
        int dims[3];
        this->Image->GetDimensions(dims);
        imageSize[0] = static_cast<double>(dims[0]);
        imageSize[1] = static_cast<double>(dims[1]);
      }
      int* p1 = this->PositionCoordinate->GetComputedDisplayValue(this->Renderer);
      int* p2 = this->Position2Coordinate->GetComputedDisplayValue(this->Renderer);
      borderSize[0] = p2[0] - p1[0];
      borderSize[1] = p2[1] - p1[1];
      o[0] = static_cast<double>(p1[0]);
      o[1] = static_cast<double>(p1[1]);

      // this preserves the image aspect ratio. The image is
      // centered around the center of the bordered ragion.
      this->AdjustImageSize(o, borderSize, imageSize);

      // Update the points
      this->Texture->SetInputData(this->Image);
      this->Texture->InterpolateOn();
      this->TexturePoints->SetPoint(0, o[0], o[1], 0.0);
      this->TexturePoints->SetPoint(1, o[0] + imageSize[0], o[1], 0.0);
      this->TexturePoints->SetPoint(2, o[0] + imageSize[0], o[1] + imageSize[1], 0.0);
      this->TexturePoints->SetPoint(3, o[0], o[1] + imageSize[1], 0.0);
      // For GL backend 2 it is important to modify the point array
      this->TexturePoints->Modified();
    }
  }

  // Note that the transform is updated by the superclass
  this->Superclass::BuildRepresentation();
}

//------------------------------------------------------------------------------
void vtkLogoRepresentation::GetActors2D(vtkPropCollection* pc)
{
  if (pc != nullptr && this->GetVisibility())
  {
    pc->AddItem(this->TextureActor);
  }
  this->Superclass::GetActors2D(pc);
}

//------------------------------------------------------------------------------
void vtkLogoRepresentation::ReleaseGraphicsResources(vtkWindow* w)
{
  this->TextureActor->ReleaseGraphicsResources(w);
  this->Superclass::ReleaseGraphicsResources(w);
}

//------------------------------------------------------------------------------
int vtkLogoRepresentation::RenderOverlay(vtkViewport* v)
{
  int count = 0;
  if (this->TextureActor->GetVisibility())
  {
    vtkRenderer* ren = vtkRenderer::SafeDownCast(v);
    if (ren)
    {
      count += this->TextureActor->RenderOverlay(v);
    }
    // Display border on top of logo
    count += this->Superclass::RenderOverlay(v);
  }
  return count;
}

//------------------------------------------------------------------------------
void vtkLogoRepresentation::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  if (this->Image)
  {
    os << indent << "Image:\n";
    this->Image->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Image: (none)\n";
  }

  if (this->ImageProperty)
  {
    os << indent << "Image Property:\n";
    this->ImageProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Image Property: (none)\n";
  }
}
VTK_ABI_NAMESPACE_END
