// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class vtkGridAxesActor2D
 * @brief actor for an axes plane in a 3D view.
 *
 * vtkGridAxesActor2D is a vtkProp3D that renders a single axes plane (or
 * face). Despite the name, it renders a prop in 3D with ability to render lines
 * for the axes bounds along with labels on them.
 *
 * To use this, one sets the `GridBounds` and then the `Face` which identifies
 * the face of the cube defined by the `GridBounds` along which this prop will
 * render the grid. Parameters available allow additional controls including
 * labeling, grid rendering, edge rendering etc.
 *
 * @sa vtkGridAxesActor3D
 */

#ifndef vtkGridAxesActor2D_h
#define vtkGridAxesActor2D_h

#include "vtkProp3D.h"
#include "vtkRenderingGridAxesModule.h" //needed for exports

#include "vtkGridAxesHelper.h"       // needed of Helper
#include "vtkGridAxesPlaneActor2D.h" // needed for inline methods
#include "vtkNew.h"                  // needed for vtkNew.
#include "vtkSmartPointer.h"         // needed for vtkSmartPointer.

#include <functional> // for std::function

VTK_ABI_NAMESPACE_BEGIN
class vtkAxis;
class vtkContextScene;
class vtkDoubleArray;
class vtkProperty;
class vtkTextProperty;

class VTKRENDERINGGRIDAXES_EXPORT vtkGridAxesActor2D : public vtkProp3D
{
public:
  static vtkGridAxesActor2D* New();
  vtkTypeMacro(vtkGridAxesActor2D, vtkProp3D);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  ///@{
  /**
   * Set the bounding box defining the grid space. This, together with the
   * \c Face identify which planar surface this class is interested in. This
   * class is designed to work with a single planar surface.
   */
  vtkSetVector6Macro(GridBounds, double);
  vtkGetVector6Macro(GridBounds, double);
  ///@}

  ///@{
  /**
   * Indicate which face of the specified bounds is this class operating with.
   *
   * Defaults to vtkGridAxesHelper::MIN_YZ.
   */
  vtkSetClampMacro(Face, int, vtkGridAxesHelper::MIN_YZ, vtkGridAxesHelper::MAX_XY);
  vtkGetMacro(Face, int);
  ///@}

  ///@{
  /**
   * Set the axes to label. The mask can be generated by or-ing values in
   * vtkGridAxesActor2D::LabelMasks to label multiple axes. Any request to label
   * an axes not present on the selected `Face` is ignored e.g. if `Face` is
   * `MIN_YZ`, then `LabelMask` set to `MIN_X` will have no effect.
   *
   * Default is 0xff.
   */
  vtkSetMacro(LabelMask, unsigned int);
  vtkGetMacro(LabelMask, unsigned int);
  ///@}

  /**
   * For some exporters and other other operations we must be
   * able to collect all the actors or volumes. These methods
   * are used in that process.
   * In case the viewport is not a consumer of this prop:
   * call UpdateGeometry() first for updated viewport-specific
   * billboard geometry.
   */
  void GetActors(vtkPropCollection*) override;

  /**
   * Updates the billboard geometry without performing any rendering,
   * to assist GetActors().
   */
  void UpdateGeometry(vtkViewport* viewport, bool doRegularUpdate);

  ///@{
  /**
   * Get/Set the property used to control the appearance of the rendered grid,
   * including edges.
   */
  void SetProperty(vtkProperty*);
  vtkProperty* GetProperty();
  ///@}

  ///@{
  /**
   * Get/Set the title text properties for each of the coordinate axes. Which
   * properties will be used depends on the selected Face being rendered.
   */
  void SetTitleTextProperty(int axis, vtkTextProperty*);
  vtkTextProperty* GetTitleTextProperty(int axis);
  ///@}

  ///@{
  /**
   * Get/Set the label text properties for each of the coordinate axes. Which
   * properties will be used depends on the selected Face being rendered.
   */
  void SetLabelTextProperty(int axis, vtkTextProperty*);
  vtkTextProperty* GetLabelTextProperty(int axis);
  ///@}

  ///@{
  /**
   * Set titles for each of the axes.
   *
   * Default is empty.
   */
  void SetTitle(int axis, const std::string& title);
  const std::string& GetTitle(int axis);
  ///@}

  ///@{
  /**
   * Get/set the numerical notation, standard, scientific or mixed (0, 1, 2).
   * Accepted values are vtkAxis::AUTO, vtkAxis::FIXED, vtkAxis::CUSTOM.
   *
   * Default is vtkAxis::AUTO
   */
  void SetNotation(int axis, int notation);
  int GetNotation(int axis);
  ///@}

  ///@{
  /**
   * Get/set the numerical precision to use, default is 2.
   */
  void SetPrecision(int axis, int val);
  int GetPrecision(int axis);
  ///@}

  ///@{
  /**
   * Get/Set the function that will be applied to the tick label of each axis.
   * If nothing is set, then the default is to use whatever was generated by vtkAxis.
   *
   * The default it to use whatever was generated by vtkAxis.
   */
  void SetTickLabelFunction(int axis, std::function<double(double)> func);
  std::function<double(double)> GetTickLabelFunction(int axis);
  ///@}

