#include "dihedral_angles_filter.h"

#include <algorithm>
#include <limits>
#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkCellIterator.h>
#include <vtkTriangle.h>


vtkStandardNewMacro(DihedralAnglesFilter);


vtkTypeBool DihedralAnglesFilter::RequestData(
	vtkInformation *request,
	vtkInformationVector **inputVector,
	vtkInformationVector *outputVector) {
	(void) request;
	vtkUnstructuredGrid* input =
		vtkUnstructuredGrid::GetData(inputVector[0]);
	vtkUnstructuredGrid* output =
		vtkUnstructuredGrid::GetData(outputVector);
	output->CopyStructure(input);
	output->GetPointData()->PassData(input->GetPointData());
	vtkCellData *cellData = output->GetCellData();
	cellData->PassData(input->GetCellData());

	vtkNew<vtkDoubleArray> dihedralAnglesArray;
	dihedralAnglesArray->SetName("dihedral_angles");
	dihedralAnglesArray->SetNumberOfComponents(6);
	dihedralAnglesArray->SetNumberOfTuples(input->GetNumberOfCells());
	double *dihedralAnglesBase = dihedralAnglesArray->GetPointer(0);

	vtkNew<vtkDoubleArray> minDihedralAngleArray;
	minDihedralAngleArray->SetName("min_dihedral_angle");
	minDihedralAngleArray->SetNumberOfTuples(input->GetNumberOfCells());
	double *minDihedralAngleBase = minDihedralAngleArray->GetPointer(0);

	AverageMinDegrees = 0;
	MinMinDegrees = std::numeric_limits<double>::infinity();

	size_t i = 0;
	vtkCellIterator *it = input->NewCellIterator();
	for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) {
		double a[3], b[3], c[3], d[3];
		vtkPoints *points = it->GetPoints();
		points->GetPoint(0, a);
		points->GetPoint(1, b);
		points->GetPoint(2, c);
		points->GetPoint(3, d);

		double nacb[3], nadc[3], nabd[3], nbdc[3];
		vtkTriangle::ComputeNormal(a, c, b, nacb);
		vtkTriangle::ComputeNormal(a, d, c, nadc);
		vtkTriangle::ComputeNormal(a, b, d, nabd);
		vtkTriangle::ComputeNormal(b, d, c, nbdc);

		dihedralAnglesBase[i+0] = vtkMath::AngleBetweenVectors(nacb, nadc);
		dihedralAnglesBase[i+1] = vtkMath::AngleBetweenVectors(nadc, nabd);
		dihedralAnglesBase[i+2] = vtkMath::AngleBetweenVectors(nabd, nacb);
		dihedralAnglesBase[i+3] = vtkMath::AngleBetweenVectors(nbdc, nacb);
		dihedralAnglesBase[i+4] = vtkMath::AngleBetweenVectors(nbdc, nadc);
		dihedralAnglesBase[i+5] = vtkMath::AngleBetweenVectors(nbdc, nabd);

		minDihedralAngleBase[i / 6] =
			*std::min_element(dihedralAnglesBase + i,
			                  dihedralAnglesBase + i + 6);
		AverageMinDegrees += minDihedralAngleBase[i / 6];
		MinMinDegrees = std::min(MinMinDegrees, minDihedralAngleBase[i / 6]);

		i += 6;
	}
	it->Delete();
	AverageMinDegrees /= input->GetNumberOfCells();
	AverageMinDegrees = AverageMinDegrees * 180. / 3.141592653589793;
	MinMinDegrees = MinMinDegrees * 180. / 3.141592653589793;
	cellData->AddArray((vtkAbstractArray *) dihedralAnglesArray);
	cellData->AddArray((vtkAbstractArray *) minDihedralAngleArray);
	return true;
}
