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

#include <QString>
template<class T> class QList;
class QVariant;
class QObject;
class QUuid;
class QDir;

namespace camitk {

class Component;
class CamiTKFile;

class PersistenceManager {

public:

    /**
     * Save the whole Application workspace including components,
     * settings (e.g. viewers configuration, rendering options...),
     * frames and transformations, action settings
    */
    static bool saveWorkspace(QString filepath);

    /**
     * Loads the whole Application workspace from a CamiTK file
     *
     * This includes components, geometry (frames, transformations),
     * window settings...
    */
    static bool loadWorkspace(QString filepath);

    /**
     * @brief Update the variant value while trying to preserve its type
     *
     *
     * This function updates the value of the given variant using the value
     * of the newValue QVariant while preserving the QVariant type defined in
     * variant (when possible).
     * Note that name is informational only abd used for debug reason to print
     * a more comprehensible message when preserving the type of variant is
     * not possible.
     *
     * Example:
     * myVariant = QVariant<QUrl>("http://example.com");
     * newVariantValue = QVariant<QString>("http://newerexample.com");
     * updatePreservingType(myVariant, newVariantValue, "my url");
     * oldVariant is now a QUrl (not a QString) but with the new content "http://newerexample.com"
     *
     * This is useful as saving to JSON might lose the type
     * e.g. QUrl -> QString, QUuid -> QString
     *
     * To properly restore variants (e.g. Property or Settings), without modifying
     * the QVariant types, the type must be infered from the existing variant.
     *
     * WARNING: if the given variant does not have the same structure as the newValue,
     * data may not be copied. e.g. copying a QList<QString> into a QString will result
     * in an empty QString.
     *
    */
    static void updateVariantValueWhilePreservingType(QVariant& variant, QVariant& newValue, QString name = "");

    /**
     * Convert the properties of a QObject to a QVariant
     *
     * This function is used to easily implement PersistenceInterface for a
     * custom class that derives from QObject and uses its property system.
     * It reads the object's properties and returns them in a QVariant
    */
    static QVariant fromProperties(const QObject*);

    /**
     * Sets the properties of a QObject from a QVariant
     *
     * This function is used to easily implement PersistenceInterface for a
     * custom class that derives from QObject and uses its property system.
     * It sets the properties of the object from the content of a QVariant.
    */
    static void loadProperties(QObject*, QVariant);

    /**
     * Gets the UUID of a QObject
     *
     * This function is used to easily implement PersistenceInterface for a
     * custom class that derives from QObject and uses its property system
     * to store its unique identifier (QUuid).
     * If the object does not have the "uuid" property, this returns an invalid QUuid
    */
    static QUuid getUuidFromProperties(const QObject*);

    /**
     * Sets the UUID of a QObject
     *
     * This function is used to easily implement PersistenceInterface for a
     * custom class that derives from QObject and uses its property system
     * to store its unique identifier (QUuid).
     * The UUID will be stored in the "uuid" property.
     * @warning The uuid can only be set once to a non-null value
     * @return This returns true if the value was set, false if it was not
    */
    static bool setUuidInProperties(QObject*, QUuid);

protected:

    /**
     * Converts component's filename and properties to QVariant
     *
     * \note
     * The filename is converted to a relative path using the rootPath parameter
     *
     * @param rootPath Filenames of the components will be stored relative to this path (if rootPath is /tmp, filename /tmp/test/t.obj will be saved as test/t.obj)
    */
    static QVariant fromComponents(QList<Component*>, QDir rootPath);

    /// @brief Loads/Open the components from the QVariant and update the property values accordingly
    /// @param rootPath the root path to decipher the filename (which should be relative)
    /// @return false if and only if the QVariant is not a list of components.
    static bool loadComponents(QVariant, QDir rootPath);

    /**
     * @brief returns a specific string representation of (limited list of) some specific QVariant types
     *
     * The value of QRect and QByteArray values cannot be recovered directly from
     * a QString if some specific conversion are not taken care of.
     * For instance, as JSON has 6 predefined data types many QVariant types will
     * be stored as QString or cannot be handle specifically during serialization
     * and deserialization.
     *
     * This methods reproduces the same principle as QSettings in order to
     * use specific strings for specific QVariant type.
     *
     * See QSettings::variantToString method
     * https://codebrowser.dev/qt5/qtbase/src/corelib/io/qsettings.cpp.html#395
     *
     * As for now only the following types are supported
     * - QRect
     * - QByteArray
     */
    static QString variantToString(const QVariant& variant);

    /**
     * @brief returns a valid QVariant for specific types corresponding to a specific string representation
     *
     * This method use a specific string representation as produced by the variantToString(..) method
     * to build a QVariant of the corresponding specific user type if possible.
     *
     * The supported types are the same the one defined in the variantToString(..).
     * See QSettings::stringToVariant
     * https://codebrowser.dev/qt5/qtbase/src/corelib/io/qsettings.cpp.html#477
     *
     * @see variantToString(QVariant &variant)
     * @return If the parameter does not represent a supported QVariant (e.g. a normal QString), the method returns an invalid QVariant
     */
    static QVariant stringToVariant(QString value);
};

} // namespace

#endif //PERSISTENCE_MANAGER_H

