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

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

// -- vtk stuff
// disable warning generated by clang about the surrounded headers
#include "CamiTKDisableWarnings"
#include <vtkWindowLevelLookupTable.h>
#include <vtkActor.h>
#include <vtkImageActor.h>
#include "CamiTKReEnableWarnings"

#include <vtkImageData.h>
#include <vtkTransform.h>
#include <vtkUnstructuredGrid.h>
#include <vtkImageReslice.h>

namespace camitk {
/**
 * @ingroup group_sdk_libraries_core_component
 *
 * @brief
 * Display a slice (i.e. an image or BitMap) of an @ref camitk::ImageComponent "ImageComponent". Helper class.
 *
 * This class manages the visual representation of one slice of a volume image.
 * The slice depends on the orientation and the currently selected slice index.
 * A special case is the arbitrary orientation, where the orientation is not perpendicular to one
 * of the axes, but can have any free orientation and translation.
 *
 * A slice is represented in 2D and 3D thanks to:
 * - image2DActor that provides a 2D representation (the slice in the world reference frame)
 * - image3DActor that provides a 3D representation (the slice at this actual 3D position)
 * Both are updated thanks to vtkImageActor::SetDisplayExtent.
 *
 * Slice also manages two other actors to visualize user picking inside the image:
 * - the picked plane actor (a wireframe box made of 4 quads around the picked plane) that shows the image border
 * - the picked pixel actor (two wireframe quads centered on the currently picked pixel) that shows the position
 *   of the currently picked pixel.
 * These two actors are build using unstructured grids.
 *
 * The picked plane actor is defined by 8 points surrounding the current image plane.
 * The points are set at the coordinates of the (red / blue / green) border of the current image plane.
 * To make sure that the borders are visible in any specific orientation, four quads are build from this points.
 * Each quad goes out of the image plane on both side (back and front).
 *
 * The picked pixel actor is also made of 8 points that describes the cross centered on the currently picked pixel.
 * The points are set at the coordinates of the (red / blue / green) cross around the picked pixel.
 * To make sure that the cross is visible in any specific orientation, two quads are build from this 8 points
 * Each quad goes out of the image plane on both side
 *
 * \note (easter egg) hit "t" on any slice viewer and move the camera around to see the quad geometries and
 * how these extra actors are build.
 *
 * \note <b>Warning</b> Be careful, the <b>center</b> of the first voxel, of image coordinates (0,0,0),
 * is displayed at coordinates (0.0, 0.0, 0.0) → There is a small shift of half a pixel.
 * The same occurs to the last pixel.
 * Therefore, pixels of a 2D slice that are located on an image border are represented only by their half,
 * or quarter depending on their coordinates.
 *
 *  \verbatim
 *       3D Volume                         2D Slice
 *       ________  /|\
 *      /|      /|  |                      _______
 *     /______ / | _|_slice     ===>      /       /        Displayed in
 *    |  |____|_/|  |  number            /______ /         the window
 *    | /     |//
 *    |/______|/
 *
 *                                          ________________
 *                                         | vtkLookUpTable |
 *                                      ---|                |
 *                                     |   |    lut         |
 *                                     |   |________________|
 *  setOriginalVolume(..)              |
 *   |                                 | setLookUpTable
 *   |                   setInput      |                                   __________________
 *   |   ________________       _______v____________                      |  vtkImageActor   |
 *   |  |  vtkImageData  |     | vtkImageMapToColor |                     |(setDisplayExtent)|
 *   |_\|                |----\|                    |-------------------\ |                  |
 *     /| originalVolume |----/|   imgToMapFilter   |-------------------/ |   image2DActor   |
 *      |________________|     |____________________|     | |             |__________________|
 *                                                        | |
 *                                                        | |              _________________________            __________________
 *                                                        | |             |  vtkTransformFilter     |          |  vtkImageActor   |
 *                                                        |  -----------\ |(setWorldTransformation) | --------\|(setDisplayExtent)|
 *                                                         -------------/ |                         | --------/|                  |
 *                                                                        |                         |          |   image3DActor   |
 *                                                                        |_________________________|          |__________________|
 *
 *
 *
 * \endverbatim
 *
 */

class CAMITK_API Slice : public InterfaceBitMap {
public:
    /** Common slices orientation: axial, sagittal, coronal axial_neuro.
     * Axial, Sagittal and Coronal orientation are given in Radiologist point of view.
     * The neurologist point of view is displayed in axial_neuro.
     *
     * The ImageComponent is supposed to be given in RAI orientation.
     * (see the "Reorient Medical Image" Action Documentation).
     * \image html images/imageRAI.png "RAI orientation (Right to left, Anterior to posterior, Inferior to superior)"
     *
     * AXIAL: from feet to head of the patient
     * \image html images/axialView.png "Axial slice"
     *
     * CORONAL: from the front to back  of the patient
     * \image html images/coronalView.png "Coronal slice"
     *
     * SAGITTAL: from the right to left of the patient
     * \image html images/sagittalView.png "Sagittal slice"
     *
     * AXIAL_NEURO: from head to feet (other side of AXIAL)
     *
     * ARBITRARY: any arbitrary orientation.
     *
     * @see ReorientImageExtension
     */
    enum SliceOrientation {
        AXIAL,
        CORONAL,
        SAGITTAL,
        AXIAL_NEURO,
        ARBITRARY
    };

