// CamiTK includes
#include "ComputeCurvatures.h"
#include <Application.h>
#include <MeshComponent.h>
#include <Log.h>
#include <Property.h>

// Vtk includes
#include <vtkDataSetAttributes.h>
#include <vtkPointData.h>
#include <vtkCallbackCommand.h>

using namespace camitk;

ComputeCurvatures::ComputeCurvatures(camitk::ActionExtension* ext) : Action(ext) {
    this->setName("Compute Curvatures");
    this->setDescription(QString(tr("Computes curvatures of a surface.")));
    this->setComponentClassName("MeshComponent");
    this->setFamily("Mesh Processing");
    this->addTag(tr("curvatures"));

    Property* curvatureTypeProperty = new Property(tr("Curvature type"), GAUSSIAN, tr("The type of curvature to compute on the surfacic mesh."), "");
    curvatureTypeProperty->setEnumTypeName("CurvatureType", this);
    addParameter(curvatureTypeProperty);
}

Action::ApplyStatus ComputeCurvatures::apply() {
    // set waiting cursor and status bar
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    Application::showStatusBarMessage(tr("Compute Curvatures..."));
    Application::resetProgressBar();
    vtkSmartPointer<vtkCallbackCommand> progressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    progressCallback->SetCallback(&Application::vtkProgressFunction);

    // target must be a polydata
    MeshComponent* mesh = dynamic_cast<MeshComponent*>(getTargets().first());

    if (mesh == nullptr) {
        QApplication::restoreOverrideCursor();
        // mesh is not a MeshComponent
        return ABORTED;
    }

    vtkPolyData* pData = vtkPolyData::SafeDownCast(mesh->getPointSet());

    if (pData == nullptr) {
        QApplication::restoreOverrideCursor();
        CAMITK_WARNING(tr("The mesh point set of \"%1\" is not a vtkPolyData. Action aborted.").arg(getTargets().first()->getName()))
        return ABORTED;
    }

    if (pData->GetNumberOfPolys() <= 1) {
        CAMITK_WARNING(tr("Not enough polygonal cells found in \"%1\": cannot compute curvature. Action aborted.").arg(getTargets().first()->getName()))
        return ABORTED;
    }

    // Compute curvatures for first target

    // set up curvatures
    vtkSmartPointer<vtkCurvatures> curv = vtkSmartPointer<vtkCurvatures>::New();
    curv->SetCurvatureType(getParameterValue("Curvature type").toInt());
    curv->SetInputData(pData);
    curv->AddObserver(vtkCommand::ProgressEvent, progressCallback);
    curv->Update();

    switch (getParameterValue("Curvature type").toInt()) {
        case MINIMUM :
            mesh->addPointData("Minimum Curvature", curv->GetOutput()->GetPointData()->GetScalars("Minimum_Curvature"));
            break;
        case MAXIMUM :
            mesh->addPointData("Maximum Curvature", curv->GetOutput()->GetPointData()->GetScalars("Maximum_Curvature"));
            break;
        case GAUSSIAN :
            if (curv->GetOutput()->GetPointData()->GetScalars("Gauss_Curvature") == nullptr) {
                CAMITK_ERROR(tr("No Gauss Curvature can be computed for: \"%1\" (probably no 3D surface in this mesh).").arg(getTargets().first()->getName()))
                return ERROR;
            }
            mesh->addPointData("Gauss Curvature", curv->GetOutput()->GetPointData()->GetScalars("Gauss_Curvature"));
            break;
        case MEAN:
            mesh->addPointData("Mean Curvature", curv->GetOutput()->GetPointData()->GetScalars("Mean_Curvature"));
            break;
        default :
            CAMITK_ERROR(tr("Invalid Curvature type value."))
            return ERROR;
            break;
    }

    // refresh restore the normal cursor and progress bar
    refreshApplication();
    Application::resetProgressBar();
    Application::showStatusBarMessage("");
    QApplication::restoreOverrideCursor();
    return SUCCESS;
}
