/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#ifndef mitkPlaneGeometryDataVtkMapper3D_h
#define mitkPlaneGeometryDataVtkMapper3D_h

#include <mitkDataStorage.h>
#include <mitkPlaneGeometryDataToSurfaceFilter.h>
#include <mitkVtkMapper.h>
#include <mitkWeakPointer.h>
#include <MitkCoreExports.h>

#include <vtkCleanPolyData.h>
#include <vtkSystemIncludes.h>

class vtkActor;
class vtkPolyDataMapper;
class vtkAssembly;
class vtkFeatureEdges;
class vtkTubeFilter;
class vtkTransformPolyDataFilter;
class vtkHedgeHog;

namespace mitk
{
  class PlaneGeometryData;
  class BaseRenderer;
  class ImageVtkMapper2D;
  class DataStorage;

  /**
  *  \brief Vtk-based mapper to display a PlaneGeometry in a 3D window
  *  \ingroup Mapper
  *
  *  Uses a PlaneGeometryDataToSurfaceFilter object to create a vtkPolyData representation of a given PlaneGeometry
  * instance.
  *  PlaneGeometry may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry).
  *
  *  The vtkPolyData object is then decorated by a colored tube on the edges and by image textures if possible
  *  (currently this requires that there is a 2D render window rendering the same geometry as this mapper).
  *
  * Properties that influence rendering are:
  *
  *   - \b "draw edges": (Bool) Toggle display of the tubed frame
  *   - \b "color": (ColorProperty) Color of the tubed frame.
  *   - \b "xresolution": (FloatProperty) Resolution (=number of tiles) in x direction. Only relevant for
  * ThinPlateSplineCurvedGeometry
  *   - \b "yresolution": (FloatProperty) Resolution (=number of tiles) in y direction. Only relevant for
  * ThinPlateSplineCurvedGeometry
  *   - \b "draw normals 3D": (BoolProperty) If true, a vtkHedgeHog is used to display normals for the generated surface
  * object. Useful to distinguish front and back of a plane. Hedgehogs are colored according to "front color" and "back
  * color"
  *   - \b "color two sides": (BoolProperty) If true, front and back side of the plane are colored differently ("front
  * color" and "back color")
  *   - \b "invert normals": (BoolProperty) Inverts front/back for display.
  *   - \b "front color": (ColorProperty) Color for front side of the plane
  *   - \b "back color": (ColorProperty) Color for back side of the plane
  *   - \b "material.representation": (BoolProperty) Choose the representation to draw the mesh in (Surface, Wireframe,
  * Point Cloud)
  *   - \b "surfacegeometry": TODO: Add documentation
  *   - \b "LookupTable": (LookupTableProperty) Set the lookuptable to render with.
  *
  *  Note: The following properties are set for each image individually, and thus, also influence the rendering of this
  * mapper:
  *
  *   - \b "texture interpolation": (BoolProperty) Turn on/off the texture interpolation of each image
  *   - \b "use color": (BoolProperty) Decide whether we want to use the color property or a lookuptable.
  *   - \b "binary": (BoolProperty) Binary image handling: Color the value=1.0 with the color property and make the
  * background (value=0.0) of the image translucent.
  *   - \b "layer": (IntProperty) Controls what image is considered "on top" of another. In the case that two should
  * inhabit the same space, higher layer occludes lower layer.
  *   - \b "opacity": (FloatProperty) Set the opacity for each rendered image.
  *   - \b "color": (FloatProperty) Set the color for each rendered image.
  */
  class MITKCORE_EXPORT PlaneGeometryDataVtkMapper3D : public VtkMapper
  {
  public:
    mitkClassMacro(PlaneGeometryDataVtkMapper3D, VtkMapper);

    itkFactorylessNewMacro(Self);

    itkCloneMacro(Self);

      /**
      * Overloaded since the displayed color-frame of the image mustn't be
      * transformed after generation of poly data, but before (vertex coordinates
      * only)
      */
      vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
    /** \brief Update the VTK transform for the given renderer. */
    void UpdateVtkTransform(mitk::BaseRenderer *renderer) override;

    /**
     * \brief Get the PlaneGeometryData that is being mapped.
     * \return The PlaneGeometryData input of this mapper.
     */
    virtual const PlaneGeometryData *GetInput();

