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

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

#include "FrameOfReference.h"
#include "Transformation.h"

// -- QT stuff
#include <QHash>
#include <QString>
#include <QVector>
#include <QPalette>
#include <QVariant>

#include <vtkSmartPointer.h>
#include <vtkTransform.h>

#include <memory>
#include <unordered_set>

namespace camitk {

class Transformation;
class FrameOfReference;
class Application;

/**
 * @brief TransformationManager manages frames of reference and transformations for a CamiTK Application
 *
 * This class is the entry point to using FrameOfReference and Transformation system.
 *
 * Every Component that is displayable contains data which are located in space using coordinates.
 * But two Components may not use the same origin in space, or the same axes.
 *
 * To manage that, the notion of Frame specifies an origin and axes, this is modeled by camitk::FrameOfReference.
 * Each component has a FrameOfReference accessed by Component::getFrame()
 *
 * Two Components may share a common Frame, for example two meshes of two organs computed from the same image.
 * In this case both components' getFrame() should return the same FrameOfReference.
 *
 * TransformationManager stores and manages all the FrameOfReference objects used in a CamiTK Application.
 *
 * When you need to display two Components, or to apply an Action that uses multiple Components,
 * it is necessary to be able to transform the coordinates of one Component's data to another.
 *
 * These geometrical transformations are stored in the camitk::Transformation class.
 * A Transformation stores the frame of origin, the frame of destination and the geometrical
 * transformation itself.
 * Currently it supports linear transformations (represented in a 4x4 homogeneous matrices).
 *
 * All Transformation objects are also stored and managed in the TransformationManager.
 * The TransformationManager provides and manages a specific and unique "world frame".
 * This is the frame used by VTK and the default for 3D viewers. Having a world frame
 * simplifies the usage of frames.
 *
 * Alongside Component, Viewers also have a FrameOfReference, which determines the
 * "point of view" they are using for the visualization.
 * More precisely, it is used to set the camera and orient the vtkActors.
 * The default frame of the 2D and 3D viewers is the world frame.

 * Their is only one TransformationManager (all public methods are static).
 *
 * \note on shared and raw pointers
 *
 * Transformation and Frames can be manipulated locally using their raw pointer.
 * Use the shared pointer only if you need to own the object as well, that if the Transformation
 * or Frame is part of your class members and need to be kept alive for your code to work.
 * Using a shared pointer ensures that the Frame or Transformation won't be removed from the system
 * by the TransformationManager.
 * If you only need to access or modify information on a frame or transformation, only use the raw pointer.
 *
 * \section TransformationManager_API
 *
 * The TransformationManager provides the following utilities:
 * - addFrameOfReference(...) methods add FrameOfReference objects
 * - addTransformation(...) methods add Transformation objects
 * - getTransformationOwnership(..) methods returns the shared_ptr to a Transformation
 * - getTransformation(...) method computes a Transformation between two Frames, if any path exists between them
 * - updateTransformation(...) methods modify the transformation matrix values
 *
 * Two methods are provided for the manipulation of world frame:
 * - getWorldFrame() returns the unique world frame, to which all frames should have a path to (composed of 1 or more transformations).
 * - ensurePathToWorld() to add a default identity Transformation between the provided Frame and the worldFrame if no Transformation was defined.
 *
 * \section Transformation
 *
 * A Transformation can either be:
 * - directly defined by the user using addTransformation() -> it holds a user defined 4x4 matrix. It has 0 sources.
 * - an inverse of another Transformation. Inverse Transformation are automatically generated by the
 *   TransformationManager. It therefore has 1 source (the direct Transformation it is the inverse of).
 * - a composite transformation defined automatically by the TransformationManager to pass from one source
 *   to another one over more than one frame. It is composed by a list of other transformations.
 *   It therefore has more than one sources.
 *
 * Information about a Transformation t can be obtained from the TransformationManager using:
 * - hasSources(t) and getSources(t)
 * - isCompositeTransformation(t)
 * - isInverseTransformation(t)
 * - isDefaultIdentityToWorld(t)
 *
 * There is three cases where TransformationManager will automatically create a transformation:
 * - when a linear transformation is added using addTransformation(), the inverse transformation is automatically generated and stored in the system. The new transformation will therefore only have 1 source.
 * - when getTransformation() is called and finds a new path of transformations between the two given frames, it will generate a new composed transformation (for optimization) from those transformations. The new transformation will therefore have all those transformations as sources.
 * - when ensurePathToWorld(f) is called, and no path can be found between f and the world frame, then a new identity transformation from f to world frame is created. This new transformation has no source (note that TransformationManager will also create its inverse, which has 1 source). Use isDefaultIdentityToWorld() to check if a transformation was generated this way.
 *
 * Note that TransformationManager always generates an inverse transformation for any existing linear transformation if it does not exist already.
 *
 * \section Transformation_Sources
 *
 * The lists of Transformation sources are managed by the TransformationManager.
 * Sources are the transformations that are used (composed) to compute a Transformation t.
 * If any of the sources are modified, t is guaranteed to reflect this update, i.e., it is recomputed from its sources.
 * If t has only one source, this means t is the inverse of this source.
 *
 * @see hasSources() getSources()
 *
 * \section FrameOfReference
 * A FrameOfReference represents a specific system of coordinates in which component data's coordinates are expressed.
 *
 * It can have a name, a description, and anatomical labels associated to its axes: for example, the X axis
 * may have label L on the lower values, and R on the higher values (for Left/Right anatomical orientation).
 * You can add new Frames of reference using addFrameOfReference methods when creating a new Component (even though
 * Component constructor creates a default one for you), or if you need to manage multiple frames in your component
 * (e.g. for an articulated robot).
 *
 * If you need to get ownership of a specific FrameOfReference (e.g. you want your Component to store the same Frame
 * as another Component), use getFrameOfReferenceOwnership()
 *
 * To edit anatomical information, name or description, refer to FrameOfReference class.
 *
 *
 * \section Transformation_Path_management
 *
 * When you need a Transformation from one Frame to another, the method to call is getTransformation()
 *
 * This method first looks if a Transformation was added using addTransformation between those Frames,
 * then if there is already a cached composite Transformation linking both Frames,
 * and finally, it checks whether there is a path in the graph of Frames and Transformations
 * linking those Frames using intermediary Frames.
 *
 * Private methods hasPath() and getPath() are used to search the graph for a suitable path.
 * If there is one, a new cached composite Transformation is stored (it combines the path of
 * Transformations into one). If there is no path, these methods return nullptr.
 *
 * When the user wants to ensure that a specific Frame has a Transformation to
 * the WorldFrame, she/he should call ensurePathToWorld(). This will create a default identity
 * Transformation to the WorldFrame if there is no existing path between the Frame and the WorldFrame.
 *
 * All default identity Transformations are marked, so that if a new Transformation is
 * added using addTransformation, these Transformations can be automatically removed. This is needed
 * to avoid creation of multiple path between Frames (there will therefore never be any cycle in
 * the Frame/Transformation graph).
 *
 * \section TransformationMemoryManagement
 *
 * As FrameOfReference and Transformation constructors are private, all Frames and Transformations
 * must be created through the TransformationManager.
 *
 * Internally, Transformation and FrameOfReference objects are stored using std::shared_ptr
 *
 * This means that ownership of these objects is shared between the TransformationManager and custom
 * objects used in CamiTK such as Component (which owns its FrameOfReference), ImageComponent (which also
 * owns its internal Transformation from raw to main).
 *
 * Most methods of this class return or use raw pointers, meaning they do not return or get ownership
 * of the FrameOfReference or Transformation object. The raw pointers are meant to be used for immediate
 * processing (e.g. getting the name of a Frame, transforming the coordinates of a point using a Transformation)
 * but not to store the pointer.
 * If you need to store the object, you must use getFrameOfReferenceOwnership() and getTransformationOwnership()
 * This makes explicit who is owning Transformations and Frames.
 *
 * Note that you may not get ownership of a composite Transformation (computed from others) or
 * a default Transformation, as those must be removable at all times.
 *
 * TransformationManager may delete any Transformation that is not owned outside its internal data structure
 * (which mean they are not used anymore apart from internally).
 * @see cleanupFramesAndTransformation(), removeDefaultPaths(), removeTransformation().
 *
 * To determine whether a Transformation or Frame is owned outside the TransformationManager, std::shared_ptr usage
 * counter is used.
 *
 * \section TransformationManager_Usage "Using TransformationManager in your extensions: TransformationManager use cases"
 *
 * Most common use cases for CamiTK extension developers:
 * - adding a new Component: use the default Component constructor which creates a new default FrameOfReference for the Component
 * - adding a new Component created from another one:
 *   - if the new Component is using the same Frame, just set its Frame to the original component's frame using InterfaceFrame::setFrameFrom()
 *   - if you need to ensure a Component has its own independent frame, use InterfaceFrame::resetFrame()
 *   - Special cases for ImageComponent: ImageComponents have a data frame and a main transformation (from their data to their main frame)
 *     that needs to be taken care of. Be aware that ImageComponent::setFrameFrom() and ImageComponent::resetFrame() are reimplemented
 *     to take care of the data frame and main transformation.
 *     For instance:
 *     - your action creates an image outImage from an image inImage, just call `out->setFrameFrom(in)`
 *     - your action creates an mesh outMesh from an image inImage, call
 *          `outMesh->setFrame(TransformationManager::getFrameOfReferenceOwnership(inImage->getDataFrame()));`
 *       as the mesh is computed from the image data, the mesh is defined in the image component data frame
 *
 * - Registering a Component to another: for example, compute the registration between two meshes, then use addTransformation() between the frames of the two meshes.
 *   @warning: if there is already a Transformation path between the meshes and the world frame is not in this path, addTransformation() will return nullptr. This means that computed transformation (for example after previous registration) already gives a transformation between the two meshes. If you want to update previous registration, you should use updateTransformation() instead. If updateTransformation() also returns nullptr, this means you are trying to create a cycle in the transformation graph.
 * - You can use multiple Frames and Transformation inside your own Component, use the TransformationExplorer to check if it is working as expected.
 *
 * In the case of an articulated robot, each part may have its own FrameOfReference, and each articulation its own Transformation. You can use
 * methods addFrameOfReference, addTransformation, updateTransformation to create and update frames and transformations in your Action and Component extensions.
 *
 */
class CAMITK_API TransformationManager {
public:

