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

#ifndef RENDERERWIDGET_H
#define RENDERERWIDGET_H

#ifdef CAMITK_ERROR
#error "Header Error: headers reordering required. Please include Log.h after including RendererWidget.h and InteractiveViewer.h or any OpenGL window based class."
#endif

// -- Core stuff
#include "CamiTKAPI.h"

// -- VTK stuff
#include <vtkVersion.h>
#include <vtkPicker.h> // Needed for smart pointers
// disable warning generated by clang about the surrounded headers
#include "CamiTKDisableWarnings"
#include <QVTKInteractor.h>
#include "CamiTKReEnableWarnings"

// -- VTK stuff classes
class vtkInteractorStyle;
class vtkRenderer;
class vtkProp;
class vtkActor;
class vtkActor2D;
class vtkScalarBarActor;
class vtkEventQtSlotConnect;
class vtkCallbackCommand;
class vtkCamera;
class vtkAxesActor;
class vtkAnnotatedCubeActor;
class vtkScalarBarWidget;
class vtkTextMapper;
class vtkAbstractPropPicker;
class vtkLogoWidget;

// Define CamiTKQVTKWidget for VTK9
#include <QVTKOpenGLNativeWidget.h>
#include <vtkGenericOpenGLRenderWindow.h>

using CamiTKQVTKWidget = QVTKOpenGLNativeWidget;



namespace camitk {

class vtkInteractorStylePick;

/**
 * @ingroup group_sdk_libraries_core_viewer
 *
 * @brief
 * RendererWidget implements all support methods to use camiTK with Qt interface.
 *
 * This class wraps the necessary VTK method for rendering, interactions, and signal/slot connections.
 * This class should be usable completely independently of Core.
 *
 * The Core companion class is InteractiveViewer (which delegates all vtk stuff to RendererWidget.
 *
 * It is directly based on QVTKWidget2, the GUI support class available in Vtk version >= 5.0
 * This is a pure Qt/vtk wrapper class (no Core stuff).
 *
 * For developers: please check the coding policy in InteractiveViewer API documentation first.
 *
 *
**/
class CAMITK_API RendererWidget : public CamiTKQVTKWidget {
    Q_OBJECT

public :

    /** \enum CameraOrientation describes the initial position and orientation of the default camera.
      * The world coordinate system is not changed by the CameraOrientation, only the inital position
      * and orientation of the camera.
      * 6 keywords can be used to determine the direction of the x and y axis as intially viewed by the camera:
      * RIGHT, LEFT, UP, DOWN, FRONT, BACK.
      * The first part of the enum name describes the x direction, the second part corresponds to the y
      * direction.
      * The camera is positionned and orientated in order to view the axes as described by the keywords.
      * E.g. RIGHT_DOWN will set the camera so that the x axis points toward the right direction and the
      * y axis points toward the down direction.
      */
    enum CameraOrientation {
        RIGHT_DOWN,  ///< World axes are seen so that x points to the right, y points downward
        LEFT_UP,     ///< World axes are seen so that x points to the left, y points upward
        RIGHT_UP,    ///< World axes are seen so that x points to the right, y points upward
        LEFT_BACK,   /// < World axes are seen so that x points to the left, y points backward. For Medical Images Coronal Views (see Image Reorientation Action Documentation)
        BACK_DOWN,   /// < World axes are seen so that x points to backward, y points to the right. For Medical Images Sagittal Views (see Image Reorientation Action Documentation)
    };
    Q_ENUM(CameraOrientation) // so that it can be used in property editor

    /// \enum ControlMode list of possible user interaction control mode
    enum ControlMode {
        JOYSTICK,       ///< the mouse is used a joystick
        TRACKBALL,      ///< the mouse is used as a trackball (default)
        TRACKBALL_2D,   ///< same as TRACKBALL but does not allow rotation using left button (but zoom and displacement parallel to slice is allowed)
        NONE
    };
    Q_ENUM(ControlMode)

    /// \enum MouseButtonState state of the pressed button (for 3 buttons mouse)
    enum MouseButtonState {
        NO_BUTTON,      ///< no buttons are currently pressed
        LEFT_BUTTON,    ///< the mouse left button is currently pressed
        MIDDLE_BUTTON,  ///< the mouse middle button is currently pressed (or 3rd button emulation)
        RIGHT_BUTTON    ///< the mouse right button is currently pressed
    };

