/*****************************************************************************
 * $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$
 ****************************************************************************/

// -- Core stuff
#include "RendererWidget.h"
#include "Log.h"
#include "GeometricObject.h"

// -- QT stuff
#include <QApplication>
#include <QKeyEvent>
#include <QFileInfo>
#include <QCursor>
#include <QProcessEnvironment>
#include <QOpenGLContext>

// -- VTK stuff
#include <vtkObjectFactory.h>
// ---- SmartPointer
#include <vtkSmartPointer.h>
// ---- Interactor
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkInteractorStyleJoystickCamera.h>
#include <vtkInteractorStyleImage.h>
#include <vtkCallbackCommand.h>
#include <vtkEventQtSlotConnect.h>
// ---- Renderer
#include <vtkRenderWindow.h>
#include <vtkRendererCollection.h>
#include <vtkCamera.h>
// ---- Various vtk stuff
#include <vtkProperty.h>
#include <vtkMatrix4x4.h>
#include <vtkTransform.h>
#include <vtkActor2D.h>
#include <vtkFollower.h>
#include <vtkAssemblyPath.h>
#include <vtkPoints.h>
#include <vtkMath.h>
// ---- Screenshots
#include <vtkWindowToImageFilter.h>
#include <vtkBMPWriter.h>
#include <vtkJPEGWriter.h>
#include <vtkPNGWriter.h>
#include <vtkPostScriptWriter.h>
#include <vtkGL2PSExporter.h>
#include <vtkOBJExporter.h>
#include <vtkVRMLExporter.h>
#include <vtkRIBExporter.h>
// ---- Text in the background
#include <vtkTextSource.h>
#include <vtkVectorText.h>
#include <vtkProperty.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
// ---- Picking
#include <vtkPicker.h>
#include <vtkProp3DCollection.h>
#include <vtkAreaPicker.h>
// ---- Axes
#include <vtkAxesActor.h>
#include <vtkCaptionActor2D.h>
#include <vtkTextActor.h>
#include <vtkProperty2D.h>
// ---- Annotated cube
#include <vtkAnnotatedCubeActor.h>
#include <vtkAssembly.h>
// ---- Color scale
#include <vtkScalarBarWidget.h>
#include <vtkScalarBarActor.h>
#include <vtkWindowLevelLookupTable.h>
#include <vtkScalarBarRepresentation.h>

