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

#include <QString>
#include <QVector>

// -- std stuff
#include <vector>

// -- vtk stuff
#include <vtkType.h>
#include <vtkSmartPointer.h>

// -- vtk stuff classes
class vtkAxesActor;
class vtkTransform;

namespace camitk {

class Viewer;

/**
 * @ingroup group_sdk_libraries_core_component
 *
 * @brief
 * This class describes what are the methods to implement in order to manage a Component position in space.
 *
 * The Frame hierarchy is inspired from IGSTK hierarchy:
 *  - Each Frame knows its parent frame
 *  - Each Frame knows the transormation to its parent frame
 *
 * For hierachy display convenience, each Frame also knows its children frames.
 *
 *
 * Ecah Component has a Frame, and each Frame belongs to only one Component.
 * However, the Frame hierarchy may be different from the Component hierarchy (InterfaceNode).
 *
 */
class InterfaceFrame {

public:
    /// empty virtual destructor, to avoid memory leak
    virtual ~InterfaceFrame() = default;

    /**
     * Hierarchy accessors / Modifyers
     * @{
     */
    /// Get the Frame Unique identifyer (can be set by user)
    virtual const QString& getFrameName() const = 0;

    /// Set the Frame Unique identifyer
    virtual void setFrameName(QString name) = 0;

    /// Get the parent frame
    virtual InterfaceFrame* getParentFrame() const = 0;

    /**
     * Set the parent frame and update or not its transform during the parent transition
     * @param parent the new parent frame of current interface frame
     * @param keepTransform if true keeps the local transform from parent. The current interface frame may move from world coordinates as its local transform is now expressed relatively to the new parent.  If false, change the transform from parent to keep the global (world) transform the same as the previous one.
     */
    virtual void setParentFrame(InterfaceFrame* parent, bool keepTransform = true) = 0;

    /**
     *   Get the Children Frames from the current Frame in the Frame Hierarchy
     *   The Frame hierarchy may not be the same as the Component Hierarchy.
     */
    virtual const QVector<InterfaceFrame*>& getChildrenFrame() const = 0;

    /** @} */

    /**
     * Transforms accessors / Modifyers
     * @{
     */
    /// Get a the transformation with respect to the world frame
    virtual const vtkSmartPointer<vtkTransform> getTransformFromWorld() const = 0;

    /// Get the transformation with respect to the parent frame
    virtual const vtkSmartPointer<vtkTransform> getTransform() const = 0;

    /// Compute the transformation from any other frame to the current frame.
    /// @note A new returned transformed is instancied at each call.
    /// Caller takes ownership of the transform and is responsible for its deletion
    virtual const vtkSmartPointer<vtkTransform> getTransformFromFrame(InterfaceFrame* frame) const = 0;

    /**
     * Set the current input frame position (according to its parent Frame)
     * @param transform The 3D transform of the current frame to its parent.
     */
    virtual void setTransform(vtkSmartPointer<vtkTransform> transform) = 0;

    /**
     * Set the current frame transform to identity.
     * In other words, the current frame and its parent share the same 3D location.
     */
    virtual void resetTransform() = 0;


    /**
     * Apply a translation relative to the current position
     */
    virtual void translate(double x, double y, double z) = 0;

    /**
     * Apply rotations relative to the current position in the alphabetical order (X, Y, Z).
     * @note Prefer using the rotateVTK method if possible, this one involves errors when retrieving rotation angles
     * from a rotation matrix, as we use a VTK method for this.
     */
    virtual void rotate(double aroundX, double aroundY, double aroundZ) = 0;

    /**
     * Apply a rotation relative to the current position, using the VTK rotation order (Z, X, Y)
     * @note Prefer using this method if you can.
     */
    virtual void rotateVTK(double aroundX, double aroundY, double aroundZ) = 0;