    /// @name Constructors / Destructors
    /// @{
    /// Constructor
    Slice(vtkSmartPointer<vtkImageData> volume, SliceOrientation AXIAL_ORIENTATION, vtkSmartPointer<vtkWindowLevelLookupTable> lookupTable = nullptr);

    /// virtual destructor
    ~Slice() override;
    /// @}

    /// @name  InterfaceBitMap implementation
    /// @{

    /// set the original volume image data (the source vtkImageData before any reslice) and refresh the vtk pipeline
    void setOriginalVolume(vtkSmartPointer<vtkImageData> img) override final;

    /** Return the vtkImageActor (vtkProp) representing a slice to be displayed in the 2D viewers. */
    vtkSmartPointer<vtkImageActor> get2DImageActor() const override;

    /** Return the vtkImageActor (vtkProp) representing a slice to be displayed in the 3D viewers. */
    vtkSmartPointer<vtkImageActor> get3DImageActor() const override;

    /** Return the vtkActor visualizing the plane of the slices. */
    vtkSmartPointer<vtkActor> getPickPlaneActor() const override;

    /** Return the vtkActor visualizing the picked pixels in the slices. */
    vtkSmartPointer<vtkActor> getPixelActor() override;

    /** This method is called when the associated plane has been picked in the InteractiveViewer,
     *  the given coordinates is position where the plane was picked.
     */
    void pixelPicked(double, double, double) override;

    /// update the position of the plane surrounding the currently selected slice
    void updatePickPlane() override final;

    /** Return the number of slices in the image data set. */
    int getNumberOfSlices() const override final;

    /** Return the index of the current displayed slice. */
    int getSlice() const override;

    /** Set the current slice index.
     * If the slice index is less than the first slice index, the first slice is displayed.
     * If the slice index is more than the last slice index, the last slice is displayed.
     * @param s the index of the slice to display (base 0). */
    void setSlice(int s) override final;

    /// Set the slice corresponding to the given real image (RAI) coordinates
    void setSlice(double x, double y, double z) override;

    /** Return the number of colors in the images.
    * If color is coded on 1 byte, the images are on 256 grey level.
    * If color is coded on 2 bytes, the images are on 4096 grey level (not 65536). */
    int getNumberOfColors() const override;

    /// move the pixel selection green indicator (pixelActor) to the given real position
    void setPixelRealPosition(double, double, double) override;