namespace camitk {

//--------------- Picking Interactor ---------------------------------------
vtkInstantiatorNewMacro(vtkInteractorStylePick);
vtkStandardNewMacro(vtkInteractorStylePick);
//--------------------------------------------------------------------------
vtkInteractorStylePick::vtkInteractorStylePick() {
    this->areaPicking = false;
    this->startPosition[0] = this->startPosition[1] = 0;
    this->endPosition[0] = this->endPosition[1] = 0;
    this->moving = 0;
    this->pixelArray = vtkSmartPointer<vtkUnsignedCharArray>::New();
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::SetAreaPicking(bool b) {
    this->areaPicking = b;
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::OnLeftButtonDown() {
    if (!this->Interactor) {
        return;
    }

    this->moving = 1;

    if (areaPicking) {
        vtkRenderWindow* renWin = this->Interactor->GetRenderWindow();

        this->startPosition[0] = this->Interactor->GetEventPosition() [0];
        this->startPosition[1] = this->Interactor->GetEventPosition() [1];
        this->endPosition[0] = this->startPosition[0];
        this->endPosition[1] = this->startPosition[1];

        this->pixelArray->Initialize();
        this->pixelArray->SetNumberOfComponents(4);
        int* size = renWin->GetSize();
        this->pixelArray->SetNumberOfTuples(size[0]*size[1]);

        renWin->GetRGBACharPixelData(0, 0, size[0] - 1, size[1] - 1, 1, this->pixelArray);

        this->FindPokedRenderer(this->startPosition[0], this->startPosition[1]);
    }
    else {
        vtkRenderWindowInteractor* rwi = this->Interactor;
        int* eventPos = rwi->GetEventPosition();
        this->FindPokedRenderer(eventPos[0], eventPos[1]);
        this->startPosition[0] = eventPos[0];
        this->startPosition[1] = eventPos[1];
        this->endPosition[0] = eventPos[0];
        this->endPosition[1] = eventPos[1];
        this->Pick();
    }
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::OnMouseMove() {
    if (!this->Interactor || !this->moving) {
        return;
    }
    if (areaPicking) {
        this->endPosition[0] = this->Interactor->GetEventPosition() [0];
        this->endPosition[1] = this->Interactor->GetEventPosition() [1];
        int* size = this->Interactor->GetRenderWindow()->GetSize();
        if (this->endPosition[0] > (size[0] - 1)) {
            this->endPosition[0] = size[0] - 1;
        }
        if (this->endPosition[0] < 0) {
            this->endPosition[0] = 0;
        }
        if (this->endPosition[1] > (size[1] - 1)) {
            this->endPosition[1] = size[1] - 1;
        }
        if (this->endPosition[1] < 0) {
            this->endPosition[1] = 0;
        }
        this->RedrawRubberBand();
    }
    else {
        vtkRenderWindowInteractor* rwi = this->Interactor;
        int* eventPos = rwi->GetEventPosition();
        this->FindPokedRenderer(eventPos[0], eventPos[1]);
        this->startPosition[0] = eventPos[0];
        this->startPosition[1] = eventPos[1];
        this->endPosition[0] = eventPos[0];
        this->endPosition[1] = eventPos[1];
        this->Pick();
    }
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::OnLeftButtonUp() {
    if (!this->Interactor || !this->moving) {
        return;
    }

    if (areaPicking) {
        if ((this->startPosition[0] != this->endPosition[0])
                || (this->startPosition[1] != this->endPosition[1])) {
            this->Pick();
        }
    }

    this->moving = 0;
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::RedrawRubberBand() {
    //update the rubber band on the screen
    int* size = this->Interactor->GetRenderWindow()->GetSize();

    vtkUnsignedCharArray* tmpPixelArray = vtkUnsignedCharArray::New();
    tmpPixelArray->DeepCopy(this->pixelArray);
    unsigned char* pixels = tmpPixelArray->GetPointer(0);

    int min[2], max[2];

    min[0] = this->startPosition[0] <= this->endPosition[0] ?
             this->startPosition[0] : this->endPosition[0];
    if (min[0] < 0) {
        min[0] = 0;
    }
    if (min[0] >= size[0]) {
        min[0] = size[0] - 1;
    }

    min[1] = this->startPosition[1] <= this->endPosition[1] ?
             this->startPosition[1] : this->endPosition[1];
    if (min[1] < 0) {
        min[1] = 0;
    }
    if (min[1] >= size[1]) {
        min[1] = size[1] - 1;
    }

    max[0] = this->endPosition[0] > this->startPosition[0] ?
             this->endPosition[0] : this->startPosition[0];
    if (max[0] < 0) {
        max[0] = 0;
    }
    if (max[0] >= size[0]) {
        max[0] = size[0] - 1;
    }

    max[1] = this->endPosition[1] > this->startPosition[1] ?
             this->endPosition[1] : this->startPosition[1];
    if (max[1] < 0) {
        max[1] = 0;
    }
    if (max[1] >= size[1]) {
        max[1] = size[1] - 1;
    }

    int i;
    for (i = min[0]; i <= max[0]; i++) {
        pixels[4 * (min[1]*size[0] + i)] = 255 ^ pixels[4 * (min[1] * size[0] + i)];
        pixels[4 * (min[1]*size[0] + i) + 1] = 255 ^ pixels[4 * (min[1] * size[0] + i) + 1];
        pixels[4 * (min[1]*size[0] + i) + 2] = 255 ^ pixels[4 * (min[1] * size[0] + i) + 2];
        pixels[4 * (max[1]*size[0] + i)] = 255 ^ pixels[4 * (max[1] * size[0] + i)];
        pixels[4 * (max[1]*size[0] + i) + 1] = 255 ^ pixels[4 * (max[1] * size[0] + i) + 1];
        pixels[4 * (max[1]*size[0] + i) + 2] = 255 ^ pixels[4 * (max[1] * size[0] + i) + 2];
    }
    for (i = min[1] + 1; i < max[1]; i++) {
        pixels[4 * (i * size[0] + min[0])] = 255 ^ pixels[4 * (i * size[0] + min[0])];
        pixels[4 * (i * size[0] + min[0]) + 1] = 255 ^ pixels[4 * (i * size[0] + min[0]) + 1];
        pixels[4 * (i * size[0] + min[0]) + 2] = 255 ^ pixels[4 * (i * size[0] + min[0]) + 2];
        pixels[4 * (i * size[0] + max[0])] = 255 ^ pixels[4 * (i * size[0] + max[0])];
        pixels[4 * (i * size[0] + max[0]) + 1] = 255 ^ pixels[4 * (i * size[0] + max[0]) + 1];
        pixels[4 * (i * size[0] + max[0]) + 2] = 255 ^ pixels[4 * (i * size[0] + max[0]) + 2];
    }

    this->Interactor->GetRenderWindow()->SetRGBACharPixelData(0, 0, size[0] - 1, size[1] - 1, pixels, 0);
    this->Interactor->GetRenderWindow()->Frame();

    tmpPixelArray->Delete();
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::Pick() {
    //find rubber band lower left, upper right and center
    double rbcenter[3];
    int* size = this->Interactor->GetRenderWindow()->GetSize();
    int min[2], max[2];
    min[0] = this->startPosition[0] <= this->endPosition[0] ?
             this->startPosition[0] : this->endPosition[0];
    if (min[0] < 0) {
        min[0] = 0;
    }
    if (min[0] >= size[0]) {
        min[0] = size[0] - 2;
    }

    min[1] = this->startPosition[1] <= this->endPosition[1] ?
             this->startPosition[1] : this->endPosition[1];
    if (min[1] < 0) {
        min[1] = 0;
    }
    if (min[1] >= size[1]) {
        min[1] = size[1] - 2;
    }

    max[0] = this->endPosition[0] > this->startPosition[0] ?
             this->endPosition[0] : this->startPosition[0];
    if (max[0] < 0) {
        max[0] = 0;
    }
    if (max[0] >= size[0]) {
        max[0] = size[0] - 2;
    }

    max[1] = this->endPosition[1] > this->startPosition[1] ?
             this->endPosition[1] : this->startPosition[1];
    if (max[1] < 0) {
        max[1] = 0;
    }
    if (max[1] >= size[1]) {
        max[1] = size[1] - 2;
    }

    rbcenter[0] = (min[0] + max[0]) / 2.0;
    rbcenter[1] = (min[1] + max[1]) / 2.0;
    rbcenter[2] = 0;

    if (this->State == VTKIS_NONE) {
        //tell the RenderWindowInteractor's picker to make it happen
        vtkRenderWindowInteractor* rwi = this->Interactor;

        vtkAssemblyPath* path = nullptr;
        rwi->StartPickCallback();
        vtkSmartPointer<vtkAbstractPropPicker> picker =
            vtkAbstractPropPicker::SafeDownCast(rwi->GetPicker());
        if (picker != nullptr) {
            vtkAreaPicker* areaPicker = vtkAreaPicker::SafeDownCast(picker);
            if (areaPicker != nullptr) {
                areaPicker->AreaPick(min[0], min[1], max[0], max[1],
                                     this->CurrentRenderer);
            }
            else {
                picker->Pick(rbcenter[0], rbcenter[1],
                             0.0, this->CurrentRenderer);
            }
            path = picker->GetPath();
            picker = nullptr;
        }
        if (path == nullptr) {
            this->HighlightProp(nullptr);
            this->PropPicked = 0;
        }
        else {
            //highlight the one prop that the picker saved in the path
            //this->HighlightProp(path->GetFirstNode()->GetViewProp());
            this->PropPicked = 1;
        }
        rwi->EndPickCallback();
    }

    this->Interactor->Render();
}

//--------------------------------------------------------------------------
void vtkInteractorStylePick::PrintSelf(ostream& os, vtkIndent indent) {
    this->Superclass::PrintSelf(os, indent);
}

// static instanciation (global variable, global only for this file)
QMap <RendererWidget::ScreenshotFormat, RendererWidget::ScreenshotFormatInfo*> screenshotMap;

//---------------------- Constructor ------------------------
RendererWidget::RendererWidget(QWidget* parent, ControlMode mode) : QVTKWidget2(parent) {
    setObjectName("RendererWidget");

    //-- initialize screenshotMap only at the first invocation of constructor
    static bool initScreenshotMap = false;
    if (!initScreenshotMap) { //true only for the first instance of LoadCase
        initScreenshotMap = true;
        //populate the map
        buildScreenshotMap();
    }

    //-- Qt stuff
    setMinimumSize(150, 150);
    QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    policy.setHeightForWidth(false);
    setSizePolicy(policy);

    //-- OpenGL context on integrated card
    // For more information about this problem
    // see https://bugzilla-timc.imag.fr/show_bug.cgi?id=181

    // Description of the bugfix:
    // In order to make it work on integrated cards as well as on GPU (using primusrun
    // see below for optirun), RendererWidget is now inheriting from QVTKWidget2 (which
    // in turns inherits from QGLWidget, which is deprecated...)
    //
    // Unfortunately this trigger another bug where, although everything is alright
    // when running on the GPU (using primusrun), it does not work on the integrated card.
    //
    // To make it work on both, the OpenGL format has to be reinitialized.
    //
    // This might lead to performance issue (not investigated). If this is the case,
    // set the environment variable CAMITK_NO_OPENGL_RESET to 1.
    // For instance:
    // CAMITK_NO_OPENGL_RESET=1 primusrun bin/camitk-imp
    // or
    // CAMITK_NO_OPENGL_RESET=1 bin/camitk-imp
    //
    // If the CAMITK_NO_OPENGL_RESET environment variable is set to 1, then the OpenGL context will not be
    // reset.

    // Note for bumblebee users on Linux:
    // There is a bug in bumblebee/optirun, see https://bugreports.qt.io/browse/QTBUG-33258
    // you need to use primusrun (which is also the bumblebee developer team recommendation)

    /*
    CAMITK_TRACE("------------------------ OpenGL information -------------------------")
    CAMITK_TRACE("OpenGL Versions Supported: " << QGLFormat::openGLVersionFlags())
    CAMITK_TRACE("Current format:" << format())
    CAMITK_TRACE("Context valid: " << context()->isValid())
    CAMITK_TRACE("Direct rendering: " << context()->format().directRendering())
    CAMITK_TRACE("Context OpenGL Version: " << context()->format().majorVersion() << "." << context()->format().minorVersion())
    CAMITK_TRACE("Vendor: " << (const char*)glGetString(GL_VENDOR))
    CAMITK_TRACE("Renderer: " << (const char*)glGetString(GL_RENDERER))
    CAMITK_TRACE("Version: " << (const char*)glGetString(GL_VERSION))
    CAMITK_TRACE("GLSL version: " << (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION))
    CAMITK_TRACE("---------------------------------------------------------------------\n")
    */

    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    bool resetOpenGL = true;
    if (env.contains("CAMITK_NO_OPENGL_RESET") && env.value("CAMITK_NO_OPENGL_RESET") == "1") {
        CAMITK_INFO(tr("CAMITK_NO_OPENGL_RESET is set: QGLFormat not reset"))
        resetOpenGL = false;
    }

    if (resetOpenGL) {
        // recreate a format, using some default from the current context
        QGLFormat newFormat;
        newFormat.setProfile(context()->format().profile());
        newFormat.setDepthBufferSize(context()->format().depthBufferSize());
        newFormat.setStencilBufferSize(context()->format().stencilBufferSize());
        context()->setFormat(newFormat);
    }

    //-- display options (this is not a state managed by the renderer, but a property of the actors)
    backfaceCulling = false;
    displayCopyright = true;
    rendering3DRedBlue = false;
    displayColorScale = false;
    pointSize = 4.0;

    //-- render window
    vtkSmartPointer<vtkRenderWindow> renderWindow = GetRenderWindow();

    //-- renderer
    renderer = vtkSmartPointer<vtkRenderer>::New();
    renderWindow->AddRenderer(renderer);

    //-- interactor
    interactor = vtkSmartPointer<QVTKInteractor>::New();
    interactor->SetRenderWindow(renderWindow);
    renderWindow->SetInteractor(interactor);
    pickInteractorStyle = vtkSmartPointer<vtkInteractorStylePick>::New();

    //-- interaction mode
    //setInteractionMode(CONTROL);

    //-- camera control mode
    controlMode = NONE;
    cameraOrientation = RIGHT_DOWN;
    controlInteractorStyle = nullptr;
    setControlMode(mode);
    setCameraOrientation(cameraOrientation);
    interactor->Initialize();

    //-- connection for picking and other interaction
    //connector = vtkSmartPointer<vtkEventQtSlotConnect>::New();
    //connector->Connect(interactor, vtkCommand::LeftButtonPressEvent, this, SLOT(leftClick()));
    //connector->Connect(interactor, vtkCommand::RightButtonPressEvent, this, SLOT(rightClick()));
    //connector->Connect(interactor, vtkCommand::LeftButtonReleaseEvent, this, SLOT(leftRelease()));

    pickingButtonDiverter = vtkSmartPointer<vtkCallbackCommand>::New();
    pickingButtonDiverter->SetClientData(controlInteractorStyle);
    pickingButtonDiverter->SetCallback(RendererWidget::divertionCallback);
    pickingDiverter = false;

    //-- init extra actors

    //-- axes
    axes = vtkSmartPointer<vtkAxesActor>::New();
    axes->SetShaftTypeToCylinder();
    axes->SetXAxisLabelText("x");
    axes->SetYAxisLabelText("y");
    axes->SetZAxisLabelText("z");
    axes->SetTotalLength(0.1, 0.1, 0.1); // 10% of unit length
    vtkSmartPointer<vtkTextProperty> axeXTextProp = vtkSmartPointer<vtkTextProperty>::New();
    axeXTextProp->SetFontSize(20);
    axeXTextProp->BoldOn();
    axeXTextProp->ItalicOn();
    axeXTextProp->ShadowOff();
    axeXTextProp->SetFontFamilyToArial();
    axes->GetXAxisCaptionActor2D()->SetCaptionTextProperty(axeXTextProp);
    // remove the autoscaling so that the font size is smaller than default autoscale
    axes->GetXAxisCaptionActor2D()->GetTextActor()->SetTextScaleModeToNone();
    // set the color
    axes->GetXAxisCaptionActor2D()->GetCaptionTextProperty()->SetColor(1, 0.3, 0.3);
    // make sure the label can be hidden by any geometry, like the axes
    axes->GetXAxisCaptionActor2D()->GetProperty()->SetDisplayLocationToBackground();

    vtkSmartPointer<vtkTextProperty> axeYTextProp = vtkSmartPointer<vtkTextProperty>::New();
    axeYTextProp->ShallowCopy(axeXTextProp);
    axes->GetYAxisCaptionActor2D()->SetCaptionTextProperty(axeYTextProp);
    axes->GetYAxisCaptionActor2D()->GetTextActor()->SetTextScaleModeToNone();
    axes->GetYAxisCaptionActor2D()->GetCaptionTextProperty()->SetColor(0.3, 1, 0.3);
    axes->GetYAxisCaptionActor2D()->GetProperty()->SetDisplayLocationToBackground();

    vtkSmartPointer<vtkTextProperty> axeZTextProp = vtkSmartPointer<vtkTextProperty>::New();
    axeZTextProp->ShallowCopy(axeXTextProp);
    axes->GetZAxisCaptionActor2D()->SetCaptionTextProperty(axeZTextProp);
    axes->GetZAxisCaptionActor2D()->GetTextActor()->SetTextScaleModeToNone();
    axes->GetZAxisCaptionActor2D()->GetCaptionTextProperty()->SetColor(0.3, 0.3, 1.0);
    axes->GetZAxisCaptionActor2D()->GetProperty()->SetDisplayLocationToBackground();
    // add the axes (not visible)
    axes->VisibilityOff();
    addProp(axes);

    //-- annotated cube (right, left, anterior, posterior, inferior, superior)
    // For image Display, the axis should be RAI :
    // - X: from Right    [R]  (the Right of the patient) to Left      [L] (the Left of the patient)
    // - Y: from Anterior [A]  (the Face of the patient)  to Posterior [P] (the Back of the patient)
    // - Z: from Inferior [I]  (the Feet of the patient)  to Superior  [S] (the Head of the patient)
    annotatedCube = vtkSmartPointer<vtkAnnotatedCubeActor>::New();
    annotatedCube->SetXMinusFaceText("R");
    annotatedCube->SetXPlusFaceText("L");
    annotatedCube->SetYMinusFaceText("A");
    annotatedCube->SetYPlusFaceText("P");
    annotatedCube->SetZMinusFaceText("I");
    annotatedCube->SetZPlusFaceText("S");
    annotatedCube->SetXFaceTextRotation(90);
    annotatedCube->SetZFaceTextRotation(90);
    annotatedCube->SetFaceTextScale(0.65);
    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->Identity();
    transform->Scale(0.05, 0.05, 0.05); // 5% of unit length
    annotatedCube->GetAssembly()->SetPosition(0.0, 0.0, 0.0);
    annotatedCube->GetAssembly()->SetUserTransform(transform);
    vtkSmartPointer<vtkProperty> acProp = annotatedCube->GetCubeProperty();
    acProp->SetColor(0.5, 1, 1);
    acProp = annotatedCube->GetTextEdgesProperty();
    acProp->SetLineWidth(1);
    acProp->SetDiffuse(0);
    acProp->SetAmbient(1);
    acProp->SetColor(0.18, 0.28, 0.23);
    acProp = annotatedCube->GetXPlusFaceProperty();
    acProp->SetColor(1, 0, 0);
    acProp->SetInterpolationToFlat();
    acProp = annotatedCube->GetXMinusFaceProperty();
    acProp->SetColor(1, 0, 0);
    acProp->SetInterpolationToFlat();
    acProp = annotatedCube->GetYPlusFaceProperty();
    acProp->SetColor(0, 1, 0);
    acProp->SetInterpolationToFlat();
    acProp = annotatedCube->GetYMinusFaceProperty();
    acProp->SetColor(0, 1, 0);
    acProp->SetInterpolationToFlat();
    acProp = annotatedCube->GetZPlusFaceProperty();
    acProp->SetColor(0, 0, 1);
    acProp->SetInterpolationToFlat();
    acProp = annotatedCube->GetZMinusFaceProperty();
    acProp->SetColor(0, 0, 1);
    acProp->SetInterpolationToFlat();
    // add the annotatedCube
    annotatedCube->VisibilityOff();
    addProp(annotatedCube);

    // -- orientation decorations
    QString orientationDecorationLetters[4];
    orientationDecorationLetters[0] = "R";
    orientationDecorationLetters[1] = "L";
    orientationDecorationLetters[2] = "A";
    orientationDecorationLetters[3] = "P";

    vtkSmartPointer<vtkTextProperty> orientationDecorationsProp;
    orientationDecorationsProp = vtkSmartPointer<vtkTextProperty>::New();
    orientationDecorationsProp->SetFontSize(14);
    orientationDecorationsProp->ShadowOff();
    orientationDecorationsProp->SetColor(0.0, 1.0, 1.0);

    for (int i = 0; i < 4; i++) {

        orientationDecorationsTextMapper[i] = vtkSmartPointer<vtkTextMapper>::New();
        orientationDecorationsTextMapper[i]->SetInput(orientationDecorationLetters[i].toStdString().c_str());
        orientationDecorationsTextMapper[i]->GetTextProperty()->ShallowCopy(orientationDecorationsProp);

        orientationDecorationActors[i] = vtkSmartPointer<vtkActor2D>::New();
        orientationDecorationActors[i]->SetMapper(orientationDecorationsTextMapper[i]);
        orientationDecorationActors[i]->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
    }
    //      y ^
    //        |
    //        |
    //  (0,0) +-----------> x
    // Bottom
    orientationDecorationActors[3]->GetPositionCoordinate()->SetValue(0.5, 0.05);
    // Top
    orientationDecorationActors[2]->GetPositionCoordinate()->SetValue(0.5, 0.95);
    // Right
    orientationDecorationActors[1]->GetPositionCoordinate()->SetValue(0.95, 0.5);
    // Left
    orientationDecorationActors[0]->GetPositionCoordinate()->SetValue(0.05, 0.5);
    for (const auto& orientationDecorationActor : orientationDecorationActors) {
        addProp(orientationDecorationActor);
    }



    // -- text in background
    vtkSmartPointer<vtkTextProperty> copyrightTextProp = vtkSmartPointer<vtkTextProperty>::New();
    copyrightTextProp->ShallowCopy(axeXTextProp);
    copyrightTextProp->SetFontSize(12);
    copyrightTextProp->ShadowOn();

    vtkSmartPointer<vtkTextMapper> copyrightTextMapper = vtkSmartPointer<vtkTextMapper>::New();
    copyrightTextMapper->SetInput(" CamiTK\n(c) TIMC - IMAG");
    copyrightTextMapper->GetTextProperty()->ShallowCopy(copyrightTextProp);
    //  copyrightTextMapper->SetConstrainedFontSize(viewport, targetWidth, targetHeight);

    copyrightTextActor = vtkSmartPointer<vtkActor2D>::New();
    copyrightTextActor->SetMapper(copyrightTextMapper);
    copyrightTextActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();

    //      y ^
    //        |
    //        |
    //  (0,0) +-----------> x
    copyrightTextActor->GetPositionCoordinate()->SetValue(0.85, 0.1);

    // by default the copyright actor is there!
    addProp(copyrightTextActor);

    //-- color scale widget
    // create an inversed the lut
    vtkSmartPointer<vtkWindowLevelLookupTable> vtklup = vtkSmartPointer<vtkWindowLevelLookupTable>::New();
    // inverse hue (from blue to red)
    // other default ( saturation=[1,1] and value=[1,1]) are ok
    vtklup->SetHueRange(2.0 / 3.0, 0.0);
    vtklup->ForceBuild();

    // create the color bar widget itself
    colorBarWidget = vtkSmartPointer<vtkScalarBarWidget>::New();
    colorBarWidget->SetInteractor(interactor);
    colorBarWidget->RepositionableOn();
    colorBarWidget->SelectableOn();

    // set the the color bar parameters
    colorBarWidget->GetScalarBarActor()->SetLookupTable(vtklup);
    colorBarWidget->GetScalarBarActor()->SetOrientationToHorizontal();
    colorBarWidget->GetScalarBarActor()->SetNumberOfLabels(9);
    colorBarWidget->GetScalarBarActor()->SetTextPositionToPrecedeScalarBar();
    colorBarWidget->GetScalarBarActor()->DrawTickLabelsOn();
    colorBarWidget->GetScalarBarActor()->SetTextPad(3);
    colorBarWidget->GetScalarBarActor()->GetTitleTextProperty()->SetColor(0.0, 0.0, 0.0);
    colorBarWidget->GetScalarBarActor()->GetLabelTextProperty()->SetColor(0.0, 0.0, 0.0);

    // set the representation and the way the color bar is displayed
    vtkScalarBarRepresentation* rep =  vtkScalarBarRepresentation::SafeDownCast(colorBarWidget->GetRepresentation());
    rep->SetPosition(0.1, 0.01);
    rep->SetPosition2(0.8, 0.08);

    // default background
    displayGradient = false;

    // set the background color
    setBackgroundColor(0.0, 0.0, 0.0);
}


//----------------------   Destructor   ------------------------
RendererWidget::~RendererWidget() {
    // delete all properties
    foreach (ScreenshotFormatInfo* sfi, screenshotMap.values()) {
        delete sfi;
    }
    screenshotMap.clear();
}

//---------------------- buildScreenshotMap ------------------------
void RendererWidget::buildScreenshotMap() {
    // NB: if you add something here, add something in the switch statement of the screenshot method
    screenshotMap[PNG] = new ScreenshotFormatInfo(PNG, "png", "Portable Network Graphics");
    screenshotMap[JPG] = new ScreenshotFormatInfo(JPG, "jpg", "JPEG");
    screenshotMap[BMP] = new ScreenshotFormatInfo(BMP, "bmp", "Bitmap");
    screenshotMap[PS] = new ScreenshotFormatInfo(PS, "ps", "PostScript");
    screenshotMap[EPS] = new ScreenshotFormatInfo(EPS, "eps", "Encapsulated PostScript");
    screenshotMap[PDF] = new ScreenshotFormatInfo(PDF, "pdf", "Portable Document Format");
    screenshotMap[TEX] = new ScreenshotFormatInfo(TEX, "TeX", "LaTeX(text only)");
    screenshotMap[SVG] = new ScreenshotFormatInfo(SVG, "svg", "Scalable Vector Graphics");
    screenshotMap[OBJ] = new ScreenshotFormatInfo(OBJ, "obj", "Alias Wavefront .OBJ ");
    screenshotMap[RIB] = new ScreenshotFormatInfo(RIB, "rib", "RenderMan/BMRT .RIB");
    screenshotMap[VRML] = new ScreenshotFormatInfo(VRML, "VRML", "VRML 2.0");
    screenshotMap[NOT_SUPPORTED] = new ScreenshotFormatInfo();
}

//---------------------- getScreenshotFormatInfo ------------------------
const RendererWidget::ScreenshotFormatInfo* RendererWidget::getScreenshotFormatInfo(unsigned int id) {
    if (id > NOT_SUPPORTED) {
        return RendererWidget::getScreenshotFormatInfo(NOT_SUPPORTED);
    }
    else {
        return RendererWidget::getScreenshotFormatInfo((ScreenshotFormat)id);
    }
}

const RendererWidget::ScreenshotFormatInfo* RendererWidget::getScreenshotFormatInfo(QString ext)  {
    unsigned int i = 0;
    bool found = false;

    while (i != NOT_SUPPORTED && !found) {
        found = (RendererWidget::getScreenshotFormatInfo(i)->extension == ext);
        i++;
    }

    if (found) {
        i--;
        return RendererWidget::getScreenshotFormatInfo(i);
    }
    else {
        return RendererWidget::getScreenshotFormatInfo(NOT_SUPPORTED);
    }
}

const RendererWidget::ScreenshotFormatInfo* RendererWidget::getScreenshotFormatInfo(ScreenshotFormat f)  {
    return screenshotMap.value(f);
}

//---------------------- screenshot ------------------------
void RendererWidget::screenshot(QString filename) {
    const char* fileprefix = QString(QFileInfo(filename).absolutePath() + "/" + QFileInfo(filename).baseName()).toStdString().c_str();

    // declare exporter/writer that can be used for more than one format
    vtkSmartPointer<vtkImageWriter> imageWriter = nullptr;
    vtkSmartPointer<vtkExporter> exporter = nullptr;

    // check the extension
    QString extension = QFileInfo(filename).suffix().toLower();

    // default is PNG
    if (extension.length() == 0) {
        extension = screenshotMap.value(PNG)->extension;
        filename += "." + extension;
    }

    // it is the correct format
    switch (getScreenshotFormatInfo(extension)->type) {
        case BMP:
            imageWriter = vtkSmartPointer<vtkBMPWriter>::New();
            break;
        case JPG:
            imageWriter = vtkSmartPointer<vtkJPEGWriter>::New();
            break;
        case PS:
            imageWriter = vtkSmartPointer<vtkPostScriptWriter>::New();
            break;
        case EPS:
            exporter = vtkSmartPointer<vtkGL2PSExporter>::New();
            vtkGL2PSExporter::SafeDownCast(exporter)->SetFileFormatToEPS();
            break;
        case PDF:
            exporter = vtkSmartPointer<vtkGL2PSExporter>::New();
            vtkGL2PSExporter::SafeDownCast(exporter)->SetFileFormatToPDF();
            break;
        case TEX:
            exporter = vtkSmartPointer<vtkGL2PSExporter>::New();
            vtkGL2PSExporter::SafeDownCast(exporter)->SetFileFormatToTeX();
            break;
        /* TODO export to POV and export to X3D using corresponding writer
        vtkPOVExporter *povexp = vtkPOVExporter::New();
        povexp->SetRenderWindow(renWin);
        povexp->SetFileName("TestPOVExporter.pov");
        povexp->Write();
          */
        case SVG:
            exporter = vtkGL2PSExporter::New();
            vtkGL2PSExporter::SafeDownCast(exporter)->SetFileFormatToSVG();
            break;
        case OBJ:
            exporter = vtkSmartPointer<vtkOBJExporter>::New();
            vtkOBJExporter::SafeDownCast(exporter)->SetFilePrefix(fileprefix);
            break;
        case RIB:
            exporter = vtkSmartPointer<vtkRIBExporter>::New();
            vtkRIBExporter::SafeDownCast(exporter)->SetFilePrefix(fileprefix);
            break;
        case VRML:
            exporter = vtkSmartPointer<vtkVRMLExporter>::New();
            vtkVRMLExporter::SafeDownCast(exporter)->SetFileName(filename.toStdString().c_str());
            break;
        case NOT_SUPPORTED:
            break;
        case PNG: // default
        default:
            imageWriter = vtkSmartPointer<vtkPNGWriter>::New();
            break;
    }

    // specific options for GL2PS exporter (Vtk 6 has a much better vtkGL2PSExporter)
    vtkSmartPointer<vtkGL2PSExporter> gl2psExporter = vtkGL2PSExporter::SafeDownCast(exporter);
    if (gl2psExporter) {
        vtkGL2PSExporter::SafeDownCast(exporter)->CompressOff();
        vtkGL2PSExporter::SafeDownCast(exporter)->SetSortToBSP();
        vtkGL2PSExporter::SafeDownCast(exporter)->Write3DPropsAsRasterImageOff();
        vtkGL2PSExporter::SafeDownCast(exporter)->SetFilePrefix(fileprefix);
        vtkGL2PSExporter::SafeDownCast(exporter)->DrawBackgroundOff();
    }

    if (exporter) {
        bool displayGradientBackup = displayGradient;
        if (displayGradientBackup) {
            setGradientBackground(false);
        }
        exporter->SetInput(GetRenderWindow());
        exporter->Write();
        if (displayGradientBackup) {
            setGradientBackground(true);
        }
        refresh();
    }
    else {
        if (imageWriter) {
            // create the proper vtk filter for export
            vtkSmartPointer<vtkWindowToImageFilter> imageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
            imageFilter->SetInput(GetRenderWindow());

            // write image
            imageWriter->SetInputConnection(imageFilter->GetOutputPort());
            imageWriter->SetFileName(filename.toStdString().c_str());
            imageWriter->Write();
        }
    }
}

//---------------------- refresh ------------------------
void RendererWidget::refresh() {
    update();
}


//---------------------- setBackgroundColor ------------------------
void RendererWidget::setBackgroundColor(double r, double g, double b) {
    renderer->SetBackground(r, g, b);
    // update copyright text color
    vtkTextMapper::SafeDownCast(copyrightTextActor->GetMapper())->GetTextProperty()->SetColor(1.0 - r, 1.0 - g, 1.0 - b);
}

//---------------------- getBackgroundColor ------------------------
void RendererWidget::getBackgroundColor(double& r, double& g, double& b) {
    renderer->GetBackground(r, g, b);
}

//--------------------- setGradientBackground --------------
bool RendererWidget::getGradientBackground() {
    return displayGradient;
}

//--------------------- setGradientBackground --------------
void RendererWidget::setGradientBackground(bool gb) {
    removeProp(copyrightTextActor);

    displayGradient = gb;

    if (displayGradient) {
        // Setup the background gradient
        renderer->GradientBackgroundOn();
        renderer->SetBackground(0.823, 0.823, 0.921); // light blue in the bottom
        renderer->SetBackground2(1, 1, 1); // white on top
    }
    else {
        renderer->GradientBackgroundOff();
    }

    // have to do that to force the display of (c) on top
    if (displayCopyright) {
        addProp(copyrightTextActor);
    }
}

//--------------------- toggleCopyright --------------
void RendererWidget::toogle3DRedBlue() {
    rendering3DRedBlue = !rendering3DRedBlue;
    if (rendering3DRedBlue) {
        // 3D red/blue
        GetRenderWindow()->StereoCapableWindowOn();
        GetRenderWindow()->SetStereoTypeToRedBlue();
        GetRenderWindow()->StereoRenderOn(); //On();
        GetRenderWindow()->StereoUpdate();
    }
    else {
        GetRenderWindow()->StereoRenderOff(); //On();
        GetRenderWindow()->StereoUpdate();
    }
}

//--------------------- toggleCopyright --------------
void RendererWidget::toggleCopyright(bool c) {
    displayCopyright = c;
    removeProp(copyrightTextActor);

    if (displayCopyright) {
        addProp(copyrightTextActor);
    }
}

// // --------------------- getInteractionMode ------------------------
// RendererWidget::InteractionMode RendererWidget::getInteractionMode() const {
//   return interactionMode;
// }
//
// // --------------------- setInteractionMode ------------------------
// void RendererWidget::setInteractionMode ( RendererWidget::InteractionMode mode ) {
//     if ( mode != interactionMode ) {
//         interactionMode = mode;
//         switch ( mode ) {
//         case PICKING :
//             pickInteractor->SetAreaPicking ( false );
//             break;
//         case AREA_PICKING :
//             pickInteractor->SetAreaPicking ( true );
// 	    break;
//         case CONTROL :
//         default :
//             break;
//         }
//     }
// }

void RendererWidget::setAreaPicking(bool areaPicking) {
    pickInteractorStyle->SetAreaPicking(areaPicking);
}

// --------------------- getControlMode ----------------------------
RendererWidget::ControlMode RendererWidget::getControlMode() const {
    return controlMode;
}

// --------------------- setControlMode ----------------------------
void RendererWidget::setControlMode(ControlMode mode) {
    if (mode != controlMode) {
        controlMode = mode;

        switch (controlMode) {
            case JOYSTICK:
                controlInteractorStyle = vtkSmartPointer<vtkInteractorStyleJoystickCamera>::New();
                break;
            case TRACKBALL:
                controlInteractorStyle = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
                break;
            case TRACKBALL_2D:
                controlInteractorStyle = vtkSmartPointer<vtkInteractorStyleImage>::New();
                break;
            default : //NONE
                controlInteractorStyle = nullptr;
                break;
        }

        interactor->SetInteractorStyle(controlInteractorStyle);

        update();
    }
}

void RendererWidget::startPicking() {
    interactor->SetInteractorStyle(pickInteractorStyle);
    update();
}

void RendererWidget::endPicking() {
    interactor->SetInteractorStyle(controlInteractorStyle);
    update();
}

// --------------------- pick -----------------------------------
void RendererWidget::pick() {
    // Screen coordinates
    QPoint mousePointerPos = QCursor::pos();

    // Widget coordinates
    QPoint inWidgetCoord = mapFromGlobal(mousePointerPos);

    pickActor(inWidgetCoord.x(), inWidgetCoord.y());
}

// --------------------- pickActor -----------------------------------
void RendererWidget::pickActor(int x, int y) {
    //-- if there is no picker or a picker that we can't use with (x,y), then picking was not initialized, so do nothing!
    vtkSmartPointer<vtkPicker> picker = vtkPicker::SafeDownCast(GetInteractor()->GetPicker());
    if (!picker) {
        return;
    }

    // get the current renderer for picking
    renderer = GetInteractor()->FindPokedRenderer(GetInteractor()->GetEventPosition()[0], GetInteractor()->GetEventPosition()[1]);

    // perform picking operation
    int picked = picker->Pick(GetInteractor()->GetEventPosition()[0],
                              GetInteractor()->GetEventPosition()[1],
                              0,  // always zero.
                              GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());

    if (picked) {
        // if something was picked, cry it out loud!
        if (picker->GetProp3Ds()->GetLastProp() != nullptr) {
            emit actorPicked(picker);
        }
    }

}

// ----------------------- setBackfaceCulling -----------------------------
void RendererWidget::setBackfaceCulling(bool culling) {
    backfaceCulling = culling;

    // update all currently managed actors
    vtkSmartPointer<vtkActorCollection> ac = renderer->GetActors();
    ac->InitTraversal();
    vtkSmartPointer<vtkActor> actor;
    while (actor = ac->GetNextActor()) {
        actor->GetProperty()->SetBackfaceCulling(backfaceCulling);
    }

    update();
}

// ----------------------- getBackfaceCulling -----------------------
bool RendererWidget::getBackfaceCulling() const {
    return backfaceCulling;
}

// --------------- setCameraOrientation ------------------------------------------------
void RendererWidget::setCameraOrientation(RendererWidget::CameraOrientation a) {
    cameraOrientation = a;
}

// --------------- getCameraOrientation ------------------------------------------------
RendererWidget::CameraOrientation RendererWidget::getCameraOrientation() const {
    return cameraOrientation;
}


// ----------------------- setLightFollowCamera -----------------------
void RendererWidget::setLightFollowCamera(bool lightFollow) {
    lightFollowCamera = lightFollow;
    interactor->LightFollowCameraOff(); // legacy
    renderer->SetLightFollowCamera(lightFollowCamera);
}

// ----------------------- getLightFollowCamera -----------------------
bool RendererWidget::getLightFollowCamera() const {
    return lightFollowCamera;
}

// ----------------------- setPointSize -----------------------
void RendererWidget::setPointSize(double size) {
    pointSize = size;

    // update all currently managed actors
    vtkSmartPointer<vtkActorCollection> ac = renderer->GetActors();
    ac->InitTraversal();
    vtkSmartPointer<vtkActor> actor;
    while (actor = ac->GetNextActor()) {
        actor->GetProperty()->SetPointSize(pointSize);
    }

    update();
}

// ----------------------- getPointSize -----------------------
double RendererWidget::getPointSize() const {
    return pointSize;
}

//----------------------- toggleAxes -------------------
void RendererWidget::toggleAxes(bool f) {
    axes->SetVisibility(f);
    annotatedCube->SetVisibility(f);
    // remove the axes and annotated cube from the computed bounds
    // (otherwise if they are the biggest object, they are going to set the bounds!)
    axes->SetUseBounds(false);
    annotatedCube->SetUseBounds(false);
    // update sizes
    updateAxes();
}

// ----------------------- updateAxes -----------------------
void RendererWidget::updateAxes() {
    // set the size depending on the bounding box
    // bounds[] = xmin, xmax, ymin, ymax, zmin, zmax
    double bounds[6];
    computeVisiblePropBounds(bounds);
    double xLength, yLength, zLength;
    xLength = fabs(bounds[1] - bounds[0]);
    yLength = fabs(bounds[3] - bounds[2]);
    zLength = fabs(bounds[5] - bounds[4]);
    double maxLength = (xLength > yLength) ?
                       ((xLength > zLength) ? xLength : zLength) :
                       ((yLength > zLength) ? yLength : zLength);

    // the size of the axe is about 10% the size of the bound
    maxLength /= 10.0;
    axes->SetTotalLength(maxLength, maxLength, maxLength);

    // the size of the annotated cube is about 5% of the size of the bound
    maxLength /= 2.0;
    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->Identity();
    transform->Scale(maxLength, maxLength, maxLength);

    // free the last user transform used. Not freeing it, caused a memory leak on each viewer->refresh() call
    // annotatedCube->GetAssembly()->GetUserTransform()->Delete();

    annotatedCube->GetAssembly()->SetUserTransform(transform);
}


// ----------------------- toogleOrientationDecorations ---------
void RendererWidget::toggleOrientationDecorations(bool f) {
    for (auto& orientationDecorationActor : orientationDecorationActors) {
        orientationDecorationActor->SetVisibility(f);
    }
}

// ----------------------- setOrientationDecorationsLetters ---------
void RendererWidget::setOrientationDecorationsLetters(QString letters[4]) {
    vtkSmartPointer<vtkTextProperty> orientationDecorationsProp;
    orientationDecorationsProp = vtkSmartPointer<vtkTextProperty>::New();
    orientationDecorationsProp->SetFontSize(14);
    orientationDecorationsProp->ShadowOff();
    orientationDecorationsProp->SetColor(0.0, 1.0, 1.0);

    for (int i = 0; i < 4; i++) {
        orientationDecorationsTextMapper[i]->SetInput(letters[i].toStdString().c_str());
        if ((letters[i] == "R") || (letters[i] == "L")) {
            orientationDecorationsTextMapper[i]->GetTextProperty()->SetColor(1.0, 0.0, 0.0);
        }
        else if ((letters[i] == "A") || (letters[i] == "P")) {
            orientationDecorationsTextMapper[i]->GetTextProperty()->SetColor(0.0, 1.0, 0.0);
        }
        else if ((letters[i] == "S") || (letters[i] == "I")) {
            orientationDecorationsTextMapper[i]->GetTextProperty()->SetColor(0.0, 0.0, 1.0);
        }
    }

}

// ----------------------- setColorScale -----------------------
void RendererWidget::setColorScale(bool state) {
    displayColorScale = state;
    if (displayColorScale) {
        colorBarWidget->EnabledOn();
    }
    else {
        colorBarWidget->EnabledOff();
    }
}

// ----------------------- getColorScale -----------------------
bool RendererWidget::getColorScale() const {
    return displayColorScale;
}

//------------------------- setColorScaleMinMax ----------------------------
void RendererWidget::setColorScaleMinMax(double min, double max) {
    if (!displayColorScale) {
        setColorScale(true);
    }

    vtkLookupTable::SafeDownCast(colorBarWidget->GetScalarBarActor()->GetLookupTable())->SetTableRange(min, max);
}

//------------------------- setColorScaleTitle ----------------------------
void RendererWidget::setColorScaleTitle(QString title) {
    if (!displayColorScale) {
        setColorScale(true);
    }

    colorBarWidget->GetScalarBarActor()->SetTitle(title.toStdString().c_str());
}

// ----------------------- addProp -----------------------
void RendererWidget::addProp(vtkSmartPointer<vtkProp> prop, bool refresh) {
    if (prop != nullptr && !containsProp(prop)) {
        //-- "intelligence" is defined here
        // NOTE: please add a document any addition/modification in the header autodocumentation!

        //-- for actor set backface culling, point size
        vtkSmartPointer<vtkActor> aPart = vtkActor::SafeDownCast(prop);
        if (aPart) {
            aPart->InitPathTraversal();
            vtkSmartPointer<vtkAssemblyPath> path;
            while ((path = aPart->GetNextPath()) != nullptr) {
                vtkSmartPointer<vtkAssemblyNode> node = path->GetLastNode();
                vtkSmartPointer<vtkActor> a;
                if (node && (a = vtkActor::SafeDownCast(node->GetViewProp()))) {
                    a->GetProperty()->SetBackfaceCulling(backfaceCulling);

                    // check if the point representation is active, and if it is, set the proper size
                    if (a->GetProperty()->GetRepresentation() == VTK_POINTS) {
                        a->GetProperty()->SetPointSize(pointSize);
                    }
                }
            }
        }

        // now add it!
        renderer->AddViewProp(prop);

        if (refresh)
            // update axes size
        {
            updateAxes();
        }
    }

}

// ----------------------- containsProp -----------------------
bool RendererWidget::containsProp(vtkSmartPointer<vtkProp> prop) {
    return (renderer->GetViewProps()->IsItemPresent(prop) != 0);
}

// ----------------------- removeProp -----------------------
void RendererWidget::removeProp(vtkSmartPointer<vtkProp> prop, bool refresh) {
    if (prop != nullptr && containsProp(prop)) {
        renderer->RemoveViewProp(prop);
        if (refresh)
            // update axes size
        {
            updateAxes();
        }
    }
}

// --------------------- actorTransform --------------------------------
void RendererWidget::actorTransform(vtkSmartPointer<vtkActor> actor, double* boxCenter, int numRotation,
                                    double** rotate, double* translate, double* scale) {
    vtkSmartPointer<vtkMatrix4x4> oldMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
    actor->GetMatrix(oldMatrix);

    double orig[3];
    actor->GetOrigin(orig);

    vtkSmartPointer<vtkTransform> newTransform = vtkSmartPointer<vtkTransform>::New();
    newTransform->PostMultiply();

    if (actor->GetUserMatrix() != nullptr) {
        newTransform->SetMatrix(actor->GetUserMatrix());
    }
    else {
        newTransform->SetMatrix(oldMatrix);
    }

    if ((rotate != nullptr) || (scale != nullptr)) {
        newTransform->Translate(-(boxCenter[0]), -(boxCenter[1]),
                                -(boxCenter[2]));

        if (rotate != nullptr) {
            for (int i = 0; i < numRotation; i++) {
                newTransform->RotateWXYZ(rotate[i][0], rotate[i][1],
                                         rotate[i][2], rotate[i][3]);
            }
        }

        if (scale != nullptr && (scale[0] * scale[1] * scale[2]) != 0.0) {
            newTransform->Scale(scale[0], scale[1], scale[2]);
        }

        newTransform->Translate(boxCenter[0], boxCenter[1], boxCenter[2]);

        // now try to get the composit of rotate, and scale
        newTransform->Translate(-(orig[0]), -(orig[1]), -(orig[2]));
        newTransform->PreMultiply();
        newTransform->Translate(orig[0], orig[1], orig[2]);
    }

    // Last - Do the TRANSLATION
    if (translate != nullptr) {
        newTransform->PostMultiply();
        newTransform->Translate(translate[0], translate[1],
                                translate[2]);
    }

    // set the new Actor - Matrix
    if (actor->GetUserMatrix() != nullptr) {
        newTransform->GetMatrix(actor->GetUserMatrix());
    }
    else {
        actor->SetPosition(newTransform->GetPosition());
        actor->SetScale(newTransform->GetScale());
        actor->SetOrientation(newTransform->GetOrientation());
    }

}

// ----------------------- computeVisiblePropBounds -----------------------
void RendererWidget::computeVisiblePropBounds(double* bounds) {
    renderer->ComputeVisiblePropBounds(bounds);
}

// ----------------------- resetClippingPlanes -----------------------
void RendererWidget::resetClippingPlanes(double* bounds) {
    if (bounds != nullptr) {
        renderer->ResetCameraClippingRange(bounds);
    }
    else {
        renderer->ResetCameraClippingRange();
    }
    update();
}

// --------------------- RotateCamera---------------------
void RendererWidget::rotateCamera(double angle, int axe) {
    vtkSmartPointer<vtkCamera> cam = renderer->GetActiveCamera();

    switch (axe) {
        case 0 : //axe X
            cam->Azimuth(0.0);
            cam->Elevation(angle);
            cam->OrthogonalizeViewUp();
            break;

        case 1: //axe y
            cam->Azimuth(angle);
            cam->Elevation(0.0);
            cam->OrthogonalizeViewUp();
            break;

        default :
            break;
    }

    resetClippingPlanes();
}


// --------------------- keyPressEvent --------------------------------
void RendererWidget::keyPressEvent(QKeyEvent* event) {
    // NOTE: nothing but the following line should be here
    // All interaction should be defined directly in the class that use this instance
    // for example, see InteractiveViewer::keyPressEvent method
    event->ignore();
}

//--------------------- mousePressEvent ----------------------------------
void RendererWidget::mousePressEvent(QMouseEvent* event) {
    if ((QApplication::keyboardModifiers() == Qt::ControlModifier) &&
            (event->button() == Qt::LeftButton)) {
        startPicking();
        pickInteractorStyle->OnLeftButtonDown();

    }
    else {
        if (event->button() == Qt::RightButton) {
            emit rightButtonPressed();
        }
        QVTKWidget2::mousePressEvent(event);
    }
}

//--------------------- mouseReleaseEvent ----------------------------------
void RendererWidget::mouseReleaseEvent(QMouseEvent* event) {
    QVTKWidget2::mouseReleaseEvent(event);
    endPicking();
}

//---------------------------- slotLeftClick ---------------------------------
// void RendererWidget::leftClick() {
//     // Ctrl+Left click
//     if (QApplication::keyboardModifiers() == Qt::ControlModifier) {
//         // divert the left button press event, so that it won't
//         // have any effect on the scene while continuously allow picking
// //         if (!pickingDiverter) {
// //             pickingDiverter = true;
// //             interactorStyle->AddObserver(vtkCommand::MouseMoveEvent, pickingButtonDiverter);
// //         }
// //
// //         // do the picking action
// //         pick();
// 	startPicking();
// 	pickInteractorStyle->OnLeftButtonDown();
//     }
// }

//---------------------------- slotLeftRelease ---------------------------------
// void RendererWidget::leftRelease() {
//
// }

//---------------------------- slotRightClick ---------------------------------
// void RendererWidget::rightClick() {
//     emit rightButtonPressed();
// }

// --------------------- mouseMoveEvent ----------------------------------
void RendererWidget::mouseMoveEvent(QMouseEvent* event) {
    if (QApplication::keyboardModifiers() == Qt::ControlModifier && QApplication::mouseButtons() == Qt::LeftButton) {
        // continuous picking mode:
        // you don't have to release the button to do another picking
        // just keep the button pressed
        //pick();
    }
    QVTKWidget2::mouseMoveEvent(event);
}


// --------------------- resetCamera ----------------------
void RendererWidget::resetCamera() {
    resetCameraSettings();

    // auto calculate/update the bounds
    renderer->ResetCamera();
    resetClippingPlanes();
}

void RendererWidget::resetCamera(double* bounds) {
    resetCameraSettings();

    // set the proper bound
    renderer->ResetCamera(bounds);
    resetClippingPlanes(bounds);
}

// --------------------- resetCameraSettings ----------------------
void RendererWidget::resetCameraSettings() {
    vtkSmartPointer<vtkCamera> cam = renderer->GetActiveCamera();

    switch (cameraOrientation) {
        case LEFT_UP:
            // default position
            cam->SetPosition(0, 0, -1);
            cam->SetFocalPoint(0, 0, 0);
            cam->SetViewUp(0, 1, 0);
            cam->OrthogonalizeViewUp();
            break;
        case RIGHT_DOWN:
            // default position
            cam->SetPosition(0, 0, -1);
            cam->SetFocalPoint(0, 0, 0);
            cam->SetViewUp(0, -1, 0);
            cam->OrthogonalizeViewUp();
            break;
        // For Coronal view of Coronal Viewer
        // Image Reorientation Action Documentation...
        case LEFT_BACK:
            cam->SetPosition(0.0, -1.0, 0.0);
            cam->SetFocalPoint(0.0, 0.0, 0.0);
            cam->SetViewUp(0.0, 0.0, 1.0);
            cam->OrthogonalizeViewUp();
            break;
        case BACK_DOWN:
            cam->SetPosition(1.0, 0.0, 0.0);
            cam->SetFocalPoint(0.0, 0.0, 0.0);
            cam->SetViewUp(0.0, 0.0, 1.0);
            cam->OrthogonalizeViewUp();
            break;
        case RIGHT_UP:
        default:
            cam->SetPosition(0, 0, 1);
            cam->SetFocalPoint(0, 0, 0);
            cam->SetViewUp(0, 1, 0);
            cam->OrthogonalizeViewUp();
            break;
    }
}

// --------------------- getCameraSettings ----------------------
void RendererWidget::getCameraSettings(double* position, double* focalPoint, double* viewUp) {
    vtkSmartPointer<vtkCamera> cam = renderer->GetActiveCamera();

    cam->GetPosition(position);
    cam->GetFocalPoint(focalPoint);
    cam->GetViewUp(viewUp);
}

// --------------------- setActiveCamera --------------------------
void RendererWidget::setActiveCamera(vtkCamera* cam) {
    renderer->SetActiveCamera(cam);
}

// --------------------- setActiveCamera --------------------------
vtkCamera* RendererWidget::getActiveCamera() {
    return renderer->GetActiveCamera();
}

// --------------------- getMouse3DCoordinates ----------------------
void RendererWidget::getMouse3DCoordinates(double& x, double& y, double& z) {
    QPoint cursor_pos = mapFromGlobal(QCursor::pos());
    renderer = GetInteractor()->FindPokedRenderer(cursor_pos.x(), cursor_pos.y());

    vtkSmartPointer<vtkCoordinate> c1 = vtkSmartPointer<vtkCoordinate>::New();
    c1->SetCoordinateSystemToDisplay();
    c1->SetValue(double(cursor_pos.x()), double(cursor_pos.y()));
    double* position = c1->GetComputedWorldValue(renderer);

    x = position[0];
    y = position[1];
    z = position[2];
}

// --------------------- setPicker ----------------------
void RendererWidget::setPicker(vtkSmartPointer<vtkAbstractPropPicker> woodyWood) {
    if (GetInteractor()) {
        // I know, it is really Woody Wood Pecker, but you know, with a french accent...
        GetInteractor()->SetPicker(woodyWood);
    }
}


}
