/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#include "CenterMesh.h"

#include <MeshComponent.h>
#include <Property.h>
#include <Application.h>
#include <Log.h>

using namespace camitk;

#include <QVector3D>

#include <vtkUnstructuredGrid.h>
#include <vtkPolyData.h>
//For Vtk 5.10 #include <vtkCenterOfMass.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>

// --------------- constructor -------------------
CenterMesh::CenterMesh(ActionExtension* extension) : Action(extension) {
    setName("Center Mesh");
    setDescription("Center current mesh component in the world coordinates. When applied this action moves the barycenter of the mesh at world coordinate (0,0,0). Does not consider any frame transformation.");
    setComponentClassName("MeshComponent");
    setFamily("Basic Mesh");
    addTag("Transform");
    addTag("Translate");

    Property* centerPosition = new Property("Mesh Center", QVector3D(0.0, 0.0, 0.0), "Position of the center in the world coordinates (independent from current frame) before it was moved. If more than one mesh are selected, shows the last selected center.", "");
    centerPosition->setReadOnly(true);
    addParameter(centerPosition);
}

// --------------- getWidget -------------------
QWidget* CenterMesh::getWidget() {
    // just update the property
    double barycenter[3];
    updateMeshBarycenter(dynamic_cast<MeshComponent*>(getTargets().last()), barycenter);
    setParameterValue("Mesh Center", QVector3D(barycenter[0], barycenter[1], barycenter[2]));

    return Action::getWidget();
}

// --------------- apply -------------------
Action::ApplyStatus CenterMesh::apply() {

    // set waiting cursor and status bar
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    Application::showStatusBarMessage("Centering...");
    Application::resetProgressBar();

    // apply the transformation
    MeshComponent* targetMesh;

    for (unsigned int i = 0; i < (unsigned) getTargets().size(); i++) {
        targetMesh = dynamic_cast<MeshComponent*>(getTargets().at(i));
        CAMITK_INFO(tr("Centering \"%1\"").arg(targetMesh->getName()))

        double barycenter[3];
        updateMeshBarycenter(targetMesh, barycenter);

        //-- create the transformation to center the barycenter
        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->Translate(-barycenter[0], -barycenter[1], -barycenter[2]);

        //-- transform the mesh using the filter
        vtkSmartPointer<vtkTransformFilter> filter = vtkSmartPointer<vtkTransformFilter>::New();
        filter->SetInputConnection(targetMesh->getDataPort());
        filter->SetTransform(transform);
        filter->Update();

        //-- get the resulting mesh
        vtkSmartPointer<vtkPointSet> result = vtkPointSet::SafeDownCast(filter->GetOutputDataObject(0));
        if (result) {
            targetMesh->setPointSet(result);
            targetMesh->setModified();
        }
        else {
            CAMITK_ERROR(tr("Transform filter output is of type: %1, no vtkPointSet found.").arg(QString(filter->GetOutputDataObject(0)->GetClassName())))
            return ERROR;
        }

        Application::setProgressBarValue(100.0 * (i + 1.0) / ((double)getTargets().size()));

    }

    // shows last target's center
    double barycenter[3];
    updateMeshBarycenter(dynamic_cast<MeshComponent*>(getTargets().last()), barycenter);
    setParameterValue("Mesh Center", QVector3D(barycenter[0], barycenter[1], barycenter[2]));
    Action::getWidget()->update();

    // restore the normal cursor and progress bar
    refreshApplication();
    Application::resetProgressBar();
    QApplication::restoreOverrideCursor();

    return SUCCESS;
}

// --------------- updateMeshBarycenter -------------------
void CenterMesh::updateMeshBarycenter(MeshComponent* targetMesh, double center[3]) {
    //-- compute barycenter
    double sumCoord[3] = {0.0, 0.0, 0.0};

    for (vtkIdType i = 0; i < targetMesh->getPointSet()->GetNumberOfPoints(); i++) {
        double* position = targetMesh->getPointSet()->GetPoints()->GetPoint(i);

        for (unsigned int j = 0; j < 3; j++) {
            sumCoord[j] += position[j];
        }
    }

    for (unsigned int i = 0; i < 3; i++) {
        center[i] = sumCoord[i] / targetMesh->getPointSet()->GetNumberOfPoints();
    }
}