  /**
   * Set custom tick positions for each of the axes.
   * The positions are deep copied. Set to nullptr to not use custom tick positions
   * for the axis.
   *
   * Default is nullptr.
   */
  void SetCustomTickPositions(int axis, vtkDoubleArray* positions);

  //---------------------------------------------------------------------------
  // *** Properties to control grid rendering ***
  //---------------------------------------------------------------------------

  ///@{
  /**
   * Turn off to not generate polydata for the plane's grid.
   *
   * Default is true.
   */
  void SetGenerateGrid(bool val) { this->PlaneActor->SetGenerateGrid(val); }
  bool GetGenerateGrid() { return this->PlaneActor->GetGenerateGrid(); }
  vtkBooleanMacro(GenerateGrid, bool);
  ///@}

  ///@{
  /**
   * Turn off to not generate the polydata for the plane's edges.
   *
   * Default is true.
   */
  void SetGenerateEdges(bool val) { this->PlaneActor->SetGenerateEdges(val); }
  bool GetGenerateEdges() { return this->PlaneActor->GetGenerateEdges(); }
  vtkBooleanMacro(GenerateEdges, bool);
  ///@}

  ///@{
  /**
   * Turn off to not generate the markers for the tick positions. Which edges
   * are rendered is defined by the TickMask.
   *
   * Default is true.
   */
  void SetGenerateTicks(bool val) { this->PlaneActor->SetGenerateTicks(val); }
  bool GetGenerateTicks() { return this->PlaneActor->GetGenerateTicks(); }
  vtkBooleanMacro(GenerateTicks, bool);
  ///@}

  //--------------------------------------------------------------------------
  // Methods for vtkProp3D API.
  //--------------------------------------------------------------------------

  ///@{
  /**
   * Returns the prop bounds.
   */
  double* GetBounds() override
  {
    this->GetGridBounds(this->Bounds);
    return this->Bounds;
  }
  ///@}

  ///@{
  /**
   * If true, the actor will always be rendered during the opaque pass.
   *
   * Default is false.
   */
  vtkSetMacro(ForceOpaque, bool);
  vtkGetMacro(ForceOpaque, bool);
  vtkBooleanMacro(ForceOpaque, bool);
  ///@}

  ///@{
  /**
   * Standard render methods for different types of geometry
   */
  int RenderOpaqueGeometry(vtkViewport*) override;
  int RenderTranslucentPolygonalGeometry(vtkViewport* viewport) override;
  int RenderOverlay(vtkViewport* viewport) override;
  vtkTypeBool HasTranslucentPolygonalGeometry() override;
  ///@}

  /**
   * Release any graphics resources that are being consumed by this prop.
   * The parameter window could be used to determine which graphic
   * resources to release.
   */
  void ReleaseGraphicsResources(vtkWindow*) override;

  /**
   * Overridden to include the mtime for the text properties.
   */
  vtkMTimeType GetMTime() override;

  ///@{
  /**
   * Set/Get the label display offset
   *
   * This is useful to offset axes labels if they overlap at the corners.
   *
   * Defaults to [0, 0]
   *
   * \note Uses display space coordinates
   *
   */
  vtkSetVector2Macro(LabelDisplayOffset, int);
  vtkGetVector2Macro(LabelDisplayOffset, int);
  ///@}

protected:
  vtkGridAxesActor2D();
  ~vtkGridAxesActor2D() override;

  bool Update(vtkViewport* viewport);
  void UpdateTextProperties(vtkViewport* viewport);
  void UpdateLabelPositions(vtkViewport* viewport);
  void UpdateTextActors(vtkViewport* viewport);
  friend class vtkGridAxesActor3D;

private:
  vtkGridAxesActor2D(const vtkGridAxesActor2D&) = delete;
  void operator=(const vtkGridAxesActor2D&) = delete;

  class vtkLabels;
  vtkLabels* Labels;
  friend class vtkLabels;

  bool DoRender;

  double GridBounds[6];
  int Face = vtkGridAxesHelper::MIN_YZ;
  unsigned int LabelMask = 0xff;

  vtkTuple<vtkSmartPointer<vtkTextProperty>, 3> TitleTextProperty;
  vtkTuple<vtkSmartPointer<vtkTextProperty>, 3> LabelTextProperty;
  vtkTuple<std::string, 3> Titles;

  vtkNew<vtkGridAxesHelper> Helper;
  vtkSmartPointer<vtkGridAxesPlaneActor2D> PlaneActor;
  vtkNew<vtkAxis> AxisHelpers[3];
  vtkNew<vtkContextScene> AxisHelperScene;
  vtkTimeStamp UpdateLabelTextPropertiesMTime;
  int LabelDisplayOffset[2] = { 0, 0 };

  bool ForceOpaque = false;
  std::function<double(double)> TickLabelFunction[3] = { nullptr, nullptr, nullptr };
};

VTK_ABI_NAMESPACE_END
#endif
