#include "vtkConstrainedPointHandleRepresentation.h"

#include <cstdlib>
#include <iostream>

#include "WidgetTestingMacros.h"
#include "vtkPolyData.h"

#include "vtkPlane.h"
#include "vtkPlaneCollection.h"
#include "vtkPlanes.h"
#include "vtkProperty.h"

int vtkConstrainedPointHandleRepresentationTest1(int, char*[])
{
  vtkSmartPointer<vtkConstrainedPointHandleRepresentation> node1 =
    vtkSmartPointer<vtkConstrainedPointHandleRepresentation>::New();

  EXERCISE_BASIC_HANDLE_REPRESENTATION_METHODS(vtkConstrainedPointHandleRepresentation, node1);

  vtkSmartPointer<vtkPolyData> pd = vtkSmartPointer<vtkPolyData>::New();
  node1->SetCursorShape(pd);
  vtkSmartPointer<vtkPolyData> pd2 = node1->GetCursorShape();
  if (!pd2 || pd2 != pd)
  {
    std::cerr << "Error in Set/Get cursor shape." << std::endl;
    return EXIT_FAILURE;
  }

  vtkSmartPointer<vtkPolyData> pd3 = vtkSmartPointer<vtkPolyData>::New();
  node1->SetActiveCursorShape(pd3);
  vtkSmartPointer<vtkPolyData> pd4 = node1->GetActiveCursorShape();
  if (pd4 != pd3)
  {
    std::cerr << "Error in Set/Get active cursor shape." << std::endl;
    return EXIT_FAILURE;
  }

  // constrained 0-3
  TEST_SET_GET_INT_RANGE(node1, ProjectionNormal, 1, 2);
  node1->SetProjectionNormalToXAxis();
  node1->SetProjectionNormalToYAxis();
  node1->SetProjectionNormalToZAxis();
  node1->SetProjectionNormalToOblique();

  vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
  node1->SetObliquePlane(plane);
  vtkSmartPointer<vtkPlane> plane2 = node1->GetObliquePlane();
  if (!plane2 || plane2 != plane)
  {
    std::cerr << "Error in Set/Get oblique plane." << std::endl;
    return EXIT_FAILURE;
  }

  TEST_SET_GET_DOUBLE_RANGE(node1, ProjectionPosition, -10.0, 10.0);

  vtkSmartPointer<vtkPlane> bplane = vtkSmartPointer<vtkPlane>::New();
  vtkSmartPointer<vtkPlane> bplane2 = vtkSmartPointer<vtkPlane>::New();
  node1->AddBoundingPlane(bplane);
  node1->AddBoundingPlane(bplane2);
  node1->RemoveBoundingPlane(bplane);
  node1->RemoveAllBoundingPlanes();

  vtkSmartPointer<vtkPlaneCollection> planeCol = vtkSmartPointer<vtkPlaneCollection>::New();
  node1->SetBoundingPlanes(planeCol);
  vtkSmartPointer<vtkPlaneCollection> planeCol2 = node1->GetBoundingPlanes();
  if (!planeCol2 || planeCol2 != planeCol)
  {
    std::cerr << "Error in Set/Get bounding planes." << std::endl;
    return EXIT_FAILURE;
  }

  vtkSmartPointer<vtkPlanes> bplanes = vtkSmartPointer<vtkPlanes>::New();
  node1->SetBoundingPlanes(bplanes);

  // test Set/GetPosition, in display coords, so only x,y are used
  double pos[3] = { 10.0, 11.0, -12.0 };
  double* pos2 = nullptr;
  node1->SetPosition(pos);
  pos2 = node1->GetPosition();
  if (pos2 == nullptr)
  {
    std::cerr << "Failure in Get/Set Position pos,  null pointer." << std::endl;
    return EXIT_FAILURE;
  }
  else if (pos2[0] != pos[0] || pos2[1] != pos[1])
  {
    std::cerr << "Failure in Get/Set Position pos, expected " << pos[0] << ", " << pos[1] << ", "
              << pos[2] << ", instead got " << pos2[0] << ", " << pos2[1] << ", " << pos2[2]
              << std::endl;
    return EXIT_FAILURE;
  }
  else
  {
    std::cout << "Set Position to " << pos2[0] << ", " << pos2[1] << ", " << pos2[2] << std::endl;
  }
  pos[0] = 12.0;
  node1->SetPosition(pos[0], pos[1], pos[2]);
  pos2 = node1->GetPosition();
  if (pos2 == nullptr)
  {
    std::cerr << "Failure in Get/Set Position pos,  null pointer." << std::endl;
    return EXIT_FAILURE;
  }
  else if (pos2[0] != pos[0] || pos2[1] != pos[1])
  {
    std::cerr << "Failure in Get/Set Position pos, expected " << pos[0] << ", " << pos[1] << ", "
              << pos[2] << ", instead got " << pos2[0] << ", " << pos2[1] << ", " << pos2[2]
              << std::endl;
    return EXIT_FAILURE;
  }
  else
  {
    std::cout << "Set Position to " << pos2[0] << ", " << pos2[1] << ", " << pos2[2] << std::endl;
  }

  pos[0] -= 1.0;
  node1->SetPosition(pos[0], pos[1], pos[2]);
  double pos3[3];
  node1->GetPosition(pos3);
  if (pos3[0] != pos[0] || pos3[1] != pos[1])
  {
    std::cerr << "Failure in Get/Set Position pos, expected " << pos[0] << ", " << pos[1] << ", "
              << pos[2] << ", instead got " << pos3[0] << ", " << pos3[1] << ", " << pos3[2]
              << std::endl;
    return EXIT_FAILURE;
  }
  else
  {
    std::cout << "Set Position to " << pos3[0] << ", " << pos3[1] << ", " << pos3[2] << std::endl;
  }

  // Properties
  vtkSmartPointer<vtkProperty> prop1 = vtkSmartPointer<vtkProperty>::New();
  double colour[3] = { 0.2, 0.3, 0.4 };
  prop1->SetColor(colour);
  node1->GetProperty()->SetColor(colour);
  vtkSmartPointer<vtkProperty> prop = node1->GetProperty();
  if (!prop)
  {
    std::cerr << "Got null property back after setting it!" << std::endl;
    return EXIT_FAILURE;
  }
  double* col = prop->GetColor();
  if (!col)
  {
    std::cerr << "Got null colour back!" << std::endl;
    return EXIT_FAILURE;
  }
  if (col[0] != colour[0] || col[1] != colour[1] || col[2] != colour[2])
  {
    std::cerr << "Got wrong colour back after setting it! Expected " << colour[0] << ", "
              << colour[1] << ", " << colour[2] << ", but got " << col[0] << ", " << col[1] << ", "
              << col[2] << std::endl;
    return EXIT_FAILURE;
  }

  vtkSmartPointer<vtkProperty> prop2 = vtkSmartPointer<vtkProperty>::New();
  colour[0] += 0.1;
  colour[2] += 0.1;
  colour[2] += 0.1;
  prop2->SetColor(colour);
  node1->GetSelectedProperty()->SetColor(colour);
  prop = node1->GetSelectedProperty();
  if (!prop)
  {
    std::cerr << "Got null selected property back after setting it!" << std::endl;
    return EXIT_FAILURE;
  }
  col = prop->GetColor();
  if (!col)
  {
    std::cerr << "Got null selected colour back!" << std::endl;
    return EXIT_FAILURE;
  }
  if (col[0] != colour[0] || col[1] != colour[1] || col[2] != colour[2])
  {
    std::cerr << "Got wrong selected colour back after setting it! Expected " << colour[0] << ", "
              << colour[1] << ", " << colour[2] << ", but got " << col[0] << ", " << col[1] << ", "
              << col[2] << std::endl;
    return EXIT_FAILURE;
  }

  prop = node1->GetActiveProperty();
  colour[0] += 0.1;
  colour[2] += 0.1;
  colour[2] += 0.1;
  prop->SetColor(colour);

  return EXIT_SUCCESS;
}
