/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * 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 "BoxVOI.h"

// includes from CamiTK
#include <Application.h>
#include <Log.h>

// includes from VTK
#include <vtkExtractVOI.h>

// local includes
#include "BoxVOIWidget.h"

using namespace camitk;

// --------------- constructor -------------------
BoxVOI::BoxVOI(ActionExtension* extension) : Action(extension) {
    // Setting name, description and input component
    setName("Box Volume Of Interest");
    setDescription(tr("<p>This filter helps you to crop a volume to keep only an interesting subsample. To use it, select respectively the min and the max of each axe (i,j,k), i.e 6 points. It is possible to use only 2 points (the origin of parallelepiped and its opposite diagonal point)."));
    setComponent("ImageComponent");

    setEmbedded(true);

    // Setting classification family and tags
    this->setFamily("Image Processing");
    this->addTag(tr("Crop Volume"));
    this->addTag(tr("Box"));
    this->addTag(tr("Volume Of Interest"));
    this->addTag(tr("Seed Point"));

    // Setting the widget containing the parameters
    theWidget = nullptr;

}

// --------------- destructor -------------------
BoxVOI::~BoxVOI() {
    // do not delete the widget has it might have been used in the ActionViewer (i.e. the ownership might have been taken by the stacked widget)
}

// --------------- getWidget --------------
QWidget* BoxVOI::getWidget() {

    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;
}

// --------------- apply -------------------
Action::ApplyStatus BoxVOI::apply() {
    // 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) {
        process(input);
    }
    else {
        CAMITK_WARNING(tr("2 or 6 seeds are required to apply this action. Action Aborted."))
        return ABORTED;
    }
    return SUCCESS;
}

// --------------- apply -------------------
Action::ApplyStatus BoxVOI::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) {
        process(input);
    }
    else {
        CAMITK_WARNING(tr("2 or 6 seeds are required to apply this action. Action Aborted."))
        return ABORTED;
    }

    return SUCCESS;
}

// --------------- process -------------------
void BoxVOI::process(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* inputO = inputImage->GetOrigin();
    // value - origin in case of origin of an image is not (0,0,0)
    double* spacing = comp->getImageData()->GetSpacing();
    resultImage->SetOrigin(std::min(x1, x2)*spacing[0] + inputO[0],
                           std::min(y1, y2)*spacing[1] + inputO[1],
                           std::min(z1, z2)*spacing[2] + inputO[2]);

    // 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);

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

    // consider frame policy on new image created
    Action::applyTargetPosition(comp, outputComp);

    Application::refresh();

}
