// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
//
// This example shows how to create a simple tree view from an XML file.
// You may specify the label array and color array from the command line.
//

#include "vtkActor.h"
#include "vtkActor2D.h"
#include "vtkDynamic2DLabelMapper.h"
#include "vtkGlyph3D.h"
#include "vtkGlyphSource2D.h"
#include "vtkGraphLayout.h"
#include "vtkGraphToPolyData.h"
#include "vtkInteractorStyleImage.h"
#include "vtkPointData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkStringToNumeric.h"
#include "vtkTextProperty.h"
#include "vtkTreeLayoutStrategy.h"
#include "vtkXMLTreeReader.h"

#include <iostream>

void usage()
{
  std::cerr << endl;
  std::cerr << "usage: TreeLayout filename [label_attribute] [color_attribute]" << endl;
  std::cerr << "  filename is an xml file" << endl;
  std::cerr << "  label_attribute is the attribute to use as labels." << endl;
  std::cerr << "    Default is .tagname which labels using the element tag." << endl;
  std::cerr << "  color_attribute is the attribute to color by (numeric)." << endl;
  std::cerr << "    Default is no color." << endl;
}

int main(int argc, char* argv[])
{
  // Initialize parameters from the command line.
  const char* labelArray = ".tagname";
  const char* colorArray = nullptr;
  if (argc < 2)
  {
    usage();
    return 0;
  }
  char* filename = argv[1];
  if (argc >= 3)
  {
    labelArray = argv[2];
  }
  if (argc >= 4)
  {
    colorArray = argv[3];
  }

  // Read in the XML file into a tree.
  // This creates a tree with string columns for every attribute
  // present in the file, plus the special arrays named .tagname
  // (containing the XML tag name) and .chardata (containing the
  // character data within the tag).
  vtkXMLTreeReader* reader = vtkXMLTreeReader::New();
  reader->SetFileName(filename);

  // Automatically convert string columns containing numeric
  // values into integer and double arrays.
  vtkStringToNumeric* stringToNumeric = vtkStringToNumeric::New();
  stringToNumeric->SetInputConnection(reader->GetOutputPort());

  // Retrieve the tree from the pipeline so we can check whether
  // the specified label and color arrays exist.
  stringToNumeric->Update();
  vtkTree* tree = vtkTree::SafeDownCast(stringToNumeric->GetOutput());
  if (tree->GetVertexData()->GetAbstractArray(labelArray) == nullptr)
  {
    std::cerr << "ERROR: The label attribute " << labelArray << " is not defined in the file."
              << endl;
    reader->Delete();
    stringToNumeric->Delete();
    usage();
    return 0;
  }
  if (colorArray && tree->GetVertexData()->GetAbstractArray(colorArray) == nullptr)
  {
    std::cerr << "ERROR: The color attribute " << colorArray << " is not defined in the file."
              << endl;
    reader->Delete();
    stringToNumeric->Delete();
    usage();
    return 0;
  }
  if (colorArray &&
    vtkArrayDownCast<vtkDataArray>(tree->GetVertexData()->GetAbstractArray(colorArray)) == nullptr)
  {
    std::cerr << "ERROR: The color attribute " << colorArray << " does not have numeric values."
              << endl;
    reader->Delete();
    stringToNumeric->Delete();
    usage();
    return 0;
  }

  // If coloring the vertices, get the range of the color array.
  double colorRange[2] = { 0, 1 };
  if (colorArray)
  {
    tree->GetVertexData()->GetRange(colorArray, colorRange);
  }

  // Layout the tree using vtkGraphLayout.
  vtkGraphLayout* layout = vtkGraphLayout::New();
  layout->SetInputConnection(stringToNumeric->GetOutputPort());

  // Specify that we want to use the tree layout strategy.
  vtkTreeLayoutStrategy* strategy = vtkTreeLayoutStrategy::New();
  strategy->RadialOn();      // Radial layout (as opposed to standard top-down layout)
  strategy->SetAngle(360.0); // The tree fills a full circular arc.
  layout->SetLayoutStrategy(strategy);

  // vtkGraphToPolyData converts a graph or tree to polydata.
  vtkGraphToPolyData* graphToPoly = vtkGraphToPolyData::New();
  graphToPoly->SetInputConnection(layout->GetOutputPort());

  // Create the standard VTK polydata mapper and actor
  // for the connections (edges) in the tree.
  vtkPolyDataMapper* edgeMapper = vtkPolyDataMapper::New();
  edgeMapper->SetInputConnection(graphToPoly->GetOutputPort());
  vtkActor* edgeActor = vtkActor::New();
  edgeActor->SetMapper(edgeMapper);
  edgeActor->GetProperty()->SetColor(0.0, 0.5, 1.0);

  // Glyph the points of the tree polydata to create
  // VTK_VERTEX cells at each vertex in the tree.
  vtkGlyph3D* vertGlyph = vtkGlyph3D::New();
  vertGlyph->SetInputConnection(0, graphToPoly->GetOutputPort());
  vtkGlyphSource2D* glyphSource = vtkGlyphSource2D::New();
  glyphSource->SetGlyphTypeToVertex();
  vertGlyph->SetInputConnection(1, glyphSource->GetOutputPort());

  // Create a mapper for the vertices, and tell the mapper
  // to use the specified color array.
  vtkPolyDataMapper* vertMapper = vtkPolyDataMapper::New();
  vertMapper->SetInputConnection(vertGlyph->GetOutputPort());
  if (colorArray)
  {
    vertMapper->SetScalarModeToUsePointFieldData();
    vertMapper->SelectColorArray(colorArray);
    vertMapper->SetScalarRange(colorRange);
  }

  // Create an actor for the vertices.  Move the actor forward
  // in the z direction so it is drawn on top of the edge actor.
  vtkActor* vertActor = vtkActor::New();
  vertActor->SetMapper(vertMapper);
  vertActor->GetProperty()->SetPointSize(5);
  vertActor->SetPosition(0, 0, 0.001);

  // Use a dynamic label mapper to draw the labels.  This mapper
  // does not allow labels to overlap, as long as the camera is
  // not rotated from pointing down the z axis.
  vtkDynamic2DLabelMapper* labelMapper = vtkDynamic2DLabelMapper::New();
  labelMapper->SetInputConnection(graphToPoly->GetOutputPort());
  labelMapper->GetLabelTextProperty()->SetJustificationToLeft();
  labelMapper->GetLabelTextProperty()->SetColor(0, 0, 0);
  if (labelArray)
  {
    labelMapper->SetLabelModeToLabelFieldData();
    labelMapper->SetFieldDataName(labelArray);
  }
  vtkActor2D* labelActor = vtkActor2D::New();
  labelActor->SetMapper(labelMapper);

  // Add the edges, vertices, and labels to the renderer.
  vtkRenderer* ren = vtkRenderer::New();
  ren->SetBackground(0.8, 0.8, 0.8);
  ren->AddActor(edgeActor);
  ren->AddActor(vertActor);
  ren->AddActor(labelActor);

  // Setup the render window and interactor.
  vtkRenderWindow* win = vtkRenderWindow::New();
  win->AddRenderer(ren);
  vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::New();
  iren->SetRenderWindow(win);

  // Constrain movement to zoom and pan using the image interactor style.
  vtkInteractorStyleImage* style = vtkInteractorStyleImage::New();
  iren->SetInteractorStyle(style);

  // Start the main application loop.
  iren->Initialize();
  iren->Start();

  // Clean up.
  style->Delete();
  iren->Delete();
  win->Delete();
  ren->Delete();
  labelActor->Delete();
  labelMapper->Delete();
  vertActor->Delete();
  vertMapper->Delete();
  glyphSource->Delete();
  vertGlyph->Delete();
  edgeMapper->Delete();
  edgeActor->Delete();
  graphToPoly->Delete();
  strategy->Delete();
  layout->Delete();
  stringToNumeric->Delete();
  reader->Delete();

  return 0;
}
