/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2024 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 ARBITRARYSINGLEIMAGEVOLUMECOMPONENT_H
#define ARBITRARYSINGLEIMAGEVOLUMECOMPONENT_H

// -- Core stuff
#include "SingleImageComponent.h"

// -- QT stuff classes
#include <QVector3D>

namespace camitk {
/**
 * @ingroup group_sdk_libraries_core_component_image
 *
 * @brief
 * This Component manages the specific case of arbitrary orientation of a sub-component of the image component.
 *
 * Arbitrary slice frame is used to position and orientate the slice inside the parent image volume.
 *
 * It does have a Slice representation (InterfaceBitMap), not a Geometry.
 *
 * Some frame management methods (inherited from InterfaceFrame) are overriden in order to manage
 * the arbitrary orientation.
 *
 * These method should allow the displacement but constrained it
 * so that the center of the arbitrary slice is always inside the parent ImageComponent
 * volume.
 *
 * For arbitrary slice, the default transform is set to be on the z plane
 * but in the middle of the volume.
 *
 * The initial position of the frame has therefore no rotation
 * and a translation equals to (0, 0, dim[2]*spacing[2])
 *
 * Note:
 * - setSlice methods on arbitrary slice do nothing
 * - getSlice and getNumberOfSlices return 0.0
 * Use getTranslationInVolume() method to know where the slice is relatively to the parent ImageComponent.
 *
 */
class CAMITK_API ArbitrarySingleImageComponent : public camitk::SingleImageComponent {
    Q_OBJECT

public:
    /// Constructor
    ArbitrarySingleImageComponent(Component* parentComponent, const QString& name, vtkSmartPointer<vtkWindowLevelLookupTable> lut);

    /// Destructor
    ~ArbitrarySingleImageComponent() override = default;

    /// @name overriden from Component to manage arbitrary orientation
    ///
    /// @{
    /// same as default but notify the slice as well
    virtual void setTransform(vtkSmartPointer<vtkTransform>) override;

    /// reset the transformation to the image component parent so that the frame
    /// of the arbitrary slice is in the center of the volume along the z axis by default
    virtual void resetTransform() override final;

    /// set the arbitrary slice translation inside the volume between 0 and 1 (first two parameters are ignored)
    virtual void setTransformTranslation(double, double, double) override final;

    /// rotate of the given angles around the center of the frame/arbitrary slice
    virtual void setTransformRotation(double, double, double) override final;

    /// translation along the z axis (TODO implement this method)
    CAMITK_API_UNIMPLEMENTED virtual void translate(double, double, double) override;

    /// rotate on the slide center (TODO implement this method)
    CAMITK_API_UNIMPLEMENTED virtual void rotate(double, double, double) override;

    /// rotate on the slide center using the VTK way (TODO implement this method)
    CAMITK_API_UNIMPLEMENTED virtual void rotateVTK(double, double, double) override;

    /// set translation the VTK way (TODO implement this method)
    CAMITK_API_UNIMPLEMENTED virtual void setTransformTranslationVTK(double, double, double) override;

    /// set rotation the VTK way (TODO implement this method)
    CAMITK_API_UNIMPLEMENTED virtual void setTransformRotationVTK(double, double, double) override;

    /// set slice as a percentage on the z axis translation
    /// \note To set the slice using absolute value, please use setTransformTranslation and getTranslationInVolume instead
    virtual void setSlice(int) override;

    /// rewritten because the setSlice(int) method is overriden (compiler needs this)
    virtual void setSlice(double, double, double) override;

    /// return the slice as the percentage of translation (between 0 and 100)
    /// \note To set the slice using absolute value, please use setTransformTranslation and getTranslationInVolume instead
    virtual int getSlice() const override;

    /// always return 100 (as getSlice() gives a percentage value, the max number of slices is 100.
    /// \note To set the slice using absolute value, please use setTransformTranslation and getTranslationInVolume instead
    virtual int getNumberOfSlices() const override;

    /** This method is called when the arbitrary image has been picked in the arbitrary InteractiveViewer,
     *  The given coordinates is position where the plane was picked.
     *  We need to use the
     */
    virtual void pixelPicked(double, double, double) override;

    ///@}

    /// @name Specific helper methods to manage arbitrary orientation and its representation
    ///
    /// @{

