/*****************************************************************************
 * $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 INTERACTIVE_VIEWER_H
#define INTERACTIVE_VIEWER_H

// -- Core stuff
#include "CamiTKAPI.h"
#include "Component.h"
#include "Viewer.h"
#include "RendererWidget.h"
#include "InteractiveViewerFrame.h"

//-- QT stuff
#include <QPushButton>
#include <QWidgetAction>
#include <QComboBox>
class QToolBar;
class QFrame;

//-- vtk stuff
#include <vtkType.h>
#include <vtkSmartPointer.h>

//-- vtk stuff classes
class vtkActor;
class vtkPicker;
class vtkProp;
class vtkObject;
class vtkCamera;
class vtkEventQtSlotConnect;

namespace camitk {
// -- Core stuff classes
class SliderSpinBoxWidget;
class GeometricObject;
class InterfaceGeometry;
class InterfaceBitMap;
class InteractiveViewer;
class PropertyObject;
class Property;
class FrameOfReference;

using vtkSmartPointerCamera = vtkSmartPointer<vtkCamera>;

/**
  * @ingroup group_sdk_libraries_core_viewer
  *
  * @brief
  * InteractiveViewer is used to view 3D objects and slices (anything that provides either a InterfaceBitMap or a InterfaceGeometry).
  *
  * It contains a renderer (class RendererWidget) that combines VTK and QT.
  * The RendererWidget instance manage all the display at the VTK level.
  * InteractiveViewer delegates all pure VTK level stuff to the renderer.
  * All things that needs InterfaceBitMap/InterfaceGeometry interaction/knowledge/access are
  * manage at this level.
  * The keyboard events are all managed in InteractiveViewer as well.
  * Keyboard/Mouse interactions: check "what's this?" on the scene 3D to get all interaction shortcuts.
  *
  * InteractiveViewer manages a list of cameras. Each camera has a name. Use getCamera(QString) to
  * create a new camera, or get the default camera and setActiveCamera(QString) to change the active
  * camera.
  * The default camera is called "default".
  *
  * InteractiveViewer manages picking session.
  * A picking session starts when the control key is pressed and the left mouse button is clicked and ends when
  * the mouse button is released. A picking session is a nice/quick way to do a lot of picking by
  * simply moving the mouse, without the need to click for each picking.
  * At the start of a picking session, the picking action is determined: it is either selection
  * or unselection. If one of the first picked components was already selected, then the user wants to unselect,
  * therefore picking action is "unselection", and all subsequently calls to pickPoint(..) or pickCell(..)
  * will use pickingIsSelecting=false (2nd parameter of the method). If the first picked component was not
  * selected, then the picking session is going to select any picked component.
  * @see InterfaceGeometry::pickPoint() InterfaceGeometry::pickCell()
  *
  * <hr>
  *
  * The following help is for InteractiveViewer/RendererWidget developers, please read if you want/need
  * to change anything in one of this two class. It should help you in your coding decisions (hopefully!).
  *
  * 1. Keyboard shortcut policy: for keyboard shortcuts do not use the Ctrl key modifier for letters/digit (e.g Alt-P is ok,
  * Ctrl+Arrows is ok, but Ctrl-P is not).
  * Ctrl-Letter/Digit shortcuts should only be used for the application shortcuts.
  *
  * 2. That's it for now!
  *
  */

class CAMITK_API InteractiveViewer : public Viewer {
    Q_OBJECT
    Q_ENUMS(HighlightMode RendererWidget::ControlMode RendererWidget::CameraOrientation);    // so that it can be used in property editor

public:
    /// \enum ViewerType there is two possibilities: this InteractiveViewer is used to display slices or geometry
    enum ViewerType {
        SLICE_VIEWER,     ///< display slices (the view is blocked in 2D and the slider is available)
        GEOMETRY_VIEWER   ///< display 3D stuff (geometry, etc...)
    };

