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

#include <QTest>
#include <QDir>
#include <QTemporaryFile>

#include "Application.h"
#include "ExtensionManager.h"
#include "Action.h"
#include "TransformationManager.h"
#include "Transformation.h"
#include "FrameOfReference.h"
#include "Component.h"

/**
 * Create a basic MeshComponent for tests
 */
camitk::Component* createNamedSphere(float radius = 1.0, QString name = "No name");

namespace camitk {

class TestPersistence: public QObject {
    Q_OBJECT


private slots:

    // Before all tests
    void initTestCase() {
    }

    // Run after testing (failed or not)
    void cleanupTestCase() {

    }


    void saveLoadEmptyWorkspace() {
        // Change some settings

        // Save the empty workspace

        // Change the settings

        // Load the workspace

        // Check if the settings are back to their previous values
    }

    void saveLoadWorkspaceComponents() { // Checks that components are reloaded, that the frames and transformations are identical
        // Load extensions to be able to read and save msh files
        ExtensionManager::autoload();
        // Create two components
        Component* component1 = createNamedSphere(1.0, "SphereMeshComponent");
        Component* component2 = createNamedSphere(2.0, "SphereMeshComponent2");

        // Add a Transformation between their frames
        std::shared_ptr<Transformation> tr = TransformationManager::addTransformation(component1->getFrame(), component2->getFrame());
        QVERIFY(tr != nullptr);
        QUuid transformationId = tr->getUuid();

        // Save the components
        QTemporaryFile file1(QDir::tempPath() + QDir::separator() + "Component-XXXXXX.vtk");
        QTemporaryFile file2(QDir::tempPath() + QDir::separator() + "Component-XXXXXX.vtk");
        QTemporaryFile fileWorkspace(QDir::tempPath() + QDir::separator() + "Workspace-XXXXXX.camitk");
        QVERIFY(file1.open());
        QVERIFY(file2.open());
        QVERIFY(fileWorkspace.open());
        component1->setFileName(file1.fileName());
        component2->setFileName(file2.fileName());
        Application::save(component1);
        Application::save(component2);

        // Save the workspace
        Application::saveWorkspace(fileWorkspace.fileName());

        // Keep some data for later checks
        QUuid frameId1 = component1->getFrame()->getUuid();
        QUuid frameId2 = component2->getFrame()->getUuid();

        // Cleanup unused frames and transformations
        TransformationManager::cleanupFramesAndTransformations();

        // Count frames and transformations before closing components
        int nbTrBefore = TransformationManager::getTransformations().size();
        int nbFrBefore = TransformationManager::getFramesOfReference().size();

        // Reset our shared_ptr
        tr = nullptr;

        // Close the components
        Application::close(component1);
        Application::close(component2);

        // Check that frames and transformations were cleaned up (two frames removed, one transformation and its inverse removed)
        QCOMPARE(TransformationManager::getFramesOfReference().size(), nbFrBefore - 2);
        QCOMPARE(TransformationManager::getTransformations().size(), nbTrBefore - 2);

        // Load the workspace
        Application::loadWorkspace(fileWorkspace.fileName());

        // Check that frames and transformation were added again
        QCOMPARE(nbFrBefore, TransformationManager::getFramesOfReference().size());
        QCOMPARE(nbTrBefore, TransformationManager::getTransformations().size());

        // Check that the components are there and that their frames and transformations have the same UUIDs
        component1 = nullptr;
        component2 = nullptr;
        for (Component* comp : Application::getAllComponents()) {
            if (comp->getName() == "SphereMeshComponent") {
                component1 = comp;
            }
            else {
                if (comp->getName() == "SphereMeshComponent2") {
                    component2 = comp;
                }
            }
        }
        QVERIFY(component1 != nullptr);
        QVERIFY(component2 != nullptr);
        QVERIFY(component1->getFrame()->getUuid() == frameId1);
        QVERIFY(component2->getFrame()->getUuid() == frameId2);
        QVERIFY(TransformationManager::getTransformation(component1->getFrame(), component2->getFrame())->getUuid() == transformationId);

        // Update data to be checked later
        nbTrBefore = TransformationManager::getTransformations().size();
        nbFrBefore = TransformationManager::getFramesOfReference().size();

        // Close all components
        Application::getAction("Close All")->apply();

        // Clean up the transformationManager (remove all frames and Transformations)
        // The Transformation should be detected as useless so the frames should not be in use either
        TransformationManager::removeTransformation(tr);
        TransformationManager::cleanupFramesAndTransformations();

        QVERIFY(TransformationManager::getFrameOfReferenceOwnership(frameId1) == nullptr);
        QVERIFY(TransformationManager::getFrameOfReferenceOwnership(frameId2) == nullptr);
        QVERIFY(TransformationManager::getTransformationOwnership(transformationId) == nullptr);

        QCOMPARE(nbFrBefore - 2, TransformationManager::getFramesOfReference().size());
        QCOMPARE(nbTrBefore - 2, TransformationManager::getTransformations().size());

        // Load the workspace again
        Application::loadWorkspace(fileWorkspace.fileName());

        // Check that the right number of frames and transformations were loaded
        QCOMPARE(nbFrBefore, TransformationManager::getFramesOfReference().size());
        QCOMPARE(nbTrBefore, TransformationManager::getTransformations().size());

        // Check they have the same UUIDs as before
        QVERIFY(TransformationManager::getFrameOfReferenceOwnership(frameId1) != nullptr);
        QVERIFY(TransformationManager::getFrameOfReferenceOwnership(frameId2) != nullptr);
        QVERIFY(TransformationManager::getTransformationOwnership(transformationId) != nullptr);
    }

};

} // namespace camitk

#endif // CAMITK_TESTPERSISTENCE_H
