#include "aspect_ratio_filter.h"
#include "vtkDataObject.h"

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


vtkStandardNewMacro(AspectRatioFilter);


// Ensure there is an "angles", 12 floats tuple array in the DataCell.
int AspectRatioFilter::FillInputPortInformation(int port, vtkInformation *info) {
	vtkUnstructuredGridAlgorithm::FillInputPortInformation(port, info);
	vtkNew<vtkInformation> anglesField;
	anglesField->Set(vtkDataObject::FIELD_ASSOCIATION(), vtkDataObject::FIELD_ASSOCIATION_CELLS);
	anglesField->Set(vtkDataObject::FIELD_NAME(), "angles");
	anglesField->Set(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS(), 12);
	anglesField->Set(vtkDataObject::FIELD_ARRAY_TYPE(), VTK_DOUBLE);

	vtkNew<vtkInformationVector> fields;
	fields->Append(anglesField);
	info->Set(vtkAlgorithm::INPUT_REQUIRED_FIELDS(), fields);
	return 1;
}


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

	/* Pass the input structure and point data unmodified. */
	output->CopyStructure(input);
	output->GetPointData()->PassData(input->GetPointData());

	/* Pass the existing cell data unmodified. */
	vtkCellData *cellData = output->GetCellData();
	cellData->PassData(input->GetCellData());

	/* Then add an array to it. */
	vtkNew<vtkDoubleArray> aspectRatios;
	aspectRatios->SetName("aspect_ratio");
	aspectRatios->SetNumberOfComponents(1);
	aspectRatios->SetNumberOfTuples(input->GetNumberOfCells());

	auto *angles = vtkDoubleArray::SafeDownCast(input->GetCellData()->GetArray("angles"));

	double cellAngles[12];
	double triangleRatios[4];
	for (vtkIdType cellId = 0; cellId < input->GetNumberOfCells(); cellId++) {
		if (input->GetCellType(cellId) != VTK_TETRA) continue;
		angles->GetTuple(cellId, cellAngles);
		for (vtkIdType angleId = 0;
			 angleId < angles->GetNumberOfComponents();
			 angleId += 3) {
			double minAngle = std::min(
				std::min(cellAngles[angleId], cellAngles[angleId + 1]),
				cellAngles[angleId + 2]);
			double maxAngle = std::max(
				std::max(cellAngles[angleId], cellAngles[angleId + 1]),
				cellAngles[angleId + 2]);
			triangleRatios[angleId / 3] = minAngle / maxAngle;
		}
		double worst = std::min(
			std::min(triangleRatios[0], triangleRatios[1]),
			std::min(triangleRatios[2], triangleRatios[3]));
		aspectRatios->SetTuple1(cellId, worst);
	}
	cellData->AddArray((vtkAbstractArray *) aspectRatios);
	return true;
}
