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

#include <QObject>
#include <QProcess>
#include <QDir>

#include "CamiTKExtensionGeneratorAPI.h"

/**
 * @brief Manage configuration check, CMake configure and build stages from C++ of the given CamiTK file
 *
 * The following stages can be performed by calling related applications using QProcess:
 * - Check_System: check that cmake, camitk-extensiongenerator and camitk-config are found and usable
 * - Generate_Source_Files: calls camitk-extensiongenerator (first phase with destination dir equals to the CamiTK file path)
 * - Configure_CMake: run cmake for configuration of the generated source files and creates the build directory
 * - Build_Project: run "cmake --build build"
 * - Run_CamiTK_Config: run "camitk-config --config", output can be check for new Standard extension
 * - Check_Integration: (for Standard extension) check what is build action extensions defined in the CamiTK file were build and properly loaded by camitk-config
 * - Cleanup: remove temporary directory used during the previous stages
 *
 * Set the list of stages you want to perform using setStages (default is do them all) and
 * call start(). Each stage will be performed up to the last stage or up to the first one that fails.
 *
 * Connect your methods to three given signals to follow the status of each stage.
 *
 * \example
```cpp
CMakeProjectManager manager(camitkFileFullPath);
manager.setStages({CMakeProjectManager::Check_System, CMakeProjectManager::Generate_Source_Files, CMakeProjectManager::Configure_CMake, CMakeProjectManager::Build_Project});

// connect signals to follow progress
connect(&manager, &CMakeProjectManager::stageStarted, this, [ = ](const QString & stage) {
    std::cout << "Starting stage \"" << stage << "\"..." << std::endl;
});

connect(&manager, &CMakeProjectManager::stageFinished, this, [ = ](const QString & stage, bool success, const QString & output) {
    std::cout << "Stage \"" << stage << "\" finished." << std::endl;
    std::cout  << "Status: " << ((success) ? "[OK]" : "[FAILED]") << std::endl;
    std::cout  << "Output:\n" << output << std::endl;
});

connect(&manager, &CMakeProjectManager::allStagesFinished, this, [ = ](bool status) {
    std::cout << "Stage \"" << stage << "\" finished." << std::endl;
    std::cout  << "Status: " << ((success) ? "[OK]" : "[FAILED]") << std::endl;
});

// starts the stages
manager.start();
```
 *
 */
class CAMITKEXTENSIONGENERATOR_EXPORT CMakeProjectManager : public QObject {

    Q_OBJECT

public:
    /// Known stages that can be set in the stage configuration
    enum CMakeProjectManagerStage {
        Check_System,          ///< check that cmake, camitk-extensiongenerator and camitk-config are found and usable
        Generate_Source_Files, ///< calls camitk-extensiongenerator (fist phase with destination dir equals to the CamiTK file path)
        Configure_CMake,       ///< cmake configure
        Build_Project,         ///< cmake build
        Run_CamiTK_Config,     ///< check camitk-config --config (output can be check for new Standard extension)
        Check_Integration,     ///< (for Standard extension) check what is build action extensions defined in the CamiTK file were build and properly loaded by camitk-config
        Cleanup                ///< remove temporary directory used during the previous stages
    };
    Q_ENUM(CMakeProjectManagerStage)  // This macro makes the enum available to the meta-object system

    /// @brief constructor
    /// @param camitkFilePath the full path of the camitk file (with the .camitk extension)
    /// @param parent (optional)
    CMakeProjectManager(const QString& camitkFilePath, QObject* parent = nullptr);
    ~CMakeProjectManager();

    /// @brief you can override default stages by sending an non empty list of
    /// stages to perform
    void setStages(QList<CMakeProjectManagerStage> stagesToPerform);

    /// starts all the setup stages
    void start();

    /// current value of the process
    bool success() const;

    /// current stage
    QString getCurrentStage() const;

    /// get all the stages that were setup
    QStringList getStages() const;

    /// utility method to get the QString equivalent of the given stage (underscores are replaced by space)
    QString getStageName(CMakeProjectManagerStage value) const;

signals:
    /// sent when the given stage is starting
    void stageStarted(const QString& stage);

    /// sent when the given stage is finished (with the given status and output)
    /// \note the output concatenates standard output and standard error of the subprocess
    /// in a QString of value: "Standard Output:\n%1\nStandard Error:%2\n", where %1 is
    /// the standard output and %2 is the standard error
    void stageFinished(const QString& stage, bool success, const QString& output);

    /// sent when all the stage are finished (with the given overall status)
    void allStagesFinished(bool status);

private slots:
    /// private slot connected to the executed subprocess
    void processFinished(int exitCode, QProcess::ExitStatus exitStatus);

private:
    /// The managed CamiTK file
    QString camitkFilePath;

    /// The current subprocess
    QProcess* currentProcess;

    /// overall status: true if everything is ok so far
    bool status;

    /// path to the CamiTK file directory
    QDir sourceDir;

    /// path to the build dir configured by CMake
    QDir buildDir;

    /// path the CamiTK bin directory detected during the check stage
    QDir camitkBinDir;

    /// On Windows: true if the installed/detected CamiTK is a debug version
    bool isDebug;

    /// list of stages
    QList<CMakeProjectManagerStage> stages;

    /// current stage index
    int currentStageIndex;

    /// determine which is the next stage to execure in the list and starts it
    void executeNextStage();

    /// @name stage source code (see description above)
    /// @{
    void checkSystem();
    void generateSourceFiles();
    void configureCMake();
    void buildProject();
    void runCamiTKConfig();
    void checkIntegration();
    void cleanup();
    /// @}


};

#endif // __CMAKE_PROJECT_MANAGER__
