#include "point_tris_dist.h"

#include "project_surface_points_on_poly.h"

#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkCellIterator.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#include <vtkPolyData.h>
#include <vtkKdTree.h>
#include <vtkStaticCellLinks.h>

#include <limits>


vtkStandardNewMacro(ProjectSurfacePointsOnPoly);


ProjectSurfacePointsOnPoly::ProjectSurfacePointsOnPoly() {
	SetNumberOfInputPorts(2);
}


int ProjectSurfacePointsOnPoly::FillInputPortInformation(int port, vtkInformation *info) {
	if (port == 0) {
		vtkUnstructuredGridAlgorithm::FillInputPortInformation(port, info);
	} else if (port == 1) {
		vtkNew<vtkInformation> input2;
		input2->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
		info->Append(input2);
	}
	return 1;
}


vtkTypeBool ProjectSurfacePointsOnPoly::RequestData(
	vtkInformation *request,
	vtkInformationVector **inputVector,
	vtkInformationVector *outputVector) {
	(void) request;
	vtkUnstructuredGrid* tetMesh = vtkUnstructuredGrid::GetData(inputVector[0]);
	vtkPolyData* polyMesh = vtkPolyData::GetData(inputVector[1]);
	vtkUnstructuredGrid* output =
		vtkUnstructuredGrid::GetData(outputVector);

	output->CopyStructure(tetMesh);
	output->GetPointData()->PassData(tetMesh->GetPointData());
	output->GetCellData()->PassData(tetMesh->GetCellData());

	/* Generate cell links, these tell us which cell a point belongs to. */
	vtkNew<vtkStaticCellLinks> links;
	links->BuildLinks(polyMesh);

	vtkNew<vtkKdTree> kdTree;
	kdTree->BuildLocatorFromPoints(polyMesh->GetPoints());

	vtkIdTypeArray *surfacePoints =
		vtkIdTypeArray::SafeDownCast(tetMesh->GetFieldData()->GetArray("surface_points"));
	for (vtkIdType i = 0; i < surfacePoints->GetNumberOfTuples(); i++) {
		double p[3];
		tetMesh->GetPoint(surfacePoints->GetTuple1(i), p);
		double dist2;
		vtkIdType closest = kdTree->FindClosestPoint(p, dist2);
		vtkIdType *cellIds = links->GetCells(closest);
		vtkIdType nCells = links->GetNcells(closest);
		double min_dist = std::numeric_limits<double>::infinity();
		double min_direction[3];

		/* For each tri the closest point belongs to. */
		for (vtkIdType j = 0; j < nCells; j++) {
			vtkIdType cellId = cellIds[j];
			double direction[3];
			double dist = pointTriangleDistance(p, polyMesh->GetCell(cellId), direction);
			/* Find the closest one. */
			if (dist < min_dist) {
				min_dist = dist;
				min_direction[0] = direction[0];
				min_direction[1] = direction[1];
				min_direction[2] = direction[2];
			}
		}

		vtkMath::MultiplyScalar(min_direction, min_dist);
		vtkMath::Subtract(p, min_direction, p);
		tetMesh->GetPoints()->SetPoint(surfacePoints->GetTuple1(i), p);
		tetMesh->Modified();
	}

	return true;
}