    /** \enum PickingMode Different kind of picking must be available: pixel in slice, a point, a cell, ...
     *  \warning int values must correspond to the insertion order in the toolbar combox box
     */
    enum PickingMode {
        LOCATION_PICKING = 0,   ///<pick a point on any VTK representation (can be any location)
        POINT_PICKING    = 1,   ///< pick a point in the VTK representation of an Geometry
        CELL_PICKING     = 2,   ///< pick a cell in the VTK representation of an Geometry
        AREA_POINT_PICKING = 3, ///< pick points that are inside a rectangular area in the VTK representation of an Geometry
        AREA_CELL_PICKING  = 4, ///< pick cells that are inside a rectangular area in the VTK representation of an Geometry
        PIXEL_PICKING      = 5, ///< pick a pixel on a Slice
        NO_PICKING              ///< no picking possible
    };

    /** \enum HighlightMode describes the current mode of display. It is useful to change the
      * way the currently selected Components look compared to the unselected ones.
      * In the default mode a Component is not highlighted, not shaded and not hidden.
      */
    enum HighlightMode {
        OFF,            ///< both selected and non-selected Components are in default mode
        SELECTION,      ///< the selected Components are in default mode, the non-selected Components are shaded
        SELECTION_ONLY  ///< the selected Components are in default mode, the non-selected are hidden
    };

    /** \enum CameraOrientation describes the current position of the camera.
     * It is used to set the camera to fixed standard positions, or Custom for a non-standard orientation
      *
      */
    enum class CameraOrientation {
        XY, // Show the XY plane (camera perpendicular to Z axis)
        XZ, // Show the XZ plane (camera perpendicular to Y axis)
        YZ, // Show the YZ plane (camera perpendicular to X axis)
        AXIAL, // Shows the Axial plane if the current FrameOfReference has anatomicalOrientation information
        CORONAL, // Shows the Coronal plane if the current FrameOfReference has anatomicalOrientation information
        SAGITTAL, // Shows the Sagittal plane if the current FrameOfReference has anatomicalOrientation information
        CUSTOM // Any other orientation (e.g. freely moving camera in the 3D viewer)
    };

    /// @{
    /** Construtor.
     *  @param name    the name of the viewer is mandatory, it is used as an identifier (e.g. in MedicalImageViewer)
     *  @param type    type of the InteractiveViewer, depending on which it will behave as slice viewer, i.e. with no rotation interactions are possible, or 3D viewer
     */
    Q_INVOKABLE InteractiveViewer(QString& name, camitk::InteractiveViewer::ViewerType type);

    /** Destructor */
    virtual ~InteractiveViewer() override;

    /// get the scene name
    QString getName() const;
    /// @}

    /** @name Inherited from Viewer
      */
    ///@{
    /// Refresh the display.
    void refresh(Viewer* whoIsAsking = nullptr) override;

    /// get the InteractiveViewer widget (QTreeWidget).
    QWidget* getWidget() override;

    /// get the  InteractiveViewer propertyObject (only non-null for GEOMETRY_VIEWER)
    PropertyObject* getPropertyObject() override;

    /// get the explorer menu
    QMenu* getMenu() override;

    /// get the viewer toolbar
    QToolBar* getToolBar() override;

    ///@}


    /** @name Manage the viewer frame of reference
      */
    ///@{
    /// set the FrameOfReference of the viewer
    virtual void setFrame(const std::shared_ptr<FrameOfReference>& frame);

    /// get the FrameOfReference of the viewer (it may also contain AnatomicalOrientation information)
    virtual const FrameOfReference* getFrame() const;
    ///@}


    /** @name Refresh/screenshot
     */
    /// @{

    /** Reset scene camera. Use a trick (when this is a SLICE_VIEWER) for scaling up to max size in the viewer */
    void resetCamera();

    /// Set the active virtual camera
    void setActiveCamera(QString cameraName);

    /// @brief Set camera orientation (using InteractiveViewer::CameraOrientation enum class)
    /// @param orientation
    void setCameraOrientation(InteractiveViewer::CameraOrientation orientation);
    /// @brief Get the current camera orientation
    /// @return The camera orientation as an enum
    InteractiveViewer::CameraOrientation getCameraOrientation();


