// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkOpenGLTextActor3D.h"

#include "vtkCamera.h"
#include "vtkMatrix4x4.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLGL2PSHelper.h"
#include "vtkPath.h"
#include "vtkRenderer.h"
#include "vtkTextProperty.h"
#include "vtkTextRenderer.h"

#include <sstream>
#include <string>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkOpenGLTextActor3D);

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

//------------------------------------------------------------------------------
int vtkOpenGLTextActor3D::RenderTranslucentPolygonalGeometry(vtkViewport* vp)
{
  vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
  if (gl2ps)
  {
    switch (gl2ps->GetActiveState())
    {
      case vtkOpenGLGL2PSHelper::Capture:
        return this->RenderGL2PS(vp, gl2ps);
      case vtkOpenGLGL2PSHelper::Background:
        return 0; // No render.
      case vtkOpenGLGL2PSHelper::Inactive:
        break; // normal render.
    }
  }

  return this->Superclass::RenderTranslucentPolygonalGeometry(vp);
}

//------------------------------------------------------------------------------
vtkOpenGLTextActor3D::vtkOpenGLTextActor3D() = default;

//------------------------------------------------------------------------------
vtkOpenGLTextActor3D::~vtkOpenGLTextActor3D() = default;

//------------------------------------------------------------------------------
int vtkOpenGLTextActor3D::RenderGL2PS(vtkViewport* vp, vtkOpenGLGL2PSHelper* gl2ps)
{
  vtkRenderer* ren = vtkRenderer::SafeDownCast(vp);
  if (!ren)
  {
    vtkWarningMacro("Viewport is not a renderer.");
    return 0;
  }

  // Get path
  std::string input = this->Input && this->Input[0] ? this->Input : "";
  vtkNew<vtkPath> textPath;

  vtkTextRenderer* tren = vtkTextRenderer::GetInstance();
  if (!tren)
  {
    vtkWarningMacro(<< "Cannot generate path data from 3D text string '" << input
                    << "': Text renderer unavailable.");
    return 0;
  }

  if (!tren->StringToPath(this->TextProperty, input, textPath, vtkTextActor3D::GetRenderedDPI()))
  {
    vtkWarningMacro(<< "Failed to generate path data from 3D text string '" << input
                    << "': StringToPath failed.");
    return 0;
  }

  // Get actor info
  vtkNew<vtkMatrix4x4> actorMatrix;
  this->GetModelToWorldMatrix(actorMatrix);
  double actorBounds[6];
  this->GetBounds(actorBounds);
  double textPos[3] = { (actorBounds[1] + actorBounds[0]) * 0.5,
    (actorBounds[3] + actorBounds[2]) * 0.5, (actorBounds[5] + actorBounds[4]) * 0.5 };

  double* fgColord = this->TextProperty->GetColor();
  unsigned char fgColor[4] = { static_cast<unsigned char>(fgColord[0] * 255),
    static_cast<unsigned char>(fgColord[1] * 255), static_cast<unsigned char>(fgColord[2] * 255),
    static_cast<unsigned char>(this->TextProperty->GetOpacity() * 255) };

  // Draw the background quad as a path:
  if (this->TextProperty->GetBackgroundOpacity() > 0.f)
  {
    double* bgColord = this->TextProperty->GetBackgroundColor();
    unsigned char bgColor[4] = { static_cast<unsigned char>(bgColord[0] * 255),
      static_cast<unsigned char>(bgColord[1] * 255), static_cast<unsigned char>(bgColord[2] * 255),
      static_cast<unsigned char>(this->TextProperty->GetBackgroundOpacity() * 255) };

    // Get the camera so we can calculate an offset to place the background
    // behind the text.
    vtkCamera* cam = ren->GetActiveCamera();
    vtkMatrix4x4* mat =
      cam->GetCompositeProjectionTransformMatrix(ren->GetTiledAspectRatio(), 0., 1.);
    double forward[3] = { mat->GetElement(2, 0), mat->GetElement(2, 1), mat->GetElement(2, 2) };
    vtkMath::Normalize(forward);
    double bgPos[3] = { textPos[0] - (forward[0] * 0.0001), textPos[1] - (forward[1] * 0.0001),
      textPos[2] - (forward[2] * 0.0001) };

    vtkTextRenderer::Metrics metrics;
    if (tren->GetMetrics(this->TextProperty, input, metrics, vtkTextActor3D::GetRenderedDPI()))
    {
      vtkNew<vtkPath> bgPath;
      bgPath->InsertNextPoint(static_cast<double>(metrics.TopLeft.GetX()),
        static_cast<double>(metrics.TopLeft.GetY()), 0., vtkPath::MOVE_TO);
      bgPath->InsertNextPoint(static_cast<double>(metrics.TopRight.GetX()),
        static_cast<double>(metrics.TopRight.GetY()), 0., vtkPath::LINE_TO);
      bgPath->InsertNextPoint(static_cast<double>(metrics.BottomRight.GetX()),
        static_cast<double>(metrics.BottomRight.GetY()), 0., vtkPath::LINE_TO);
      bgPath->InsertNextPoint(static_cast<double>(metrics.BottomLeft.GetX()),
        static_cast<double>(metrics.BottomLeft.GetY()), 0., vtkPath::LINE_TO);
      bgPath->InsertNextPoint(static_cast<double>(metrics.TopLeft.GetX()),
        static_cast<double>(metrics.TopLeft.GetY()), 0., vtkPath::LINE_TO);

      std::ostringstream bgLabel;
      bgLabel << "vtkOpenGLTextActor3D::RenderGL2PS background for string: '" << input << "'.";
      gl2ps->Draw3DPath(bgPath, actorMatrix, bgPos, bgColor, ren, bgLabel.str().c_str());
    }
  }

  // Draw the text path:
  std::ostringstream label;
  label << "vtkOpenGLTextActor3D::RenderGL2PS path for string: '" << input << "'.";
  gl2ps->Draw3DPath(textPath, actorMatrix, textPos, fgColor, ren, label.str().c_str());

  return 1;
}
VTK_ABI_NAMESPACE_END
