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

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

// -- vtk stuff classes
class vtkPointSet;
class vtkTexture;
class vtkActor;
class vtkProp;
class vtkDataArray;
class vtkAlgorithm;
class vtkAlgorithmOutput;
class vtkTransform;


namespace camitk {
/**
 * @ingroup group_sdk_libraries_core_component
 *
 * @brief
 * This class describes what are the methods to implement
 * for a Geometry (rendering parameters, input/output, filters, picking parameters...)
 *
 * An InterfaceGeometry is a kind of simplifier/wrapper for vtkPointSet.
 *
 * This class defines an "interface" (in the OOP/java meaning of the term).
 * See the introduction of GoF: "Program to an interface, not an implementation."
 * To see what Erich Gamma has to say about it: http://www.artima.com/lejava/articles/designprinciplesP.html
 * To see what Bjarne Stroustrup has to say about it: http://www.artima.com/intv/modern.html
 *
 * @see Geometry
 */
class InterfaceGeometry {

public:
    /// @enum RenderingMode (and QFlags RenderingModes) handle actor rendering options (render this InterfaceGeometry as a surface, a wireframe and set of points).
    enum RenderingMode {
        None = 0x0,      ///< no rendering mode, the InterfaceGeometry is not visible
        Surface = 0x1,   ///< the surface is visible
        Wireframe = 0x2, ///< the wireframe is visible
        Points = 0x4    ///< the points are visible
    };
    Q_DECLARE_FLAGS(RenderingModes, RenderingMode)

    /// @enum EnhancedMode (and QFlags EnhancedModes) handle the way the rendering actors will be enhanced or not (from completely hidden to highlighted)
    enum EnhancedMode {
        Normal = 0x0,       ///< the object is normally displayed
        Hidden = 0x1,       ///< the object is hidden
        Shaded = 0x2,       ///< the object is shaded
        Highlighted = 0x4  ///< the object is highlighted
    };
    Q_DECLARE_FLAGS(EnhancedModes, EnhancedMode)

    /// @enum GlyphType (and QFlags GlyphTypes) is the type of glyph attached to the geometry representation
    enum GlyphType {
        NoGlyph = 0x0,      ///< there are no glyph type attached to the geometry
        Sphere = 0x1       ///< the glyph is a nice sphere
    };
    Q_DECLARE_FLAGS(GlyphTypes, GlyphType)

    /// empty virtual destructor, to avoid memory leak
    virtual ~InterfaceGeometry() = default;

    /// @name Vtk related
    /// @{
    /// get the low-level vtk data
    virtual vtkSmartPointer<vtkPointSet> getPointSet() = 0;

    /** set the low-level data set.
     * \note if there is already a vtkPointSet, this method calls DeepCopy(ds)
     *
     * \note it is very important to overload this method in your Component subclass as this will be called by all the operators
     *  that operates a change directly on the data.
     */
    virtual void setPointSet(vtkSmartPointer<vtkPointSet>) = 0;

    /// set the point data (may contains a lookup table). \note values have to be in interval [0..1]
    virtual void setPointData(vtkSmartPointer<vtkDataArray>) = 0;

    /// set the transformation for 3D representation
    virtual void setMeshWorldTransform(vtkSmartPointer<vtkTransform>) = 0;


    /** get the custom algorithm pipeline input.
      * This method returns the unmodified data that you have to use
      * as an input for your filter/algorithm.
      * I.e. if you want to temporarily apply some filter/algorithm to the InterfaceGeometry
      * call this method to get the input of your filter/algorithm pipeline.
      * Typically, your custom filter/algorithm connection should start with:
      * \code
      *    vtkSomeAlgorithm *startFilter = vtkSomeAlgorithm::New();
      *    startFilter->SetInputConnection(theAbstractGeometry->getDataPort());
      *    ...
      *    // in the end call setDataConnection()
      * \endcode
      *
      * @see setDataConnection()
      * @see Geometry
      */
    virtual vtkSmartPointer<vtkAlgorithmOutput> getDataPort() const = 0;

