/*****************************************************************************
 * $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$
 ****************************************************************************/
#include "ViewerGenerator.h"

// includes from cepcoreschema
#include <Viewer.hxx>

// includes from STL
#include <iostream>
#include <memory>

// includes from Qt
#include <QFileInfo>
#include <QDir>
#include <QTextStream>
#include <QSet>
#include <QRegularExpression>

// Local includes
#include "ClassNameHandler.h"

using namespace cepcoreschema;

ViewerGenerator::ViewerGenerator(QString xmlFileName, QString licence) {
    this->licence = licence;
    QFileInfo xmlFile(xmlFileName);

    if ((! xmlFile.exists()) || (! xmlFile.isFile())) {
        throw std::invalid_argument("I/O exception during viewer file generation:\nFile " + xmlFileName.toStdString() + " does not exist or is not a file...\n");
    }

    try {
        std::string xmlFileStr = xmlFileName.toStdString();
        std::unique_ptr<Viewer> domViewer = viewer(xmlFileStr, xml_schema::flags::dont_validate);
        createFromDom(*domViewer);
    }
    catch (...) {
        throw std::invalid_argument("I/O exception during viewer file generation:\nFile " + xmlFileName.toStdString() + " is not valid...\n");
    }
}

ViewerGenerator::ViewerGenerator(Viewer& domViewer, QString licence) {
    this->licence = licence;
    createFromDom(domViewer);
}

QString ViewerGenerator::getClassName() const {
    return className;
}

// -------------------- createFromDom --------------------
void ViewerGenerator::createFromDom(cepcoreschema::Viewer& dom) {
    name          = QString(dom.name().c_str());
    className     = ClassNameHandler::getClassName(name);
    description   = QString(dom.description().c_str()).simplified();
    type          = QString(dom.type().c_str());
    std::cout << "Generating Viewer \"" << className.toStdString() << "\"..." << std::endl;

    // components
    for (auto it = dom.component().begin(); it != dom.component().end(); it++) {
        components << QString((*it).c_str());
    }
}

void ViewerGenerator::generateFiles(QString directoryName) {
    writeHFile(directoryName);
    writeCFile(directoryName);
}

void ViewerGenerator::writeHFile(QString directoryName) {

    // Setting the right input and output files
    QFile initFile(":/resources/Viewer.h.in");
    initFile.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream in(&initFile);

    QFileInfo extFilePath;
    extFilePath.setFile(directoryName, className + ".h");
    QFile extFile(extFilePath.absoluteFilePath());

    if (! extFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QString msg = "Exception from extension generation: \n    Cannot write on file " + extFilePath.fileName() + "\n";
        throw (msg);
    }

    QTextStream out(&extFile);
    QString text;

    do {
        text = in.readLine();
        text.replace(QRegularExpression("@LICENCE@"), licence);
        text.replace(QRegularExpression("@HEADDEF@"), className.toUpper());
        text.replace(QRegularExpression("@VIEWERCLASSNAME@"), className);

        out << text << Qt::endl;
    }
    while (! text.isNull());

    extFile.close();
    initFile.close();
}


void ViewerGenerator::writeCFile(QString directoryName) {
    // Opening init file
    QFile initFile(":/resources/Viewer.cpp.in");
    initFile.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream in(&initFile);

    // Opening destination file
    QFileInfo extFilePath;
    extFilePath.setFile(directoryName, className + ".cpp");
    QFile extFile(extFilePath.absoluteFilePath());

    if (! extFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QString msg = "Exception from extension generation: \n    Cannot write on file " + extFilePath.fileName() + "\n";
        throw (msg);
    }

    QTextStream out(&extFile);

    // Parsing init file to fill destinaiton file.
    QString text;

    do {
        text = in.readLine();
        text.replace(QRegularExpression("@LICENCE@"), licence);
        text.replace(QRegularExpression("@VIEWERCLASSNAME@"), className);
        text.replace(QRegularExpression("@VIEWERTYPE@"), type);
        text.replace(QRegularExpression("@DESCRIPTION@"), description);
        if (text.contains(QRegularExpression("@BEGIN_ADDCOMPONENTS@"))) {
            text = in.readLine();

            if (components.size() > 0) {
                QStringList addTheComponents;

                while (! text.contains(QRegularExpression("@END_ADDCOMPONENTS@"))) {
                    for (int componentIdx = 0; componentIdx < components.size(); componentIdx++) {
                        QString textTmp = text;
                        textTmp.replace(QRegularExpression("@COMPONENT@"), components[componentIdx]);
                        addTheComponents << textTmp;
                    }

                    text = in.readLine();
                }

                for (int t = 0; t < addTheComponents.size(); t++) {
                    out << addTheComponents.at(t) << Qt::endl;
                }
            }
            else {
                while (! text.contains("@END_ADDCOMPONENTS@")) {
                    text = in.readLine();
                }
            }
        }
        else {
            out << text << Qt::endl;
        }
    }

    while (! text.isNull());

    extFile.close();
    initFile.close();

}