    /// get current state as a QString
    static QString toString();

    ///////////////////////////////////////////////////////////////////////
    ///@{ FrameOfReference Management

    /**
     * Get a FrameOfReference from its UUID
     */
    static std::shared_ptr<FrameOfReference> getFrameOfReferenceOwnership(const QUuid& uuid);

    /**
     * Get the shared_ptr that owns the given FrameOfReference.
     * This should only be used to take shared ownership of the given FrameOfReference.
     *
     * @return nullptr if the given FrameOfReference is unknown to the TransformationManager
     */
    static std::shared_ptr<FrameOfReference> getFrameOfReferenceOwnership(const FrameOfReference*);

    /**
     * Add a FrameOfReference with a name and description
     * This is the standard way to create a new FrameOfReference
     *
     * @return the corresponding shared_ptr (save it to keep ownership, ignore if ownership is not needed)
     */
    static std::shared_ptr<FrameOfReference> addFrameOfReference(QString name, QString description = "");

    /**
     * Add a copy of the provided FrameOfReference (with a different UUID)
     *
     * @return the corresponding shared_ptr (save it to keep ownership, ignore if ownership is not needed)
     */
    static std::shared_ptr<FrameOfReference> addFrameOfReference(const FrameOfReference&);

    /**
     * Get a list of all stored FrameOfReference
     */
    static QVector<FrameOfReference*> getFramesOfReference();
    /// @}

