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

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 mitkLabelSetImageVtkMapper2D_h
#define mitkLabelSetImageVtkMapper2D_h

// MITK
#include <MitkMultilabelExports.h>
#include <mitkCommon.h>

// MITK Rendering
#include <mitkBaseRenderer.h>
#include <mitkExtractSliceFilter.h>
#include <mitkLabelSetImage.h>
#include <mitkVtkMapper.h>

// VTK
#include <vtkSmartPointer.h>

class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageReslice;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkNeverTranslucentTexture;
class vtkImageMapToColors;

namespace mitk
{
  class IPreferences;

  /** \brief Mapper to resample and display 2D slices of a 3D labelset image.
   *
   * Properties that can be set for labelset images and influence this mapper are:
   *
   *   - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not
   *   - \b "labelset.contour.width": (FloatProperty) line width of the contour

   * The default properties are:

   *   - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite )
   *   - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite )

   * \ingroup Mapper
   */
  class MITKMULTILABEL_EXPORT LabelSetImageVtkMapper2D : public VtkMapper
  {
  public:
    /** Standard class typedefs. */
    mitkClassMacro(LabelSetImageVtkMapper2D, VtkMapper);

    /** Method for creation through the object factory. */
    itkNewMacro(Self);

    /** \brief Get the Image to map */
    const mitk::Image *GetInput(void);

    /** \brief Checks whether this mapper needs to update itself and generate
     * data. */
    void Update(mitk::BaseRenderer *renderer) override;

    /** \brief Get the VTK prop for MITK-VTK rendering pipeline.
     * \param renderer The renderer to get the prop for.
     * \return The vtkProp for rendering.
     */
    vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;

    /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
    /**
       * To render axial, coronal, and sagittal, the mapper is called three times.
       * For performance reasons, the corresponding data for each view is saved in the
       * internal helper class LocalStorage. This allows rendering n views with just
       * 1 mitkMapper using n vtkMapper.
       * */
    class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
    {
    public:
      /** \brief Assembly of all VTK actors used for rendering. */
      vtkSmartPointer<vtkPropAssembly> m_Actors;

      /** \brief Vector containing the pointer of the currently used group images.
       *
       * IMPORTANT: This member must not be used to access any data.
       * Its purpose is to allow checking if the order of the groups has changed
       * in order to adapt the pipeline accordingly.
       */
      std::vector<const Image*> m_GroupImageIDs;

      /** \brief Vector of actors for each label group layer. */
      std::vector<vtkSmartPointer<vtkActor>> m_LayerActorVector;
      /** \brief Vector of mappers for each label group layer. */
      std::vector<vtkSmartPointer<vtkPolyDataMapper>> m_LayerMapperVector;
      /** \brief Vector of resliced image data for each label group layer. */
      std::vector<vtkSmartPointer<vtkImageData>> m_ReslicedImageVector;
      /** \brief Vector of image-to-color mappers for each label group layer. */
      std::vector<vtkSmartPointer<vtkImageMapToColors>> m_LayerImageMapToColors;
      /** \brief Vector of textures for each label group layer. */
      std::vector<vtkSmartPointer<vtkNeverTranslucentTexture>> m_LayerTextureVector;

      /** \brief Empty poly data used as a placeholder. */
      vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
      /** \brief The plane source used for texture mapping. */
      vtkSmartPointer<vtkPlaneSource> m_Plane;

      /** \brief Vector of reslice filters for each label group layer. */
      std::vector<mitk::ExtractSliceFilter::Pointer> m_ReslicerVector;

      /** \brief Poly data for the label outline. */
      vtkSmartPointer<vtkPolyData> m_OutlinePolyData;
      /** \brief An actor for the outline */
      vtkSmartPointer<vtkActor> m_OutlineActor;
      /** \brief An actor for the outline shadow*/
      vtkSmartPointer<vtkActor> m_OutlineShadowActor;
      /** \brief A mapper for the outline */
      vtkSmartPointer<vtkPolyDataMapper> m_OutlineMapper;

      /** \brief Timestamp of last update of stored data. */
      itk::TimeStamp m_LastDataUpdateTime;
      /** \brief Timestamp of last update of a property. */
      itk::TimeStamp m_LastPropertyUpdateTime;
      /** \brief Timestamp of last update of the active label. */
      itk::TimeStamp m_LastActiveLabelUpdateTime;

      /** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
      mitk::ScalarType *m_mmPerPixel;

      /** \brief Look up table for label colors. */
      mitk::LookupTable::Pointer m_LabelLookupTable;