    /// Set the pointer to the arbitrary slice transformation.
    /// this should be done once, at initialization.
    /// For SingleImageComponent, it should be the same as the frame of the arbitrary slices (getTransform).
    /// This is the transformation relative to the 3D image (it is not parallel to one
    /// of the main axe, but has a specific rotation and translation relative to the vtkImage).
    /// This is only required for arbitrary orientation.
    virtual void setArbitraryTransform(vtkSmartPointer<vtkTransform>) override;

    /// get the current image data
    vtkSmartPointer<vtkImageData> getImageData() const override;

    // @}

    /// @name manage extra prop associated with a Slice
    /// @{
    /// 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;

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

    /// @}


protected:

    /// @name Protected utility methods
    /// @{

    /** Initialize Attributes */
    void init();

    /** Initialize actors and everything they need.*/
    void initActors();

    /** Compute the volume coordinates (xyz) from the resliced coordinates (ijk)
     *  @param ijk: given resliced coordinates (generally from a pixel picked in the pickPlane)
     *  @param xyz: output (should be allocated before calling this function) volume coordinates (with image at the origin in RAI convention) computed from the input
    */
    void reslicedToVolumeCoords(const double* ijk, double* xyz);

    /// Compute the resliced coordinates (ijk) from the volume coordinates (xyz)
    /// @param xyz: given volume coordinates (with image at the origin in RAI convention)
    /// @param ijk: output (should be allocated before calling this function) resliced coordinates computed from the input
    void volumeToReslicedCoords(const double* xyz, double* ijk);
    /// @}

    /// @name Attributes / Members of the class
    /// @{

    /** Direction of the reslice */
    SliceOrientation sliceOrientation;

    /** Smart pointer to the original volume to reslice (input of the vtk pipeline) */
    vtkSmartPointer<vtkImageData> originalVolume;

    /** Keep track of the slice number */
    int currentSliceIndex;

    /// Common lookup table
    vtkSmartPointer<vtkWindowLevelLookupTable> lut;

    /** Voxel size of the original image volume. Used to compute point coordinates between real world and index world.*/
    double originalSpacing[3];

    /** Real size (originalDimension * originalSpacing in x, y and z) of the original image */
    double originalSize[3];

    /// 3D actor
    vtkSmartPointer<vtkImageActor> image3DActor;

    /// 2D actor
    vtkSmartPointer<vtkImageActor> image2DActor;
    /// @}

    /// @name Management of the arbitrary slice
    /// @{

    /// The image reslicer computes the arbitrary slice pixels
    vtkSmartPointer<vtkImageReslice> image2DReslicer;

    /// @}

    /// @name  Used to visualize the current picking
    /// @{

    /// init the pick plane actor
    void initPickPlaneActor();

    /**
     * Init the pixel actor for pixel picking
     */
    void initPixelActor();

    /** Update the pixel actor position according to the specified pixel picked by the user
     * i.e. Compute and draw the bounding box around the selected pixel.
     * @param x:
     * The absciss value of the selected pixel
     * @param y:
     * The ordinate value of the selected pixel
     * @param z:
     * The depth value of the selected pixel. In the plane, it's always +/- 0.01 in order to the pixel actor to be
     * visible over the slice.
     **/
    void updatePixelActor(double x, double y, double z);

    /// update the pixel actor to the middle of the current slice
    void updatePixelActor();

    /** Actor representing the pickPlane. */
    vtkSmartPointer<vtkActor> pickPlaneActor;

    /// the pick plane actor unstructured grid
    vtkSmartPointer<vtkUnstructuredGrid> pickPlaneActorPointSet;

    /** Actor representing a pixel, displayed over the image. */
    vtkSmartPointer<vtkActor> pixelActor;

    /// the pixel actor unstructured grid
    vtkSmartPointer<vtkUnstructuredGrid> pixelActorPointSet;
    /// @}

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

    /// The additional map for prop
    QMap<QString, vtkSmartPointer<vtkProp> > extraProp;

    /// @}
};

}

#endif // CANONICAL_SLICE_H