    /**
     * \brief Set the data storage used for traversing image nodes for texturing.
     *
     * All images found when traversing the data tree that are resliced by an
     * ImageVtkMapper2D will be mapped as textures. This allows using a data
     * storage other than the default.
     *
     * \param storage The data storage to traverse for image nodes.
     */
    virtual void SetDataStorageForTexture(mitk::DataStorage *storage);

  protected:
    typedef std::multimap<int, vtkActor *> LayerSortedActorList;

    PlaneGeometryDataVtkMapper3D();

    ~PlaneGeometryDataVtkMapper3D() override;

    /** \brief Generate the VTK representation for the given renderer. */
    void GenerateDataForRenderer(BaseRenderer *renderer) override;

    /** \brief Process a single data node, creating texture actors for the plane surface. */
    void ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors);

    /** \brief Callback invoked when an ImageVtkMapper2D is destroyed. */
    void ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject &event);

    /** \brief general PropAssembly to hold the entire scene */
    vtkAssembly *m_Prop3DAssembly;

    /** \brief PropAssembly to hold the planes */
    vtkAssembly *m_ImageAssembly;

    PlaneGeometryDataToSurfaceFilter::Pointer m_SurfaceCreator;

    BoundingBox::Pointer m_SurfaceCreatorBoundingBox;

    BoundingBox::PointsContainer::Pointer m_SurfaceCreatorPointsContainer;

    /** \brief Edge extractor for tube-shaped frame */
    vtkFeatureEdges *m_Edges;

    /** \brief Filter to apply object transform to the extracted edges */
    vtkTransformPolyDataFilter *m_EdgeTransformer;

    /** \brief Source to create the tube-shaped frame  */
    vtkTubeFilter *m_EdgeTuber;

    /** \brief Mapper for the tube-shaped frame  */
    vtkPolyDataMapper *m_EdgeMapper;

    /** \brief Actor for the tube-shaped frame */
    vtkActor *m_EdgeActor;

    /** \brief Mapper for black plane background */
    vtkPolyDataMapper *m_BackgroundMapper;

    /** \brief Actor for black plane background */
    vtkActor *m_BackgroundActor;

    /** \brief Transforms the surface before applying the glyph filter */
    vtkTransformPolyDataFilter *m_NormalsTransformer;

    /** \brief Mapper for normals representation (thin lines) */
    vtkPolyDataMapper *m_FrontNormalsMapper;
    vtkPolyDataMapper *m_BackNormalsMapper;

    /** \brief  Generates lines for surface normals */
    vtkHedgeHog *m_FrontHedgeHog;
    vtkHedgeHog *m_BackHedgeHog;

    /** \brief Actor to hold the normals arrows */
    vtkActor *m_FrontNormalsActor;
    vtkActor *m_BackNormalsActor;

    /** Cleans the polyline in order to avoid phantom boundaries */
    vtkCleanPolyData *m_Cleaner;

    /** Internal flag, if actors for normals are already added to m_Prop3DAssembly*/
    bool m_NormalsActorAdded;

    /** \brief The DataStorage defines which part of the data tree is traversed for rendering. */
    mitk::WeakPointer<mitk::DataStorage> m_DataStorage;

    /**
     * \brief Helper class that associates a vtkActor with an observer on a mapper object.
     *
     * Stores the actor, the observed sender, and the observer ID. The observer
     * connection is cleaned up on destruction.
     */
    class MITKCORE_EXPORT ActorInfo
    {
    public:
      vtkActor *m_Actor;       /**< \brief The VTK actor for the image texture. */
      itk::Object *m_Sender;   /**< \brief The observed mapper object (raw pointer, cleaned up via observer). */
      unsigned long m_ObserverID; /**< \brief The observer ID for the delete callback. */

      /** \brief Initialize with an actor, a sender object, and a delete command. */
      void Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command);

      ActorInfo();
      ~ActorInfo();
    };

    /** \brief List holding the vtkActor to map the image into 3D for each
    * ImageMapper
    */
    typedef std::map<ImageVtkMapper2D *, ActorInfo> ActorList;
    ActorList m_ImageActors;

    // responsibility to remove the observer upon its destruction
    typedef itk::MemberCommand<PlaneGeometryDataVtkMapper3D> MemberCommandType;
    MemberCommandType::Pointer m_ImageMapperDeletedCommand;
  };
} // namespace mitk
#endif
