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

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

#include <QMap>

//-- VTK Classes
#include <vtkTransformFilter.h>
#include <vtkTubeFilter.h>
#include <vtkTextMapper.h>
#include <vtkCastToConcrete.h>
#include <vtkTexture.h>

class vtkPointSet;
class vtkAlgorithmOutput;
class vtkDataSetMapper;
class vtkActor;
class vtkProp;
class vtkGlyph3D;
class vtkTransform;
class vtkSphereSource;


namespace camitk {
/**
 * @ingroup group_sdk_libraries_core_component
 *
 * @brief
 * A 3D representation of a vtkPointSet to be displayed in a InteractiveViewer,
 * this class implements the InterfaceGeometry.
 *
 * A Geometry is build out of a vtkPointSet. A single vtkDataSetMapper is created,
 * with several associated vtkActors. Each actor has specific rendering properties:
 * representation mode, color, ...
 *
 * Available actors are: surface, wireframe and points.
 *
 * An actor is visible or not according to the options of the RenderingModes associated to the Geometry
 * as well as the highlight mode (from hidden to highlighted).
 *
 * \note when the Geometry is instantiated the Geometry TAKES CONTROL of the given
 * vtkPointSet (hence, if you then delete the instance, the vtkDataSet will be deleted as well).
 *
 * This class is used as a delegate by Component (who delegates all the InterfaceGeometry
 * service implementation to Geometry),
 * according to the Object Adapter Design Pattern (Component is the adaptor, Geometry is the adaptee
 * concerning the InterfaceGeometry service).
 *
 * If you need to add your own vtk filter/algorithm pipeline to pre-process the data, you
 * need to write something like this:
 * \code
 *    vtkSomeAlgorithm *startFilter = vtkSomeAlgorithm::New();
 *    startFilter->SetInputConnection(theAbstractGeometry->getDataPort());
 *    ...
 *    ...
 *    theAbstractGeometry->setDataConnection(endFilter->GetOutputPort());
 * \endcode
 *
 *
 *  The complete Vtk Pipeline looks like this:
 *
 * \verbatim
 *                                                                                  +-----> getDataPort()
 *  +----------------+     +------------------+      +---------------------------+ /
 *  |  vtkPointSet   |     |vtkCastToConcrete |      |vtkTransformPolyDataFilter |/
 *  |                |----\|                  |----\ |                           |----\ your custom  ----\ ...
 *  |   pointSet     |----/|  concreteData    |----/ |    data in world frame    |----/  pipeline    ----/
 *  +----------------+     +------------------+      +--------------------------+|
 *     ^           |                                                      |
 *     |           |                                                      |
 * setPointSet()   |                                                      |
 *                 |                                                      |
 *                 v                                                      v
 *          getPointSet()                                       getPointSetWorldCoords()
 *
 *
 * setDataConnection(..)
 *      |
 *      |  +---OPTIONAL---+       +------------------+
 *      +->| vtkTubeFilter|       | vtkDataSetMapper |
 *... ----\|              |-----\ |                  |
 *    ----/|     tube     |-----/ |     mapper       |
 *         +--------------+       +------------------+
 *                ^                     |
 *                |                     V
 *         setLinesAsTube()        +------------------+
 *                                 |     3 actors:    |
 *                                 |   surfaceActor   |
 *                                 | wireframeActor   |
 *                                 |    pointsActor   |
 *                                 |                  |
 *                                 +------------------+
 *
 * \endverbatim
 *
 * The other vtk (minor) ingredients not mentioned are specific vtkProp (actors, volumes and annotations).
 * By default two props are defined: label and glyph.
 * They can be accessed by getProp("label") and getProp("glyph").
 * You can also add your own additional custom props by using addProp(QString, vtkProp) and setPropVisible(QString, bool).
 */
class CAMITK_API Geometry : public InterfaceGeometry {

public:
    /** instantiate a Geometry using existing stuff.
     * \note when the Geometry is instantiated like this,
     * the object takes CONTROL of the vtkDataSet (hence, if you then delete this instance,
     * the vtkDataSet will be deleted as well).
     * dataSet thus becomes owned by the Geometry and will be deleted when necessary,
     * so do not pass the address of an object on the stack.
     *
     * @param label name to display on the mesh using vtkProp
     * @param pointSet the vtkDataSet to take as the vtk object to display
     * @param mode the default rendering mode
     */
    Geometry(QString label, vtkSmartPointer<vtkPointSet> pointSet, const InterfaceGeometry::RenderingModes mode = InterfaceGeometry::Surface);