    /** constructors.
      *
      * By default:
      * - the backface culling is off,
      * - the interaction mode is controlling the camera
      * - the light follow the camera
      * - the logo is shown
      * - the gradient background is not shown
      *
      * @param parent the parent widget
      * @param mode the mouse interaction control mode (default is TRACKBALL)
      */
    RendererWidget(QWidget* parent = nullptr, ControlMode mode = RendererWidget::TRACKBALL);

    /// destructor
    ~RendererWidget() override;

    ///@name picking and interaction
    ///@{

    void setAreaPicking(bool areaPicking);

    /// get the current control mode
    ControlMode getControlMode() const;

    /// Set the interaction like trackball or joystick style
    void setControlMode(ControlMode mode);

    /** set the picker to handle the action
      * @param woodyWood the picker (sorry, I could not resist this one!)
      */
    void setPicker(vtkSmartPointer<vtkAbstractPropPicker> woodyWood);

    /// key events (do nothing but pass on e to the parent widget), please do not add any shortcut management here (see note in implementation)!
    void keyPressEvent(QKeyEvent* e) override;

    /** save the screenshot in a file
      * @param filename the filename extension (suffix) must be supported (use getScreenshotFormatInfo to get the correct extension)
      */
    void screenshot(QString filename);

    /// refresh the display
    void refresh();
    ///@}

    /// @name view/camera/interactor settings
    ///@{
    /// Set/unset backface culling on all actors
    void setBackfaceCulling(bool);

    /// Get the current state of backface culling
    bool getBackfaceCulling() const;

    /// Set/unset FXAA Antialiasing in renderer
    void setFxaaAntialiasing(bool);

    /// Get the current state of FXAA Antialiasing in renderer
    bool getFxaaAntialiasing() const;

    /// Set the current axes mode.
    void setCameraOrientation(RendererWidget::CameraOrientation);

    /// Return the current axes mode.
    RendererWidget::CameraOrientation getCameraOrientation() const;

    /// Set/unset the light to follow the camera
    void setLightFollowCamera(bool);

    /// Get the current state of the property
    bool getLightFollowCamera() const;

    /// set the default point size
    void setPointSize(double size);

    /// get the current value of point size
    double getPointSize() const;

    /// Rotate the camera around param "axe" of "angle" degrees
    void rotateCamera(double angle, int axe);

    /** reset the camera to the default position, default FOV.
     * The camera focal is set so that all the things in the scenes are visible
     *  (i.e. reset the camera clipping range based on the bounds of the visible actors. This ensures that no props are cut off)
     */
    void resetCamera();

    /// reset the camera to the default position, default FOV and use the given bounds to focus on
    void resetCamera(double* bounds);

    /// get camera settings information (position, what is looked at and how) in world coordinates
    void getCameraSettings(double* position, double* focalPoint, double* viewUp);

    /// set active camera
    void setActiveCamera(vtkCamera* cam);

    /// get the active camera
    vtkCamera* getActiveCamera();

    /// get the current interactor (utility to manage VTK7/VTK8 and VTK9 compatibility)
    vtkSmartPointer<QVTKInteractor> getInteractor();

    /// get the render window (utility to manage VTK7/VTK8 and VTK9 compatibility)
    vtkSmartPointer<vtkRenderWindow> getRenderWindow();

    /// get the mouse coordinates in 3D
    void getMouse3DCoordinates(double& x, double& y, double& z);

    /// set the background color (rgb)
    void setBackgroundColor(double, double, double);

    /// get the background color (rgb)
    void getBackgroundColor(double&, double&, double&);

    /// get the current state of the gradient background
    bool getGradientBackground();

    /// set the gradient background
    void setGradientBackground(bool);

    /// toggle stereo 3D red/blue rendering (you will need red/blue glasses)
    void toogle3DRedBlue();

    /// toggle logo
    void toggleLogo(bool);

    /// display the axes
    void toggleAxes(bool);

    /// update the axes sizes
    void updateAxes();

    /// display orientation decorations
    void toggleOrientationDecorations(bool);

    /// give the letters for orientation decoration: Left, Right, Top, Down
    void setOrientationDecorationsLetters(QStringList letters);

    /// display the color scale in the viewport, use setColorScaleMinMax to change the displayed values
    void setColorScale(bool);

    /// get the color display state
    bool getColorScale() const;

    /** set the min and max values.
     *  @param m minimum value (blue)
     *  @param M maximum value (red)
     */
    void setColorScaleMinMax(double m, double M);