    /** get a camera by its name, creates one if it does not exist already.
      * This method does not activate the given camera, please use setActiveCamera for this.
      */
    vtkSmartPointer<vtkCamera> getCamera(QString cameraName = "default");

    /// call this method to take a screenshot using the given filename (the extension must be a supported format extension, see class RendererWindow)
    void screenshot(QString);

public slots:
    /// call this method to take a screenshot in various format and write the resulting image to a file
    void screenshot();
    /// @}

public:
    /// set gradient background on/off
    virtual void setGradientBackground(bool);

    /// set background color
    virtual void setBackgroundColor(QColor);

public slots:
    /** @name Viewing/Interaction Property
     */
    ///@{
    /// set the backface culling mode (default is true).
    void setBackfaceCulling(bool);

    /** @name Viewing/Interaction Property
     */
    ///@{
    /// set the FXAA antialiasing mode (default is false).
    void setFxaaAntialiasing(bool);

    /// visibility of the screenshot in slice viewers
    void setScreenshotAction(bool);

    /** Update the visualization of lines (for all the InterfaceGeometry of the scene).
      *
      * \note it is only possible to transform lines to tubes if
      * you build an Geometry using lines.
      *
      * @param tubes if true, then the lines have to be displayed as tube
      */
    void setLinesAsTubes(bool tubes);

public:
    /// Handle keyboard events in the scene, let to the parent widget if not processed here. This method is a friend of class InteractiveViewerFrame
    void keyPressEvent(QKeyEvent* e);

    /// Set the current highlighting mode from the current value of the property
    virtual void setHighlightMode();

    /// return interactiveViewer RendererWidget
    inline RendererWidget* getRendererWidget() {
        return rendererWidget;
    }

    ///@}

    /// @name Misc
    /// @{
    /// set the color scale in the viewport, use setColorScaleMinMax / setColorScaleTitle to change the displayed values.
    void setColorScale(bool);

    /// get the current value of the color scale property.
    bool getColorScale() const;

    /** set the min and max values.
     *  Automatically turns setColorScale to true.
     *  @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);

    /** Init the picker with a given picking mode. */
    void initPicking(PickingMode);

    /// Compute the bounding box of the selected elements [xmin,xmax, ymin,ymax, zmin,zmax]
    void getBoundsOfSelected(double* bound);

    /// Compute the bounding box of all displayed Component
    void getBounds(double* bound);

    /// set the slice viewer side bar+screenshot button visibility
    void setSideFrameVisible(bool);
    ///@}

public slots:

    /// @name public slots
    /// @{
    /** Slot called when the InteractiveViewer slider has been changed. If there is a InterfaceBitMap in the scene,
     *    set the slice index with the new slider value. */
    void sliderChanged(int);

    /// show/hide the logo at the bottom right corner
    void toggleLogo(bool);

    /// @}

protected:

    /// used by both constructors
    void init();

    /** @name Display properties
      *
      * Properties that can be managed without the knowledge/intervention of the InterfaceGeometry:
      * - pointSize (the user preferred value is stored here, the access to the actor needs InterfaceGeometry knowledge)
      * - interpolation (toggle, this is a boolean state, kept by each vtkImageActor interpolate property)
      *
      * Properties that need to be managed by the InterfaceGeometry itself (not boolean state managed somewhere
      * by vtk, not integer/float value manage in InteractiveViewer as a user-preference)
      * - linesAsTubes (the InterfaceGeometry needs to add/manage a filter before the mapper)
      */
    /// @{

    /// initialize the property object and state using the user settings (user preferences system files .config/.ini)
    void initSettings();

    /// type of InteractiveViewer (display slice or geometry)
    ViewerType myType;

    /// for InterfaceBitMap, toggle the interpolation mode (intern method, not a property because it can only be modified by the keyboard interaction)
    void toggleInterpolation();

    /// for InterfaceBitMap, reset the lut that was changed by the image interactor (window and level)
    void resetLUT();

