/*****************************************************************************
 * $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 "CropImage.h"

// CamiTK includes
#include <Property.h>
#include <TransformationManager.h>
#include <ImageComponent.h>
#include <Log.h>

// includes for specific types
#include <vtkExtractVOI.h>
#include <vtkTransform.h>

// local includes
#include "BoxVOIWidget.h"

using namespace camitk;

// -------------------- init --------------------
void CropImage::init() {
    // Setting the widget containing the parameters
    theWidget = nullptr;
}

// -------------------- process --------------------
Action::ApplyStatus CropImage::process() {
    // check the widget
    auto* rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);
    // Get the image
    ImageComponent* input = dynamic_cast<ImageComponent*>(getTargets().last());

    // this call works only with a GUI (i.e. if theWidget exists)
    if ((input == nullptr) || (rgWidget == nullptr)) {
        CAMITK_WARNING(tr("This action cannot be called without a GUI (input data are required to be set manually). Action Aborted."))
        return ABORTED;
    }

    // Get the parameters
    seedPoints = rgWidget->getSeedPoints(input);

    // check if number of seeds is coherent
    if (seedPoints->count() == 2 || seedPoints->count() == 6) {
        crop(input);
    }
    else {
        CAMITK_WARNING(tr("2 or 6 seeds are required to apply this action. Action Aborted."))
        return ABORTED;
    }
    return SUCCESS;
}


// --------------- apply -------------------
Action::ApplyStatus CropImage::apply(QList<QVector3D>* seedPoints) {
    // Get the image
    ImageComponent* input =  dynamic_cast<ImageComponent*>(getTargets().last());

    // Get the parameters
    this->seedPoints = seedPoints;

    // check if number of seeds is coherent
    if (seedPoints->count() == 2 || seedPoints->count() == 6) {
        crop(input);
    }
    else {
        CAMITK_WARNING(tr("2 or 6 seeds are required to apply this action. Action Aborted."))
        return ABORTED;
    }

    return SUCCESS;
}

// -------------------- targetDefined --------------------
void CropImage::targetDefined() {
}

// -------------------- parameterChanged --------------------
void CropImage::parameterChanged(QString parameterName) {
}

// -------------------- getUI --------------------
QWidget* CropImage::getUI() {
    auto* rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);

    //-- create the widget if needed
    if (!rgWidget) {
        theWidget = new BoxVOIWidget(this);
        rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);
    }

    //-- update the widget with a PickedPixelMap param
    rgWidget->updateComponent(dynamic_cast<ImageComponent*>(getTargets().last()));

    return theWidget;
}


// --------------- crop -------------------
void CropImage::crop(ImageComponent* comp) {
    vtkSmartPointer<vtkImageData> inputImage = comp->getImageData();
    vtkSmartPointer<vtkImageData> result = vtkSmartPointer<vtkImageData>::New();
    vtkSmartPointer<vtkExtractVOI> extractVOI = vtkSmartPointer<vtkExtractVOI>::New();

    // construction of new img
    extractVOI->SetInputData(inputImage);

    // get seeds values
    int x1, x2, y1, y2, z1, z2 = 0;

    if (seedPoints->count() == 6) {
        x1 = (seedPoints->at(0).x());
        x2 = (seedPoints->at(1).x());
        y1 = (seedPoints->at(2).y());
        y2 = (seedPoints->at(3).y());
        z1 = (seedPoints->at(4).z());
        z2 = (seedPoints->at(5).z());
    }
    else {
        x1 = (seedPoints->at(0).x());
        x2 = (seedPoints->at(1).x());
        y1 = (seedPoints->at(0).y());
        y2 = (seedPoints->at(1).y());
        z1 = (seedPoints->at(0).z());
        z2 = (seedPoints->at(1).z());
    }
    // constructs volume of interest
    extractVOI->SetVOI(std::min(x1, x2), std::max(x1, x2), std::min(y1, y2), std::max(y1, y2), std::min(z1, z2), std::max(z1, z2));
    extractVOI->Update();

    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = extractVOI->GetOutput();

    // Adapt properties of the new volume
    double* spacing = comp->getImageData()->GetSpacing();
    // adapt extent to the new volume
    int* dims = resultImage->GetDimensions();
    int extent[6] = {0,
                     dims[0] - 1,
                     0,
                     dims[1] - 1,
                     0,
                     dims[2] - 1
                    };
    resultImage->SetExtent(extent);

    //Update values computed
    result->SetExtent(extent);
    result->DeepCopy(resultImage);
    // Reset origin, TransformationManager manages position of the new image
    result->SetOrigin(0, 0, 0);

    // creation of the image component (Keep its parent frame.)
    ImageComponent* outputComp  = new ImageComponent(result, comp->getName() + "_cropped");

    // Create is a translation between the frame of the original image and the output one because they have a different origin
    vtkSmartPointer<vtkTransform> translation = vtkSmartPointer<vtkTransform>::New();
    translation->Identity();
    translation->Translate(std::min(x1, x2)*spacing[0],
                           std::min(y1, y2)*spacing[1],
                           std::min(z1, z2)*spacing[2]
                          );
    TransformationManager::addTransformation(outputComp->getFrame(), comp->getFrame(), translation);
    refreshApplication();
}

