#include "remove_external_cells_filter.h"
#include "point_tris_dist.h"

#include <vtkCellType.h>
#include <vtkUnstructuredGrid.h>
#include <vtkDataObject.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkCellIterator.h>
#include <vtkSelectEnclosedPoints.h>
#include <vtkUnsignedCharArray.h>


vtkStandardNewMacro(RemoveExternalCellsFilter);


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


int RemoveExternalCellsFilter::FillInputPortInformation(
	int port, vtkInformation *info) {
	if (port == 0) {
		info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
	} else if (port == 1) {
		info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
	}
	return 1;
}


vtkTypeBool RemoveExternalCellsFilter::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);

	vtkNew<vtkSelectEnclosedPoints> selectEnclosedPoints;
	selectEnclosedPoints->SetInputDataObject(0, tetMesh);
	selectEnclosedPoints->SetInputDataObject(1, polyMesh);
	selectEnclosedPoints->Update();
	vtkUnsignedCharArray *enclosedPoints =
		vtkUnsignedCharArray::SafeDownCast(
			selectEnclosedPoints->GetOutput()
			->GetPointData()->GetArray("SelectedPoints"));

	vtkNew<vtkCellArray> outCells;
	auto it = tetMesh->NewCellIterator();
	for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) {
		vtkIdList *pointIds = it->GetPointIds();
		if (enclosedPoints->GetValue(pointIds->GetId(0))
		    || enclosedPoints->GetValue(pointIds->GetId(1))
		    || enclosedPoints->GetValue(pointIds->GetId(2))
		    || enclosedPoints->GetValue(pointIds->GetId(3))) {
			outCells->InsertNextCell(it->GetPointIds());
		}
	}
	it->Delete();
	output->SetCells(VTK_TETRA, outCells);
	output->SetPoints(tetMesh->GetPoints());

	return true;
}