    /** Set/reset the connection for the InterfaceGeometry internal algorithm.
      * This method sets the input for the InterfaceGeometry mapping/rendering pipeline.
      * Your should call this method to have the correct rendering of
      * your custom filter/algorithm pipelines.
      * Typically, your custom filter/algorithm connection should end with:
      *
      * \code
      *    // begining of the custom filter/algorithm pipelines (don't forget to call getDataPort()!)
      *    ...
      *    theAbstractGeometry->setDataConnection(endFilter->GetOutputPort());
      * \endcode
      *
      * To remove your custom pipeline, either call setDataConnection(nullptr) or
      * theAbstractGeometry->setDataConnection(theAbstractGeometry->getDataPort())
      *
      * @see getDataPort()
      * @see Geometry
      */
    virtual void setDataConnection(vtkSmartPointer<vtkAlgorithmOutput>) = 0;

    /// Set a texture to this object.
    virtual void setTexture(vtkSmartPointer<vtkTexture>) = 0;

    /** This method is called when a vtkPoint included in the vtk representation was picked.
     *
     *  When the picking mode is set in InteractiveViewer to POINT_PICKING
     *  the vtk picker can select a specific vtkPoint in the big Geometry.
     *
     *  A Component re-implementing this method can manage a specific selection process (or any other suitable
     *  action).
     *
     *  @param pointId the vtkPoint id that was selected by picking
     *  @param pickingIsSelecting indicates if the current picking session is selecting or unselecting
     *  @see InteractiveViewer
     */
    virtual void pointPicked(vtkIdType pointId, bool pickingIsSelecting) = 0;

    /** This method is called when a vtkCell included in the vtk representation was picked.
     *
     *  This is the same as method getChildComponentFromVtkPointId, but for CELL_PICKING.
     *
     *  A Component re-implementing this method can manage a specific selection process (or any other suitable
     *  action).
     *
     *  @param cellId the vtkCell id that was selected by picking
     *  @param pickingIsSelecting indicates if the current picking session is selecting or unselecting
     *  @see InteractiveViewer
     */
    virtual void cellPicked(vtkIdType cellId, bool pickingIsSelecting) = 0;

    /// Return the actor for the representation mode, nullptr if the actor doesn't exist.
    virtual vtkSmartPointer<vtkActor> getActor(const RenderingModes) = 0;
    ///@}

    /// @name manage extra prop associated with an InterfaceGeometry
    /// @{
    /// TODO
    /// - put all this management into a dedicated interface
    /// - remove it from InterfaceBitMap and InterfaceGeometry
    /// - remove it from Slice and Geometry helper classes
    /// - create a new associated helper class
    /// - update Component class and all other code using it (if needed)
    /// Note : beware that Geometry requires this to manage to at least "label" and "glyph" extra actors

    /// Return the vtkProp (actors, volumes and annotations) corresponding to the given name
    virtual vtkSmartPointer<vtkProp> getProp(const QString&) = 0;

    /// return the number of additional prop
    virtual unsigned int getNumberOfProp() const = 0;

    /// return an additional prop by its index
    virtual vtkSmartPointer<vtkProp> getProp(unsigned int) = 0;

    /** remove a given additional prop.
     * @return true if effictively done
     */
    virtual bool removeProp(const QString&) = 0;

    /** insert an additional prop, defining it by its name  (default visibility = false).
     *  @return true if the additional prop was added (i.e. another additional prop of the same name does not exist)
     */
    virtual bool addProp(const QString&,  vtkSmartPointer<vtkProp>) = 0;

    /** update position and text of the label prop
     */
    virtual void updateLabel(const QString& label) = 0;

    ///@}

    /// @name Helpers methods
    /// @{

    /// compute the object's bounding box [xmin,xmax, ymin,ymax, zmin,zmax]
    /// @param bounds the 6-sized double tab of the bounding box (in 3D).
    /// @warning bounds must points a 6 double-sized tab minimum.
    virtual void getBounds(double* bounds) = 0;