    /// Update the display of the given Component, according to its selection state and the current HighlightMode.
    void updateSelectionDisplay(Component*);

    /// the map containing all the actors in the InteractiveViewer
    QMultiMap<Component*, vtkSmartPointer<vtkProp> > actorMap;

    /// the map containing the initial position of the vtkActor2D
    /// They have to be transformed from the creating component frame to the viewer frame
    /// each time they are added to the display as their position is always recomputed/reset
    /// in the component's frame each time getProp(..) is called.
    QMap<vtkSmartPointer<vtkActor2D>, QVector3D> transformed2DActors;

    /// The FrameOfReference in which this component's data is represented in (used to properly position the actors)
    std::shared_ptr<FrameOfReference> frameOfReference;

    /// add the given actor of the given Component to the renderer and insert it in the map
    void addActor(Component*, vtkSmartPointer<vtkProp>, const FrameOfReference* sourceFrame = nullptr);

    /// remove all the given Component actors from the renderer and delete comp from the map
    void removeAllActors(Component*);

    /// update myWidget using the color of the current frame of reference
    void updateCurrentFrameOfReferenceColorIndicator();

    /// number of top-level component that are currently displayed
    unsigned int displayedTopLevelComponents;

    /// all the available camera
    QMap<QString, vtkSmartPointerCamera> cameraMap;
    ///@}

    /// @name Widget/Action management
    /// @{
    /// The 3D scene itself, wrapping VTK render window, renderer and interactor in a single Qt widget
    RendererWidget* rendererWidget;

    /** Slider used to control the slice index in a InteractiveViewer. This slider is visible only when
     *    the scene a 2D viewer (see constructor). */
    SliderSpinBoxWidget* sliceSlider;

    /// the InteractiveViewer myWidget
    InteractiveViewerFrame* myWidget;

    /// the right side myWidget (this is where the slider and screenshot buttons are shown)
    QFrame* sideFrame;

    /// the screenshot action is inside this menu (in the slice viewer side bar)
    QToolBar* screenshotActionMenu;

    /// the InteractiveViewerFrame keyPressEvent is a good friend of InteractiveViewer
    friend void InteractiveViewerFrame::keyPressEvent(QKeyEvent* e);

    /// the QMenu for the InteractiveViewer
    QMenu* viewerMenu;

    /// the QToolBar for the InteractiveViewer
    QToolBar* viewerToolbar;

    /// init all the actions (called only once in the getWidget() method)
    void initActions();

    /// update the viewer menu depending on the selection,...
    void updateActions();

    // Combobox in the toolbar to choose the orientation
    QComboBox* orientationCombo;

    // Combobox in the toolbar to choose the picking mode
    QComboBox* pickingCombo;

    /// Screenshot
    QAction* screenshotAction;

    /// Rendering
    QMenu* renderingMenu;
    QAction* surfaceAction;
    QAction* wireframeAction;
    QAction* pointsAction;
    QAction* colorAction;
    QAction* glyphAction;

    /// display mode
    QAction* highlightSelectionAction;
    QAction* highlightSelectionOnlyAction;
    QAction* highlightOffAction;

    /// to change the camera control mode
    QAction* controlModeTrackballAction;
    QAction* controlModeJoystickAction;

    /// background color
    QAction* backgroundColorAction;

    /// button allows one to display the Axes in the InteractiveViewer
    QAction* toggleAxesAction;

    /// button allows one to display orientation decoration in SLICE_VIEWER mode
    QAction* toggleOrientationDecorationsAction;

    /// button to remove the copyright
    QAction* toggleLogoAction;

    /// button allows one to display the labels of the object3D
    QAction* toggleLabelAction;

    /// button allows one to display the lines as tubes (the lines are to be in vtkPolyData)
    QAction* toggleLinesAsTubesAction;

    /// back face culling
    QAction* toggleBackfaceCullingAction;

    /// FXAA antialiasing
    QAction* toggleFxaaAntialiasingAction;

    /// visibility of the screenshot action in the side toolbar of slice viewer
    QAction* toggleScreenshotAction;