    /** set the color scale title.
     *  @param t title of the color scale
     */
    void setColorScaleTitle(QString t);

    /// get the bounding box of all visible actors [xmin,xmax, ymin,ymax, zmin,zmax]
    void computeVisiblePropBounds(double* bounds);

    /// reset the camera clipping plane to a given bounding box
    /// If no bounds are given, reset to show all visible actors
    /// @param bounds the clipping plane will be set to the bounding box [xmin,xmax, ymin,ymax, zmin,zmax]
    void resetClippingPlanes(double* bounds = nullptr);
    ///@}

    /// @name add/manipulate actors
    ///@{
    /** add a vtkActor or vtkActor2D, updating the cull face depending on the current state.
      * The method checks it is not already there first.
      * This method is "clever": it does different things (that should be documented below), depending on the type of the vtkProp
      *
      * Action performed depending on the vtkProp (true) type:
      * - vtkActor: apply the current backface culling and point size property to the actor.
      * @param p the vtkProp to add to the scene
      * @param refresh if true the axes are refreshed (default false)
      */
    void addProp(vtkSmartPointer<vtkProp> p, bool refresh = false);

    /// is the given vtkProp (e.g. vtkActor or vtkActor2D) in this renderer
    bool containsProp(vtkSmartPointer<vtkProp>);

    /** remove the given vtkProp (e.g. vtkActor or vtkActor2D, such as color scale)
      * @param p the vtkProp to add to the scene
      * @param refresh if true the axes are refreshed (default false)
      */
    void removeProp(vtkSmartPointer<vtkProp> p, bool refresh = false);

protected slots:
    ///@name picking and interaction
    ///@{
    /// start picking
    void startPicking();

    /// end picking
    void endPicking();

    ///@}

signals :

    ///@name picking and interaction
    ///@{
    /// when an actor is picked...
    void actorPicked(vtkSmartPointer<vtkPicker>);

    /// send when the mouse right button is clicked
    void rightButtonPressed();
    ///@}

protected:

    /// mouse press handler overriden to manage different version of VTK
    void mousePressEvent(QMouseEvent* event) override;

    /// mouse release handler overriden to manage different version of VTK
    void mouseReleaseEvent(QMouseEvent* event) override;

    /// mouse move handler overriden to manage different version of VTK
    void mouseMoveEvent(QMouseEvent* event) override;

protected :

    ///@name picking and interaction
    ///@{
    ///to manage interactions
    vtkSmartPointer<QVTKInteractor> renderWindowInteractor;

    ///for the interaction with the scene
    vtkSmartPointer<vtkInteractorStyle> controlInteractorStyle;

    /// current control mode
    ControlMode controlMode;

    ///@}

    /// @name view/camera settings
    ///@{
    /// Reset camera settings (position, what is looked at and how)
    void resetCameraSettings();

    /// The current renderer
    vtkSmartPointer<vtkRenderer> renderer;

    /// Is back face culling on?
    bool backfaceCulling;

    // Is FXAA antialiasing on?
    bool fxaaAntialiasing;

    /// state of the initial camera orientation
    CameraOrientation cameraOrientation;

    /// Is the light following the camera
    bool lightFollowCamera;

    /// default point size
    double pointSize;

    /// is rendering in 3D stereo red/blue
    bool rendering3DRedBlue;
    ///@}

    /// @name callback and interaction
    ///@{

    /// picking interactor
    vtkSmartPointer<vtkInteractorStylePick> pickInteractorStyle;

    ///@}

    ///@name extra actors managements
    ///@{
    /// is the gradient background displayed
    bool displayGradient;

    /// is the logo displayed
    bool displayLogo;

    /// logo widget
    vtkSmartPointer<vtkLogoWidget> logoWidget;

    /// is the color scale currently displayed
    bool displayColorScale;

    /// the color scale displaying the lookup table + values
    vtkSmartPointer<vtkScalarBarActor> colorScale;

    /// the scalar bar widget
    vtkSmartPointer<vtkScalarBarWidget> colorBarWidget;

    /// axes actor
    vtkSmartPointer<vtkAxesActor> axes;

    /// annotated cube actor
    vtkSmartPointer<vtkAnnotatedCubeActor> annotatedCube;

    /// annotated cube text actors
    vtkSmartPointer<vtkActor2D> orientationDecorationActors[4];

    /// annotated cube text
    vtkSmartPointer<vtkTextMapper> orientationDecorationsTextMapper[4];

    ///@}

};

}

#endif //RENDERERWIDGET_H

