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

#include <MedicalImageViewer.h>
#include <RendererWidget.h>
#include <InteractiveGeometryViewer.h>
#include <Property.h>
#include <Application.h>
#include <SingleImageComponent.h>
#include <Log.h>

// disable warning generated by clang about the surrounded headers
#include <CamiTKDisableWarnings>
#include <vtkCamera.h>
#include <CamiTKReEnableWarnings>

using namespace camitk;

// --------------- Constructor -------------------
WorkingWhenSleepingLinear::WorkingWhenSleepingLinear(ActionExtension* extension) : Action(extension) {
// Setting name, description and input component
    numberOfWindows = 300;
    setName("Working When Sleeping (Linear)");
    setDescription("This action generates a linear exploration of slice sagittal, coronal and axial viewers");
    setComponentClassName("ImageComponent");

    // Setting classification family and tags
    setFamily("Tutorial");
    addTag("Working When Sleeping");

    // Setting the action's parameters
    addParameter(new Property("High Speed", false, tr("If true, change goes faster."), ""));
    addParameter(new Property("Number Of Steps", numberOfWindows, tr("Number of change steps."), ""));

}

// --------------- destructor -------------------
WorkingWhenSleepingLinear::~WorkingWhenSleepingLinear() {
    // Do not do anything yet.
    // Delete stuff if you create stuff
    // (except if you use smart pointers of course !!)
}

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

    for (Component* comp : getTargets()) {
        ImageComponent* input = dynamic_cast<ImageComponent*>(comp);
        process(input);
    }

    return SUCCESS;
}

void WorkingWhenSleepingLinear::process(ImageComponent* comp) {
    // Get the parameters
    bool highSpeed = property("High Speed").toBool();
    numberOfWindows = property("Number Of Steps").toInt();
    // Getting the input image
    vtkSmartPointer<vtkImageData> inputImage  = comp->getImageData();
    int dims[3];
    inputImage->GetDimensions(dims);

    // Getting the Viewer
    MedicalImageViewer* myMedicalImageViewer = dynamic_cast<MedicalImageViewer*>(Application::getViewer("Medical Image Viewer"));
    if (myMedicalImageViewer != nullptr) {
        myMedicalImageViewer->setVisibleViewer(MedicalImageViewer::VIEWER_ALL);
    }
    else {
        CAMITK_WARNING(tr("Cannot find \"Medical Image Viewer\". This viewer is mandatory for running this action."))
        return;
    }

    // Getting the Camera of the 3DViewer
    RendererWidget* myRendererWidget = dynamic_cast<InteractiveGeometryViewer*>(Application::getViewer("3D Viewer"))->getRendererWidget();
    vtkCamera* myCamera = myRendererWidget->getActiveCamera();

    // Getting the three images slices
    SingleImageComponent* SICSagittal = comp->getSagittalSlices();
    SingleImageComponent* SICCoronal = comp->getCoronalSlices();
    SingleImageComponent* SICAxial = comp->getAxialSlices();

    int currentSagittalSlice = 0;
    int sensSagittal = 1;

    int currentCoronalSlice = 0;
    int sensCoronal = 1;

    int currentAxialSlice = 0;
    int sensAxial = 1;

    int step = 1;
    if (highSpeed) {
        step = 5;
    }

    for (int n = 0; n < numberOfWindows; n++) {
        // modify 3DViewer Camera
        myCamera->Zoom(1. + .01 * sin((double)n * 3.14 / 50.));
        myCamera->Azimuth(step);
        myCamera->Elevation(step);
        myCamera->OrthogonalizeViewUp();
        myRendererWidget->update();

        // modify the current sagittal, coronal, axial slices
        if (SICSagittal != nullptr) {
            SICSagittal->setSlice(currentSagittalSlice);
            currentSagittalSlice += (step * sensSagittal);
            if (currentSagittalSlice <= 0) {
                sensSagittal = 1;
                currentSagittalSlice = 0;
            }
            else {
                if (currentSagittalSlice >= dims[0]) {
                    sensSagittal = -1;
                    currentSagittalSlice = dims[0] - 1;
                }
            }
            SICSagittal->refresh();
        }

        if (SICCoronal != nullptr) {
            SICCoronal->setSlice(currentCoronalSlice);
            currentCoronalSlice += (step * sensCoronal);
            if (currentCoronalSlice <= 0) {
                sensCoronal = 1;
                currentCoronalSlice = 0;
            }
            else {
                if (currentCoronalSlice >= dims[1]) {
                    sensCoronal = -1;
                    currentCoronalSlice = dims[1] - 1;
                }
            }
            SICCoronal->refresh();
        }

        if (SICAxial != nullptr) {
            SICAxial->setSlice(currentAxialSlice);
            currentAxialSlice += (step * sensAxial);
            if (currentAxialSlice <= 0) {
                sensAxial = 1;
                currentAxialSlice = 0;
            }
            else {
                if (currentAxialSlice >= dims[2]) {
                    sensAxial = -1;
                    currentAxialSlice = dims[2] - 1;
                }
            }
            SICAxial->refresh();
        }

        /* another way to do the same thing
                for (int i=0;i<3;i++)
                {
                    SIC[i]->setSlice(currentSlice[i]);
                    currentSlice[i] +=(step*currentSliceSens[i]);
                    if (currentSlice[i]<=0) { currentSliceSens[i]= 1; currentSlice[i]=0;}
                      else
                        if (currentSlice[i]>=dims[i]){ currentSliceSens[i]= -1; currentSlice[i]=dims[i]-1;}
                }

        */

        // Refresh the Viewers ....
        camitk::Application::setProgressBarValue(100 * n / numberOfWindows);

        myMedicalImageViewer->refresh();
        // In order to see the change in the viewers, the initial idea was to call
        // Application::processEvents();
        // but, as explained in the Qt documentation:
        // > In the event that you are running a local loop which calls this function continuously,
        // > without an event loop, the DeferredDelete events will not be processed. This can affect
        // > the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function
        // > properly. An alternative would be to call sendPostedEvents() from within that local loop.
        Application::sendPostedEvents();
    }

    // set normal zoom
    myCamera->Zoom(1.);
    myRendererWidget->update();


}