      /** \brief The current world plane geometry. */
      mitk::PlaneGeometry::Pointer m_WorldPlane;
      /** \brief Flag indicating whether the local storage contains valid content. */
      bool m_HasValidContent;

      /** \brief The last time step that was rendered. */
      mitk::TimeStepType m_LastTimeStep;

      /** \brief The number of label group layers. */
      unsigned int m_NumberOfLayers;

      /** \brief The label value of the last active label. */
      MultiLabelSegmentation::LabelValueType m_LastActiveLabelValue = MultiLabelSegmentation::UNLABELED_VALUE;

      /** \brief The "opacity factor" preference value baked into the current LUT.
       *
       * The factor is a global preference (no MTime), so a change is detected by
       * comparing against this cached value to trigger a LUT rebuild. */
      float m_LastOpacityFactor = 1.0f;

      /** \brief Default constructor of the local storage. */
      LocalStorage();
      /** \brief Default destructor of the local storage. */
      ~LocalStorage() override;
    };

    /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
    mitk::LocalStorageHandler<LocalStorage> m_LSH;

    /** \brief Get the LocalStorage corresponding to the current renderer. */
    LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer);

    /** \brief Set the default properties for general image rendering. */
    static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);

    /** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
     * Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
     */
    void ApplyRenderingMode(mitk::BaseRenderer *renderer);

  protected:
    /** \brief Transforms the actor to the actual position in 3D.
      *   \param renderer The current renderer corresponding to the render window.
      */
    void TransformActor(mitk::BaseRenderer *renderer);

    /** \brief Generates a plane according to the size of the resliced image in millimeters.
      *
      * In VTK a vtkPlaneSource is defined through three points. The origin and two
      * points defining the axes of the plane (see VTK documentation). The origin is
      * set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
      * resliced image in space. Z is relevant for blending and the layer property.
      * The center of the plane (C) is also the center of the view plane (cf. the image above).
      *
      * \note For the standard MITK view with three 2D render windows showing three
      * different slices, three such planes are generated. All these planes are generated
      * in the XY-plane (even if they depict a YZ-slice of the volume).
      *
      */
    void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]);

    /** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
        \param renderer Pointer to the renderer containing the needed information
        \param image
        \param pixelValue
        \note This code is based on code from the iil library.
        */
    vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer *renderer,
                                                       vtkImageData *image,
                                                       int pixelValue = 1);

    /** \brief Default constructor. */
    LabelSetImageVtkMapper2D();
    /** \brief Default destructor. */
    ~LabelSetImageVtkMapper2D() override;

    /** \brief Does the actual resampling, without rendering the image yet.
      * All the data is generated inside this method. The vtkProp (or Actor)
      * is filled with content (i.e. the resliced image).
      *
      * After generation, a 4x4 transformation matrix(t) of the current slice is obtained
      * from the vtkResliceImage object via GetReslicesAxis(). This matrix is
      * applied to each textured plane (actor->SetUserTransform(t)) to transform everything
      * to the actual 3D position (cf. the following image).
      *
      * \image html cameraPositioning3D.png
      *
      */
    void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;

    /** \brief Generate the image slice for the given renderer and outdated group IDs.
      * \param renderer The renderer to generate the slice for.
      * \param outdatedGroupIDs The group indices that need to be updated.
      */
    void GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::MultiLabelSegmentation::GroupIndexType>& outdatedGroupIDs);

    /** \brief Generate the outline for the currently active label.
      * \param renderer The renderer to generate the outline for.
      */
    void GenerateActiveLabelOutline(mitk::BaseRenderer* renderer);

    /** \brief Generates the look up table that should be used.
      */
    void GenerateLookupTable(mitk::BaseRenderer* renderer);

    /** \brief This method uses the vtkCamera clipping range and the layer property
      * to calculate the depth of the object (e.g. image or contour). The depth is used
      * to keep the correct order for the final VTK rendering.*/
    float CalculateLayerDepth(mitk::BaseRenderer *renderer);

    /**
      * \brief Calculates whether the given rendering geometry intersects the
      * given SlicedGeometry3D.
      *
      * This method checks if the given Geometry2D intersects the given
      * SlicedGeometry3D. It calculates the distance of the Geometry2D to all
      * 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
      * sign (all positive or all negative) there is no intersection.
      * If the distances have different sign, there is an intersection.
      **/
    bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry* imageGeometry) const;

  private:
    /** \brief Get the opacity factor from user preferences.
      * \return The opacity factor.
      */
    float GetOpacityFactor();
    IPreferences* m_Preferences; ///< \brief Pointer to the user preferences.
  };

} // namespace mitk

#endif
