#include "surface_points_filter.h"

#include <vtkIdList.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkIdTypeArray.h>
#include <vtkCellIterator.h>
#include <vtkTriangle.h>
#include <vtkStaticCellLinks.h>
#include <vtkIntArray.h>

#include <set>



vtkStandardNewMacro(SurfacePointsFilter);


vtkTypeBool SurfacePointsFilter::RequestData(
	vtkInformation *request,
	vtkInformationVector **inputVector,
	vtkInformationVector *outputVector) {
	(void) request;
	vtkUnstructuredGrid* input =
		vtkUnstructuredGrid::GetData(inputVector[0]);
	vtkUnstructuredGrid* output =
		vtkUnstructuredGrid::GetData(outputVector);

	/* Copy point and cell data unmodified. */
	output->CopyStructure(input);
	output->GetPointData()->PassData(input->GetPointData());
	output->GetCellData()->PassData(input->GetCellData());

	/* Create an array to store the IDs of surface points. */
	vtkNew<vtkIdTypeArray> surfacePoints;
	surfacePoints->SetName("surfacePoints");
	surfacePoints->SetNumberOfComponents(1);
	vtkNew<vtkIntArray> isSurface;
	isSurface->SetName("isSurface");
	isSurface->SetNumberOfComponents(1);
	isSurface->SetNumberOfTuples(input->GetNumberOfPoints());
	isSurface->FillValue(0);

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

	vtkNew<vtkIdList> neighborCells;
	vtkNew<vtkIdList> facePoints;
	std::set<vtkIdType> surfacePointsSet;
	auto *it = input->NewCellIterator();
	for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) {
		vtkIdList *cellPointIds = it->GetPointIds();
		for (vtkIdType faceId = 0; faceId < 4; faceId++) {
			vtkIdType idA = cellPointIds->GetId((faceId + 0) % 4);
			vtkIdType idB = cellPointIds->GetId((faceId + 1) % 4);
			vtkIdType idC = cellPointIds->GetId((faceId + 2) % 4);
			facePoints->InsertNextId(idA);
			facePoints->InsertNextId(idB);
			facePoints->InsertNextId(idC);
			input->GetCellNeighbors(it->GetCellId(), facePoints,
			                        neighborCells);
			facePoints->Reset();
			if (neighborCells->GetNumberOfIds() == 0) {
				surfacePointsSet.insert(idA);
				surfacePointsSet.insert(idB);
				surfacePointsSet.insert(idC);
				isSurface->SetValue(idA, 1);
				isSurface->SetValue(idB, 1);
				isSurface->SetValue(idC, 1);
			}
		}
	}
	it->Delete();
	surfacePoints->Allocate(surfacePointsSet.size());
	for (const vtkIdType &id : surfacePointsSet) {
		surfacePoints->InsertNextValue(id);
	}

	output->GetPointData()->SetScalars(isSurface);
	output->GetFieldData()->AddArray(surfacePoints);
	return true;
}