    ///////////////////////////////////////////////////////////////////////
    ///@{ Transformation Management

    /**
     * returns true if there a path of transformations between two frames of reference 'from' and 'to'
     * or if `from` equals `to`.
     *
     * This will search for a path in the graph of frames/transformations.
     */
    static bool hasPath(const FrameOfReference* from, const FrameOfReference* to);

    /**
     * Is the transformation a default one ?
     * This means that it was created as Identity by default and might be replaced it another Transformation is set
     * It is usually a Transformation which destination is worldFrame
     */
    static bool isDefaultIdentityToWorld(const Transformation*);

    /**
     * Get a transformation if it exists or compute it if a path exists between the frames.
     */
    static Transformation* getTransformation(const FrameOfReference* from, const FrameOfReference* to);

    /**
     * Get the shared_ptr that owns the given Transformation.
     * This should only be used to take shared ownership of the given Transformation.
     * @warning You cannot take ownership of a Composite or a default identity to world transformation.
     *
     * @warning use getTransformation() if you don't need to take ownership of a Transformation.
     *
     * @return nullptr if the given Transformation is unknown to the TransformationManager or
     * is a composite or is a default identity to world.
     */
    static std::shared_ptr<Transformation> getTransformationOwnership(const Transformation*);

    /**
     * Get the shared_ptr that owns the Transformation with given UUID.
     * This should only be used to take shared ownership of the given Transformation.
     * @warning You cannot take ownership of a Composite or a default identity to world transformation.
     *
     * @warning use getTransformation() if you don't need to take ownership of a Transformation.
     *
     * @return nullptr if the given Transformation is unknown to the TransformationManager or
     * is a composite or is a default identity to world.
     */
    static std::shared_ptr<Transformation> getTransformationOwnership(const QUuid& uuid);