    /// compute the object's bounding sphere radius
    virtual double getBoundingRadius() = 0;

    /// set a given point position
    virtual void setPointPosition(const unsigned int orderNumber, const double x, const double y, const double z) = 0;

    ///@}

    /// @name rendering mode settings
    /// @{
    /// Set the actor associated to a rendering mode visible or not.
    virtual void setRenderingModes(const RenderingModes) = 0;

    /// Return if the actor associated to a rendering mode is currently visible or not.
    virtual const RenderingModes getRenderingModes() const = 0;

    /// set the enhanced mode
    virtual void setEnhancedModes(const EnhancedModes) = 0;

    /// get the current enhanced mode
    virtual const EnhancedModes getEnhancedModes() const = 0;

    /// Set the color of given representation modes.
    /// @param color the 4-sized double tab of color (r,g,b,a) of the actor.
    /// @warning color must points a 4 double-sized tab minimum.
    virtual void setActorColor(const RenderingModes, double* color) = 0;

    /// Set the color of given representation modes.
    virtual void setActorColor(const RenderingModes, const double, const double, const double) = 0;

    /// Get the color of given representation modes in the second parameter.
    /// @param color the 4-sized double tab of color (r,g,b,a) of the actor.
    /// @param ignoreEnhancedModes Return the color without considering the changes that may be due to enhanced modes (highlight)
    /// @warning color must points a 4 double-sized tab minimum.
    virtual void getActorColor(const RenderingModes mode, double* color, bool ignoreEnhancedModes = false) const = 0;

    /// Set an (r,g,b) color to all representation modes, without changing the opacity.
    virtual void setColor(const double, const double, const double) = 0;

    /// Set an (r,g,b,a) color to all representation modes.
    virtual void setColor(const double, const double, const double, const double) = 0;

    /// Set the opacity of this representation modes. WARNING color field (surfaceColor, ...) are not modified!
    virtual void setActorOpacity(const RenderingModes, const double) = 0;

    /// Return the opacity of a given renderng mode.
    virtual double getActorOpacity(const RenderingModes) const = 0;

    /// Set the opacity of this object. WARNING color field (surfaceColor, ...) are not modified!
    virtual void setOpacity(const double) = 0;

    /// Set the mapper scalar range
    virtual void setMapperScalarRange(double min, double max) = 0;

    /** Set the glyph type (a glyph is a geometric representation attached to every point in the input dataset).
      * The glyph size is needed when the type is not NoGlyph (the size value is used depending on the current GlyphTypes) :
      * - if type is Sphere, size is the radius for the sphere (this is the default)
      * - if type is NoGlyph, then no glyph are shown
      *
      * To show the glyph call getProp("glyph")->SetVisibility(true) or getProp("glyph")->VisibilityOn().
      *
      * @param type the glyph type
      * @param size size of the glyph (default is 0.0)
      */
    virtual void setGlyphType(const GlyphTypes type, const double size = 0.0) = 0;

    /** Set the lines as tubes (<b>works only for vtkDataSet representation that contains lines</b>)
     * @param isTubes activate tube representation of lines
     * @param radiusFromLength radius of tubes is computed as a proportion of line length
     * @param radiusFactor radius of tubes will be : radiusFactor*lineLength if radiusFromLength is true, radiusFactor if it is false
     * @param numberOfSides Number of sides of the tubes
     */
    virtual void setLinesAsTubes(bool isTubes = true, bool radiusFromLength = true, double radiusFactor = 1.0 / 40.0, int numberOfSides = 5) = 0;

    virtual void setColorMode(int vtkColorMode = VTK_COLOR_MODE_DEFAULT) = 0;

    ///@}
};


}

// declare the | aka OR operators
Q_DECLARE_OPERATORS_FOR_FLAGS(camitk::InterfaceGeometry::RenderingModes)
Q_DECLARE_OPERATORS_FOR_FLAGS(camitk::InterfaceGeometry::GlyphTypes)
Q_DECLARE_OPERATORS_FOR_FLAGS(camitk::InterfaceGeometry::EnhancedModes)


#endif