    /// destructor
    ~Geometry() override;

    /**
      * @name InterfaceGeometry Vtk related inherited methods
      * All the implemented InterfaceGeometry methods (Geometry is the adaptee of Component)
      */
    ///@{
    /// Return the dataset associated to this object.
    vtkSmartPointer<vtkPointSet> getPointSet() override {
        return pointSet;
    }

    /// set the input data of the Geometry, \note if there is already a vtkPointSet, this method calls DeepCopy(ds)
    void setPointSet(vtkSmartPointer<vtkPointSet> ds) override;

    /// Set the world transform (if the Geometry depends on another Frame)
    void setMeshWorldTransform(vtkSmartPointer<vtkTransform>) override;

    /// get the custom algorithm pipeline input.
    vtkSmartPointer<vtkAlgorithmOutput> getDataPort() const override {
        return dataOutput;
    }

    /// call this method with the custom algorithm pipeline output
    void setDataConnection(vtkSmartPointer<vtkAlgorithmOutput>) override;

    /// set the point data (may contains a lookup table)
    void setPointData(vtkSmartPointer<vtkDataArray>) override;

    /** Return the actor representing this representation mode (returns nullptr if hightlight mode is Hidden).
      * If RenderingModes have a more than one possible representation, it returns in
      * priority order: the surface, wireframe or points representation.
      */
    vtkSmartPointer<vtkActor> getActor(const RenderingModes) override;

    /** Set a texture to this object. */
    void setTexture(vtkSmartPointer<vtkTexture> texture) override;

    /// a vtkPoint of the structured was picked (to be reimplemented in a Component inherited class if needed)
    void pointPicked(vtkIdType, bool) override {};

    /// a vtkCell of the structured was picked (to be reimplemented in a Component inherited class if needed)
    void cellPicked(vtkIdType, bool) override {};

    ///@}

    /// @name manage extra prop associated with a Geometry
    /// @{
    /// 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
    vtkSmartPointer<vtkProp> getProp(const QString&) override;

    /// return the number of additional prop
    unsigned int getNumberOfProp() const override;

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

    /** 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)
     */
    bool addProp(const QString&,  vtkSmartPointer<vtkProp>) override final;

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

    /// @name InterfaceGeometry Helpers inherited methods
    /// @{

    /// compute the object's bounding box [xmin,xmax, ymin,ymax, zmin,zmax]
    void getBounds(double* bounds) override final;

    /// compute the object's bounding sphere radius
    double getBoundingRadius() override final;

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

    ///@}


    /// @name InterfaceGeometry rendering mode settings inherited methods
    /// @{

    /// Set the actor associated to a rendering mode visible or not.
    void setRenderingModes(const RenderingModes rMode) override {
        renderingModes = rMode;
    }

    /// Return if the actor associated to a rendering mode is currently visible or not.
    const RenderingModes getRenderingModes() const override {
        return renderingModes;
    }

    /// set the enhanced mode
    void setEnhancedModes(const EnhancedModes) override;

    /// get the current enhanced mode
    const EnhancedModes getEnhancedModes() const override {
        return enhancedModes;
    }

    /// Set the color of given representation modes.
    void setActorColor(const RenderingModes, double*) override;

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

    /// Get the color of given representation modes in the second parameter, i.e. double[4] (r,g,b,a). Parameter ignoreEnhancedModes is used to get either the current color or the color without any enhanced mode.
    void getActorColor(const RenderingModes, double*,  bool ignoreEnhancedModes = false) const override final;

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

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

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

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

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