    /**
     * Get the shared_ptr that owns the given Transformation between from and to.
     * This should only be used to take shared ownership of the given Transformation.
     * @warning You cannot take ownership of a Composite or a default identity to world transformation.
     *
     * @warning use getTransformation() if you don't need to take ownership of a Transformation.
     *
     * @return nullptr if the given Transformation is unknown to the TransformationManager or
     * is a composite or is a default identity to world.
     */
    static std::shared_ptr<Transformation> getTransformationOwnership(const FrameOfReference* from, const FrameOfReference* to);

    /**
     * Make sure there is a Transformation from the given Frame to the world Frame.
     * This will create an identity (default) transformation if no path to world already exists.
     */
    static void ensurePathToWorld(const FrameOfReference* frame);

    /**
     * Call this method when you prefer (for visualization purpose only) to have a direct link to world from
     * the given frame instead of any other path. If another path to world exists from the frame and include
     * a default identity transformation to world, it will be delete in favor of a new default identity
     * transformation that directly links the given frame to world.
     * @return false if there is a path to world that contains no default identity transformation
     */
    static bool preferredDefaultIdentityToWorldLink(const FrameOfReference* frame);

    /**
     * @brief Get the WorldFrame
     *
     * This is the Frame that links all Frames so that there is a common space
     * If a Component's frame is not linked by a Transformation to any other Frame,
     * a default identity Transformation should be created between it and this worldFrame
     *
     * This is done by calling ensurePathToWorld
     */
    static const FrameOfReference* getWorldFrame();

    /**
     * Returns the list of direct transformations, that is the transformations that are independent of any other. The inverse transformations (which have one source) and composite transformations (which have 2 or more sources) are not included.
     */
    static QVector<Transformation*> getDirectTransformations();

    /**
     * Returns the list of all transformations managed in the system, independents or not.
     */
    static QVector<Transformation*> getTransformations();

    /// @brief  Create and register a new Transformation from a QVariant (usually from a JSON representation in a .camitk file) if there is no corresponding transformation (uuid and from/to path)
    /// @return a pointer to the new Transformation or nullptr if a transformation with the same UUid or the same from/to frames already exist in the system (use getTransformationOwnership() or getTransformation(..) instead)
    static std::shared_ptr<Transformation> addTransformation(const QVariant&);

    /// @brief  Create and register a new identity Transformation between two frames if
    /// there is no existing transformation between those frames.
    /// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist (use getTransformation())
    static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to);