    /// action of the picking menu
    QAction* pickPointAction;
    QAction* pickCellAction;
    QAction* pickCellRegionAction;
    QAction* pickPointRegionAction;
    QAction* pickLocationAction;

    ///@}

    /** @name Picking management
      */
    /// @{
    /** list of Component that are currently picked, correctly displayed in the InteractiveViewer,
      * but for speed optimization that are not yet selected in the explorer.
      * They will all be selected in the explorer when the user release the mouse button.
      */
    std::vector <Component*> pickedComponent;

    /** Current picking mode, POINT_PICKING be default. */
    PickingMode pickingMode;

    /// Indicates that this viewer is picking.
    bool isPicking;

    /// Indicates that this viewer is changing the slice by the slice slider
    bool isChangingSlice;

    /// picking effect while mouse button is kept pressed is selecting (depends on the selection state of the first component picked)
    bool pickingEffectIsSelecting;

    /// was the picking effect updated (it has to be updated with the first picking for a given button down session)
    bool pickingEffectUpdated;

    vtkSmartPointer<vtkEventQtSlotConnect> connector;

    ///@}

    /** @name Help Whats This Utility
      */
    /// @{
    /// The what's html string
    QString whatsThis;

    /// are we currently in a odd table line
    bool oddWhatsThis;

    /// initialize the what's this html string
    void initWhatsThis();

    /// start a table (section) in the what's this message
    void startWhatsThisSection(const QString& title = "");

    /// end a table (section) in the what's this message
    void endWhatsThisSection();

    /// add an item (row) in the the what's this message (to describe a shortcut)
    void addWhatsThisItem(const QString& key, const QString& description);
    /// @}

    /**
      * @name CamiTK Properties of this viewer
      */
    ///@{
    /**
     * The property object that holds the properties of this viewer
     */
    PropertyObject* propertyObject;

    /**
     * The property that stands for the type of highlight mode of the 3D viewer.
     */
    Property* highlightModeProperty;

    /**
     * The property that stands for the current orientation of the viewer.
     */
    Property* cameraOrientationProperty;


    /**
     * The property that stands for the background color of the viewer.
     */
    Property* backgroundColorProperty;

    /**
     * Property that tells whether the viewer use a gradient background color or not.
     */
    Property* backgroundGradientColorProperty;

    /**
     *  Property that tells whether the viewer uses lines as tubes or not.
     */
    Property* linesAsTubesProperty;

    /**
     * Property that tells whether the viewer uses the backface culling option or not.
     */
    Property* backfaceCullingProperty;

    /**
     * Property that tells whether the viewer uses the backface culling option or not.
     */
    Property* fxaaAntialiasingProperty;

    /**
     * Property that tells whether the screenshot action is visible or not.
     */
    Property* screenshotActionProperty;

    /**
     * Property which defines the point size of each point in the 3D viewer.
     */
    Property* pointSizeProperty;

    /**
     * Create and handle the CamiTK properties of this viewer.
     */
    void createProperties();

    /**
     * Event filter of this class instance to watch its properties instances.
     * Each time a property is dynamically changed, this method is called.
     *
     * @note Only used by Geometry viewers
     */
    bool eventFilter(QObject* object, QEvent* event) override;

    ///@}

protected slots:
    /**
     * @name All the slots called by the menu actions
     */
    ///@{
    void renderingActorsChanged();

    void highlightModeChanged(QAction* selectedAction);

    //void cameraOrientationChanged(QAction* selectedAction);

    void viewControlModeChanged(QAction*);

    void backgroundColor();

    void toggleAxes(bool);

    void toggleOrientationDecorations(bool);

    void pickingModeChanged(QAction*);

    /// get the picker and populate the picked component with the picked stuff
    void picked();

    void rightClick();

    /// if true currently selected Components label will have their label on (shown)
    void setLabel(bool);

    void setGlyph(bool);

    ///@}
};

} // namespace camitk

Q_DECLARE_METATYPE(camitk::InteractiveViewer::HighlightMode)

#endif

//**************************************************************************