    /// Set the mapper scalar range
    void setMapperScalarRange(double min, double max) override;

    /// set the glyph information
    void setGlyphType(const GlyphTypes type, const double size = 0.0) override final;

    /// display lines as tubes (depeding of the boolean) (<b>only work if the Geometry was defined using a vtkPolyData</b>).
    void setLinesAsTubes(bool isTubes = true, bool radiusFromLength = true, double radiusFactor = 1.0 / 40.0, int numberOfSides = 5) override;

    void setColorMode(int vtkColorMode = VTK_COLOR_MODE_DEFAULT) override;

    ///@}

    /// @name Label management
    /// @{
    /** update position and text of the label prop
     */
    virtual void updateLabel(const QString& label) override;
    ///@}

private:

    /** @name VTK members (data, filters, actors, etc...)
     */
    ///@{
    /// The low-level VTK data
    vtkSmartPointer<vtkPointSet> pointSet;

    /// to be able to set external custom pipeline
    vtkSmartPointer<vtkAlgorithmOutput> dataOutput;

    /// the external custom pipeline output (equals to dataOuput if no custom pipeline plugged)
    vtkSmartPointer<vtkAlgorithmOutput> customPipelineOutput;

    /// the filter to convert the DataSet to get a correct vtkPipeline output port
    vtkSmartPointer<vtkCastToConcrete> concreteData;

    /// the VTK mapper
    vtkSmartPointer<vtkDataSetMapper> mapper;

    /// the mapper to create the text
    vtkSmartPointer<vtkTextMapper> labelActorMapper;

    /// the surface actor that manages the surfacic representation
    vtkSmartPointer<vtkActor> surfaceActor;

    /// the wireframe actor that manages the representation as wireframe
    vtkSmartPointer<vtkActor> wireframeActor;

    /// the point actor that manages the representation as a set of points
    vtkSmartPointer<vtkActor> pointsActor;

    /** texture of this object. */
    vtkSmartPointer<vtkTexture> texture;

    /// the tube filter (creates tubes insead of lines)
    vtkSmartPointer<vtkTubeFilter> tube;

    /// the transform filter to place the mesh correctly with respect to its Frame
    vtkSmartPointer<vtkTransformFilter> worldTransformFilter;

    /// the sphere glyph
    vtkSmartPointer<vtkSphereSource> sphereGeom;
    ///@}

    /// @name Other members
    ///@{
    /// Rendering mode options for this Geometry (which actors are visible/rendered)
    InterfaceGeometry::RenderingModes renderingModes;

    /// Enhanced mode options (the way actors are rendered: normal, hidden, highlighted, shaded)
    InterfaceGeometry::EnhancedModes enhancedModes;

    /// Opacity value when this object must be shaded.
    double alphaShaded;

    /// the label
    QString label;

    /// current size of glyph (0.0 means no glyph)
    double glyphSize;

    /// build the label extra prop
    void buildLabelProp();

    /// update label prop text and position
    void updateLabelProp();

    /// build the glyph extra prop (sphere glyph by default)
    void buildGlyph(const GlyphTypes type);

    /** force visualization of point cloud
      * If the point set does only contains a point cloud, i.e., there is no cell to visualize the points,
      * this method will add a default poly vertex cell that will enable default visualization.
      * Caveat: if the user save this component, this cell might be saved as well.
      */
    void createPointCloudVisualization();
    ///@}

    /** @name actor colors
     * All Colors are decribed using r, g, b, alpha (Alpha is opacity 0 = transparent, 1 = opaque)
     */
    ///@{
    double surfaceColor[4];
    double wireframeColor[4];
    double pointsColor[4];
    ///@}

    /// @name Backup states
    ///@{
    double oldAlphaSurface;
    double oldAlphaWireframe;
    double oldAlphaPoints;
    ///@}

    /// @name manage extra prop associated with a Geometry
    /// @{
    /// TODO see extra prop management method section

    /// The additional map for prop (include at least "label" and "glyph")
    QMap<QString, vtkSmartPointer<vtkProp> > extraProp;
    ///@}

};



}

#endif