    /// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*)
    static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to);

    /// @brief  Create and register a new Transformation between two frames and sets the transformation to the provided transform if
    /// there is no existing transformation between those frames.
    /// @param vtkTr The vtkTransform pointer that will be stored in the Transformation.
    /// @warning if the given vtkTransform is updated later, it will affect the Transformation!
    /// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist (use getTransformation())
    static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkSmartPointer<vtkTransform> vtkTr);

    /// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*,vtkSmartPointer<vtkTransform>)
    static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to, vtkSmartPointer<vtkTransform> vtkTr);

    /// @brief  Create and register a new Transformation between two frames, copying the content of the provided matrix  if there is no existing transformation between those frames.
    /// A deep copy of the given matrix is used to initialize the new Transformation
    /// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist
    static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to, const vtkMatrix4x4* matrix);

    /// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*,const vtkMatrix4x4*)
    static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to, const vtkMatrix4x4* matrix);


    /**
     * Remove transformations and frames that are unused (i.e. only present in the TransformationManager)
    */
    static void cleanupFramesAndTransformations();

    /** Remove an existing transformation between the two frames.
     *
     * This method checks that the given shared_ptr<Transformation> has no other owner than the caller
     * (i.e., only the caller of this method has ownership of the given shared_ptr). If this is true,
     * then the shared_ptr will be set to nullptr, which will result in the deletion of the Transformation.
     *
     * If there is another owner, the shared_ptr is not modified, and the Transformation will not be removed.
     *
     * If the given shared_ptr is equal to nullptr (for instance when calling removeTransformation(getTransformationOwnership(t))
     * with t being a composite transformation), this method returns false.
     *
     * @warning if successful, the shared_ptr must not be used anymore (as it is set to nullptr).
     *
     * @return true if the Transformation was removed from the transformation system, false otherwise.
     */
    static bool removeTransformation(std::shared_ptr<Transformation>& tr);

    /** Remove an existing transformation between the two frames.
     * @see removeTransformation(std::shared_ptr<Transformation>&)
     *
     * @warning if successful, the shared_ptr must not be used anymore (as it is set to nullptr) after that.
     */
    static bool removeTransformation(const FrameOfReference* from, const FrameOfReference* to);

    /// @name updateTransformation
    /// Updating a Transformation work only for Transformations created by addTransformation()
    /// It will fail if the transformation was computed from others (e.g. inverse of another Transformation, or a composition)
    /// If there is already path, but some Transformation are default identity Transformation, you should be using addTransformation.
    /// If there is already a valid path using non-default Transformations you must break the path before using addTransformation.
    /// @return nullptr if the transformation was not updated

    ///@{

    /// Modify the Transformation between the two frames by setting its vtkTransform
    /// @warning only the vtkMatrix of the given vtkTransform is duplicated (later modification of vtkTr will not update the transformations)
    static bool updateTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkSmartPointer<vtkTransform> vtkTr);

    /// Modify the Transformation between the two frames by setting its vtkMatrix
    static bool updateTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkMatrix4x4* matrix);

    /// Modify the Transformation by setting its vtkTransform
    /// @warning only the vtkMatrix of the given vtkTransform is duplicated (later modification of vtkTr will not update the transformations)
    static bool updateTransformation(Transformation* tr, vtkSmartPointer<vtkTransform> vtkTr);

    /// Modify the Transformation by setting its matrix
    static bool updateTransformation(Transformation* tr, vtkMatrix4x4* matrix);

    ///@}

    /**
     * Was this Transformation computed from others or not?
    */
    static bool hasSources(const Transformation*);

    /**
     * Get the list of sources used to compute the provided Transformation
    */
    static QVector<Transformation*> getSources(const Transformation*);

    /// @return true if the given Transformation is an inverse, i.e., has one and only one source
    static bool isInverseTransformation(const Transformation*);

    /// @return the inverse transformation (creates it if it does not exists yet, that is if tr is a composite) or nullptr if tr is nullptr
    static Transformation* getInverseTransformation(const Transformation* tr);

    /**
     * Is this transformation composed of two or more transformations ?
     */
    static bool isCompositeTransformation(const Transformation* tr);

    /// @}

    /**
     * Save Frame and Transformation data to a QVariant
     *
     * This creates a QVariantMap with the keys "frames" and "transformations"
     * for saving the state of the TransformationManager
     * @see InterfacePersistence
    */
    static QVariant toVariant();

    /**
     * Load Frame and Transformation data from a QVariant
     *
     * This reads "frames" and "transformations" from the provided QVariantMap
     * to add Frame and Transformation objects in the TransformationManager
     * @see InterfacePersistence
    */
    static void fromVariant(const QVariant&);