    /**
     * Set the translation part of the 3D space transformation of the current frame.
     * @note Reminder: A 3D space transform is a 4x4 matrix, composed of a rotation and a translation.
     * @note This method differs from setTransformTranslationVTK on the rotation order in the transform matrix
     * Here order is alphabetical X, Y, Z, which involves problems when retrieving angles from the rotation matrix
     * as this is done using VTK.
     * If you can, prefer using setTransformTranslationVTK.
     *
     */
    virtual void setTransformTranslation(double x, double y, double z) = 0;

    /**
     * Set the translation part of the 3D space transformation of the current frame.
     * This method uses the VTK rotation order (Z, X, Y).
     * @note Reminder: A 3D space transform is a 4x4 matrix, composed of a rotation and a translation.
     * @note This method differs from setTransformTranslation on the rotation order in the transform matrix
     * Here order is the one used by VTK: Z, X, Y which guarantees that you can retrieve the correct angles from a rotation matrix.
     * Prefer using this method than setTransformTranslation.
     */
    virtual void setTransformTranslationVTK(double x, double y, double z) = 0;

    /**
     * Set the rotation part of the 3D space transformation of the current frame.
     * @note Reminder: A 3D space transform is a 4x4 matrix, composed of a rotation and a translation.
     * @note This method differs from setTransformRotationVTK on the rotation order in the transform matrix
     * Here order is alphabetical X, Y, Z, which involves problems when retrieving angles from the rotation matrix
     * as this is done using VTK.
     * If you can, prefer using setTransformRotationVTK.
     */
    virtual void setTransformRotation(double aroundX, double aroundY, double aroundZ) = 0;

    /**
     * Set the rotation part of the 3D space transformation of the current frame.
     * @note Reminder: A 3D space transform is a 4x4 matrix, composed of a rotation and a translation.
     * @note This method differs from setTransformRotation on the rotation order in the transform matrix
     * Here order is the one used by VTK: Z, X, Y which guarantees that you can retrieve the correct angles from a rotation matrix.
     * Prefer using this method than setTransformRotation.
     *
     */
    virtual void setTransformRotationVTK(double aroundX, double aroundY, double aroundZ) = 0;

    /** @} */

    /**
     * Gives the 3D representation of the frame (based on xyz arrows)
     */
    virtual vtkSmartPointer<vtkAxesActor> getFrameAxisActor() = 0;

    ///@cond
    /**
     * TODO CAMITK_DEPRECATED. This section list all the methods marked as deprecated. They are to be removed in CamiTK 6.0
     * @deprecated
     *
     * DEPRECATED (CamiTK 6.0) -> to be removed
     * Use the setFrameVisibility(QString, bool) / getFrameVisibility(QString, bool) methods
     */
    /**
     * Set the Component Frame visible for a given viewer
     */
    CAMITK_API_DEPRECATED("Please use InterfaceFrame::setFrameVisibility(QString viewerName, bool) instead") virtual void setFrameVisibility(Viewer*, bool) = 0;

    /**
     * Get the Component Frame visibility for a given viewer
     */
    CAMITK_API_DEPRECATED("Please use InterfaceFrame::getFrameVisibility(QString viewerName, bool) instead") virtual bool getFrameVisibility(Viewer*) const = 0;
    ///@endcond

    /**
     * Set the Component Frame visible for a given viewer
     */
    virtual void setFrameVisibility(QString, bool) = 0;

    /**
     * Get the Component Frame visibility for a given viewer
     */
    virtual bool getFrameVisibility(QString) const = 0;

    /**
     * Add the input frame as a child of the current frame.
     * @param frame The child frame of the current one.
     */
    virtual void addFrameChild(InterfaceFrame* frame) = 0;

    /**
     * Remove the input frame as a child of the current frame.
     * @param frame the input frame to remove from the current frame's children list
     */
    virtual void removeFrameChild(InterfaceFrame* frame) = 0;

};

}

#endif // INTERFACEFRAME_H