    /// Get the current translation relatively to the volume
    /// 0.0 means that the center of the slice is at the first border of the image volume
    /// 1.0 means that the center of the slice is
    ///
    /// \note: the center of the slice is the center of rotation
    /// @see
    virtual double getTranslationInVolume();

    /// Compute the current position of the image center
    /// and the z direction vector in the parent coordinate system (i.e. image frame)
    void getImageCenterAndTranslationVectorInParent(double C_P[4], double Z_P[4]);

    /// get the current position of the image center in the parent coordinate system
    /// i.e the image frame (given as homogeneous point, hence 4D)
    void getImageCenterInParent(double[4]);
    ///@}

private:
    /// check if the center of the frame transformed usin the given matrix stays inside the initial image volume
    bool checkCenter(vtkSmartPointer<vtkMatrix4x4>);

    /// return true only in point is inside the image volume
    bool pointInsideVolume(QVector3D);

    /// update cPlus and cMinus
    void updateTranslationExtremity();

    /// dimension of the whole image (kept here for simplifying code)
    int* dimensions;

    /// spacing of the image (kept here for simplifying code)
    double* spacing;

    /// point of intersection of the line passing at the center of the image
    /// in the z+ direction, i.e. (0,0,1) in the local frame.
    /// This is the extreme possible point to translate C in the z+ direction.
    /// This position is expressed in the parent frame coordinate system (the image coordinate system)
    /// (required to compute getTranslationInVolume and set new translation)
    QVector3D cPlus_P;

    /// point of intersection of the line passing at the center of the image
    /// in the z- direction, i.e. (0,0,-1) in the local frame.
    /// This is the extreme possible point to translate C in the z- direction
    /// This position is expressed in the parent frame coordinate system (the image coordinate system)
    /// This is required to compute getTranslationInVolume and set new translation
    QVector3D cMinus_P;

    /// @name Math utility methods
    /// TODO have this in a specific class?
    ///
    /// @{
    /// variadic method that enables clear expression of the transformation composition chain
    /// Instead of using vtkMatrix4x4::Multiply4x4 multiple times, these methods allow
    /// for expressing all transformation in one line.
    /// For instance:
    /// \code
    /// vtkSmartPointer<vtkMatrix4x4> intermediate1 = vtkSmartPointer<vtkMatrix4x4>::New();
    /// vtkMatrix4x4::Multiply4x4(a, b, intermediate1);
    /// vtkSmartPointer<vtkMatrix4x4> intermediate2 = vtkSmartPointer<vtkMatrix4x4>::New();
    /// vtkMatrix4x4::Multiply4x4(intermediate1, c, intermediate2);
    /// vtkSmartPointer<vtkMatrix4x4> resultingTransformation = vtkSmartPointer<vtkMatrix4x4>::New();
    /// vtkMatrix4x4::Multiply4x4(intermediate2, d, resultingTransformation);
    /// \endcode
    /// You can just write:
    /// \code
    /// vtkSmartPointer<vtkMatrix4x4> resultingTransformation = Multiply4x4(a, b, c, d);
    /// \endcode
    /// which result in a much clearer/cleaner way of expressing the transformation composition
    template<typename T> static vtkSmartPointer<vtkMatrix4x4> Multiply4x4(T, T);
    template<typename T, typename... Args> static vtkSmartPointer<vtkMatrix4x4> Multiply4x4(T a, T b, Args... args);

    /// compute the intersection between a line and a plane
    /// @see http://www.realtimerendering.com/intersections.html
    /// @param lineVector a vector in the direction of the line
    /// @param linePoint a point on the line
    /// @param planeNormal a normal vector to the plane
    /// @param planePoint a point on the plane
    /// @param intersection (output) the point of intersection between the line and the plane
    /// @return false if there is no intersection, true otherwise
    static bool linePlaneIntersectionPoint(QVector3D, QVector3D, QVector3D, QVector3D, QVector3D&);

    /// Round a float to 4 digits
    /// This is a great tool to avoid precision errors (when rotation/cos/sin are around, precision is difficult to maintain)
    static float roundTo4Decimals(float);
    /// Round a QVector3D to 4 digits
    /// This is a great tool to avoid precision errors (when rotation/cos/sin are around, precision is difficult to maintain)
    static QVector3D roundTo4Decimals(QVector3D);
    /// @}
};

}

#endif // ARBITRARYSINGLEIMAGEVOLUMECOMPONENT_H
