/*****************************************************************************
 * $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 "MeshPicking.h"
#include <MeshComponent.h>
#include <Application.h>
#include <InteractiveGeometryViewer.h>

using namespace camitk;

//-- vtk stuff to compute some information from the picking
#include <vtkCell.h>
#include <vtkPoints.h>

// Qt
#include <QPushButton>
#include <QVBoxLayout>

// -------------------- MeshPicking --------------------
MeshPicking::MeshPicking(ActionExtension* extension) : Action(extension) {
    setName("Basic Mesh Picking");
    setDescription("Pick a mesh to retrieve point/cell information from the mesh");
    setComponentClassName("MeshComponent");
    setFamily("Basic Mesh");
    addTag("Picking");

    //-- widget lazy instantiation
    informationFrame = nullptr;
}

QWidget* MeshPicking::getWidget() {
    if (!informationFrame) {
        //-- the frame
        informationFrame = new QFrame();
        informationFrame->setFrameShape(QFrame::StyledPanel);
        informationFrame->setFrameShadow(QFrame::Sunken);
        informationFrame->setLineWidth(3);

        //-- the information label
        informationLabel = new QLabel();
        informationLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);

        //-- run the action every time a picking is done in the axial/sagittal or coronal planes
        InteractiveGeometryViewer* default3DViewer = dynamic_cast<InteractiveGeometryViewer*>(Application::getViewer("3D Viewer"));

        if (default3DViewer != nullptr) {
            QObject::connect(default3DViewer, SIGNAL(selectionChanged()), this, SLOT(apply()));
        }

        //-- the vertical layout, put every GUI elements in it
        auto* informationFrameLayout = new QVBoxLayout();
        informationFrameLayout->addWidget(informationLabel);

        //-- set the layout for the action widget
        informationFrame->setLayout(informationFrameLayout);

    }

    return informationFrame;
}

// --------------- apply -------------------
Action::ApplyStatus MeshPicking::apply() {
    // set waiting cursor (not really needed here as the action is very fast, but after all this is a demo)
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

    // get the last selected image component
    MeshComponent* meshComponent = dynamic_cast<MeshComponent*>(getTargets().last());
    // NOTE it is not needed to check for nullptr pointer after this dynamic_cast: this action
    // has declared to be able to process MeshComponent (see the setComponent(..) in the constructor),
    // therefore it is only called using getTargets() that are compatible with MeshComponent

    // get the index in the respectively Points and Cells array
    vtkIdType lastPickedPointId = meshComponent->getPickedPointId();
    vtkIdType lastPickedCellId = meshComponent->getPickedCellId();

    // the information message (in rich text, i.e., html)
    // Note: during test, the getWidget() method is not called, informationFrame is nullptr
    // => building the message should be bypassed
    if (informationFrame) {
        QString message = "<b>Selected Component:</b> " + meshComponent->getName() + "<br/>";

        // if the id is valid, get the corresponding information
        if (lastPickedPointId >= 0) {
            message += "<b>Point Id:</b> " + QString("%1").arg(lastPickedPointId, 6) + "<br/>";
            // get the position
            double* pointPosition = meshComponent->getPointSet()->GetPoint(lastPickedPointId);
            message += "<b>Point Position:</b> " + QString("(%1,%2,%3)").arg(pointPosition[0], 6).arg(pointPosition[1], 6).arg(pointPosition[2], 6) + "<br/>";
        }
        else {
            message += "<b>No point picked</b><br/>";
        }

        if (lastPickedCellId >= 0) {
            message += "<b>Cell Id:</b> " + QString("%1").arg(lastPickedCellId, 6) + "<br/>";
            vtkCell* pickedCell = meshComponent->getPointSet()->GetCell(lastPickedCellId);
            // for example here compute the barycenter of the cell
            double cellBarycenter[3] = {0.0, 0.0, 0.0};

            // loop on all the cell points
            for (int i = 0; i < pickedCell->GetNumberOfPoints(); i++) {
                // for each point accumulate the position
                double* position = pickedCell->GetPoints()->GetPoint(i);

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

            // divide by the number of accumulation
            for (double& value : cellBarycenter) {
                value /= pickedCell->GetNumberOfPoints();
            }

            // add information
            message += "<b>Barycenter:</b> " + QString("(%1,%2,%3)").arg(cellBarycenter[0], 6).arg(cellBarycenter[1], 6).arg(cellBarycenter[2], 6);
        }
        else {
            message += "<b>No cell picked</b>";
        }

        // update the information label
        informationLabel->setText(message);
    }

    // restore the normal cursor
    QApplication::restoreOverrideCursor();

    return SUCCESS;
}
