#include "operation_mesh.h"

vtkSmartPointer<vtkPolyData> operation::buildValidTriangulation(vtkSmartPointer<vtkPolyData> polyData)
{
    vtkSmartPointer<vtkPolyData> triPolyData = vtkSmartPointer<vtkPolyData>::New();

    // if mesh is composed of quads, builds a valid triangulation
    if (polyData->GetMaxCellSize() == 4)
    {
        std::cout << "Mesh is composed of quads\n";
        // we're using a vtkQuadToTriangle filter to convert quads in triangles
        vtkSmartPointer<vtkTriangleFilter> quadToTri = vtkSmartPointer<vtkTriangleFilter>::New();
        quadToTri->SetInputData(polyData);
        quadToTri->Update();

        triPolyData = quadToTri->GetOutput();
    }
    else if (polyData->GetMaxCellSize() == 3) // if mesh is already triangulated, return origin's vtkPolyData
    {
        triPolyData = polyData;
    }
    else
    {
        std::cout << "This mesh is not handled by this program, allows only triangulated and quadrilated meshes.\n";
        std::cout << "Maximum cell size for this mesh: " << polyData->GetMaxCellSize() << ".\n";
        exit(0);
    }
    // adding a vtkTriangleFilter to ensure that the output is a well triangulated mesh
    vtkSmartPointer<vtkTriangleFilter> triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New();
    triangleFilter->SetInputData(triPolyData);
    triangleFilter->Update();

    return triangleFilter->GetOutput();
}

#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkFillHolesFilter.h>
#include <vtkCleanPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>

vtkSmartPointer<vtkPolyData> operation::closeMesh(vtkSmartPointer<vtkPolyData> polyData)
{
    // Check if the input mesh is open
    vtkSmartPointer<vtkFeatureEdges> featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
    featureEdges->SetInputData(polyData);
    // FeatureEdges configuration
    featureEdges->BoundaryEdgesOn();     // Search for boundary edges (only one neighbor)
    featureEdges->NonManifoldEdgesOff(); // Search for non-manifold edges
    featureEdges->FeatureEdgesOff();     // Deactivate search for special edges (projecting, concave, etc)
    featureEdges->Update();

    vtkSmartPointer<vtkPolyData> output = featureEdges->GetOutput();
    int numOpenEdges = output->GetNumberOfCells();

    if (numOpenEdges != 0) {
        std::cout << "There are " << numOpenEdges << " open edges.\n";

        // Display the output mesh
        vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
        mapper->SetInputData(featureEdges->GetOutput());
        vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
        actor->SetMapper(mapper);
        vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
        renderer->AddActor(actor);
        vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
        renderWindow->AddRenderer(renderer);
        vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
        renderWindowInteractor->SetRenderWindow(renderWindow);
        renderWindow->Render();
        renderWindowInteractor->Start();

        //TODO: not working for the moment...
        /*
        // Get point coordinates
        vtkPoints* points = output->GetPoints();
        // Loop through open edges and create cells to fill gaps
        for (vtkIdType i = 0; i < output->GetNumberOfCells(); i++) {
            vtkCell* cell = output->GetCell(i);
            if (cell->GetCellType() == VTK_LINE) { // Check if open edge is a line segment
                vtkIdType id0 = cell->GetPointId(0);
                vtkIdType id1 = cell->GetPointId(1);

                vtkIdType newIds[3];
                newIds[0] = id0;
                newIds[1] = id1;

                // Find third point to create triangle
                double point0[3];
                double point1[3];
                double vec[3];
                points->GetPoint(id0, point0);
                points->GetPoint(id1, point1);
                for (vtkIdType j = 0; j < output->GetNumberOfCells(); j++) {
                    if (j == i) {
                        continue;
                    }
                    vtkCell* otherCell = output->GetCell(j);
                    if (otherCell->GetCellType() != VTK_TRIANGLE) {
                        continue;
                    }
                    vtkIdType otherIds[3];
                    otherIds[0] = otherCell->GetPointId(0);
                    otherIds[1] = otherCell->GetPointId(1);
                    otherIds[2] = otherCell->GetPointId(2);
                    if (otherIds[0] == id0 || otherIds[1] == id0 || otherIds[2] == id0) {
                        if (otherIds[0] == id1 || otherIds[1] == id1 || otherIds[2] == id1) {
                            // Found a triangle sharing these two points
                            for (vtkIdType k = 0; k < 3; k++) {
                                if (otherIds[k] != id0 && otherIds[k] != id1) {
                                    points->GetPoint(otherIds[k], vec);
                                    break;
                                }
                            }
                            // Add third point and create new cell
                            newIds[2] = points->InsertNextPoint(vec);
                            vtkSmartPointer<vtkTriangle> newCell = vtkSmartPointer<vtkTriangle>::New();
                            newCell->GetPointIds()->SetId(0, newIds[0]);
                            newCell->GetPointIds()->SetId(1, newIds[1]);
                            newCell->GetPointIds()->SetId(2, newIds[2]);
                            polyData->GetPolys()->InsertNextCell(newCell);
                            break;
                        }
                    }
                }
            }
        }
        return output;*/
    } else {
        std::cout << "There are no open edges.\n";
    }    
    return polyData;
}