private:

    /// @name Transformations and Frames of reference management
    ///@{

    /// worldFrame : frame in which all actors and props are defined (maybe through their userTransform)
    static std::shared_ptr<FrameOfReference> worldFrame;

    /// Storing the frames
    static std::vector<std::shared_ptr<FrameOfReference>> frames;

    /// Storing the transformations
    static std::vector<std::shared_ptr<Transformation>> transformations;

    /// Cache to get FrameOfReference pointer from Uuid
    static QHash<QUuid, FrameOfReference*> frameMap;

    /// Cache to get Transformation pointer from Uuid
    static QHash<QUuid, Transformation*> transformationMap;

    /// Storing the sources of a transformation:
    /// - [] means it is a direct transformations (created using addTransformation())
    /// - [Transf1] means it was computed from on other transformation (it's an inverse)
    /// - [Transf1, ..., Transfn] means that it was computed by combining n transformations.
    static QHash<const Transformation*, QVector<std::shared_ptr<Transformation>>> transformationSources;

    /// Finding a transform from Frame1 to Frame2 : transformationsFromTo[Frame1][Frame2]
    static QHash<const FrameOfReference*, QHash<const FrameOfReference*, Transformation*>> transformationFromTo;

    /// List of default Identity Transformations : these can be removed if another transform is added later and completes the desired paths
    ///\example
    /// Two components C1 and C2 are loaded, create Frame1 and Frame2.
    /// C1 is added to a 3D Viewer, which has the FrameOfReference WorldFrame
    /// A default identity transformation is added to link Frame1 to WorldFrame,
    /// because we need to set Actor1 in the viewer's frame, and there is no known Transformation
    /// from Frame1 to WorldFrame.
    /// We also add C2 to the 3D Viewer.
    /// The problem is identical, a default identity transformation is added from Frame2 to WorldFrame.
    /// Now C1 and C2 are registered, and a new Transformation Tr2_1(Frame2, Frame1) must be added.
    /// But there is already a path Frame1 --> WorldFrame <-- Frame2 linking both Frames.
    /// Because the current path contains default identity transformations, these can be deleted, so
    /// that the new Frame2 --> Frame1 transformation can be added.
    /// When the Viewer updates its content, it will try to find Frame1--> WorldFrame, which was removed, and
    /// will create a default identity transformation again.
    /// When trying to show C2, it will find the Transformation Frame2 --> Frame1 --> WorldFrame
    static std::unordered_set<const Transformation*> defaultIdentityToWorldTransformations;

    /**
     * Private method to add a Transformation in the members.
     *
     * @warning: TransformationManager takes ownership of the provided pointer, therefore YOU MUST NOT DELETE IT
     *
     * @param tr the transformation to add
     * @param sources List of Transformations that were used to compute this one. This list is empty if this method is called inside addTransformation().
    */
    static std::shared_ptr<Transformation> registerTransformation(Transformation* tr, const QVector<std::shared_ptr<Transformation>>& sources = {});

    /**
     * If there is a path of transformations between two frames of reference, this will return it.
     *
     * This will search for a path in the graph of frames/transformations.
     *
     * \note if from or to are nullptr, returns an empty QVector
     *
     * @return The list of Transformations that once chained link Frame 'from' to Frame 'to' or an empty QVector if there is no path or if `from` equals `to`.
     */
    static QVector<std::shared_ptr<Transformation>> getPath(const FrameOfReference* from, const FrameOfReference* to);


    /**
     * Create and register a Transformation which is the composition of the provided transformations
    */
    static std::shared_ptr<Transformation> addCompositeTransformation(QVector<std::shared_ptr<Transformation>> transforms);

    /**
     * Add a FrameOfReference with all data
     * @warning should be used only if you need a specific UUID (e.g. when reading a FrameOfReference from a file)
     */
    static std::shared_ptr<FrameOfReference> addFrameOfReference(QUuid uuid, QString name, QString description, int dimensions = 3, const AnatomicalOrientation& ao = {}, std::vector<Unit> units = {"", "", "", "", ""});

    /**
     * Add a FrameOfReference from a QVariant (as stored using PersistenceManager)
     */
    static std::shared_ptr<FrameOfReference> addFrameOfReference(const QVariant&);

    ///@}

    /// Remove the default identity to world Transformations that are present in the path between from and to
    /// @return true if and only if at least one default transformation was removed between from and to
    static bool removeDefaultPaths(const FrameOfReference* from, const FrameOfReference* to);

    /// Helper method that removes a transformation from all internal members except the owner (transformations)
    /// @warning this is a utility methods, should only be called from cleanUp and removeTransformation methods.
    static void removeTransformationFromInternalStructures(const Transformation* trToDelete);

    /// Remove all composite from internal structures
    static void cleanupCompositeTransformations();

    /**
     * Get the shared_ptr that owns the given Transformation (works for all transformation that are not composite)
     * @warning This should never be a public method (see difference with getTransformationOwnership)
     */
    static std::shared_ptr<Transformation> getTransformationSharedPtr(const Transformation*);
};
}
#endif // TRANSFORMATIONMANAGER_H