import qbs.File
import qbs.FileInfo
import qbs.TextFile
import qbs.Process

/**
  This module generates 'i18n.pro' artifact, which then acts as an input for 'lupdate' program, which in turn produces
  translation files, which are compiled by 'lrelease' program into 'qm' files, which can be loaded by an application.
  */
Module {
    Depends { name: "Qt.core" }

    additionalProductTypes: ["i18n"]

    /*
      Unfortunately you can not simply add empty files to the product, cause 'Qt.core' module has a rule, which calls 'lrelease' on
      every 'ts' file in the product and 'lrelease' triggers error if these files are empty. Additionaly 'lupdate' also triggers
      errors, when parsing 'pro' file. Instead this property can be used to create new translation file.
      */
    property stringList additionalTranslations: []

    // Explicitly trigger build even if build a product
    property bool update: false

    // Build with legacy way though .pro file
    property bool buildWithPro: false

    property string lupdateName: "lupdate"

    readonly property string qtTranslationsPath: qtTranslationsProbe.qtTranslationsPath

    Rule {
        condition: update && buildWithPro
        multiplex: true
        inputs: ["i18n.hpp", "i18n.src", "i18n.ui", "i18n.res", "i18n.ts"]

        prepare: {
            var proCmd = new JavaScriptCommand();
            proCmd.description = 'generating ' + output.filePath;
            proCmd.highlight = 'codegen';
            proCmd.sourceCode = function() {
                var f = new TextFile(output.filePath, TextFile.WriteOnly);
                try {
                    f.writeLine("lupdate_only {");
                    if (inputs["i18n.hpp"] !== undefined)
                        for (var i = 0; i < inputs["i18n.hpp"].length; i++)
                            f.writeLine("HEADERS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.hpp"][i].filePath));
                        f.writeLine("");
                    if (inputs["i18n.src"] !== undefined)
                        for (var i = 0; i < inputs["i18n.src"].length; i++)
                            f.writeLine("SOURCES += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.src"][i].filePath));
                        f.writeLine("");
                    if (inputs["i18n.ui"] !== undefined)
                        for (var i = 0; i < inputs["i18n.ui"].length; i++)
                            f.writeLine("FORMS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.ui"][i].filePath));
                        f.writeLine("");
                    // lupdate processes QML files that are listed in the .qrc file
                    if (inputs["i18n.res"] !== undefined)
                        for (var i = 0; i < inputs["i18n.res"].length; i++)
                            f.writeLine("RESOURCES += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.res"][i].filePath));
                    f.writeLine("}");

                    f.writeLine("");
                    if (inputs["i18n.ts"] !== undefined)
                        for (var i = 0; i < inputs["i18n.ts"].length; i++)
                            f.writeLine("TRANSLATIONS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.ts"][i].filePath));
                    for (var i = 0; i < product.i18n.additionalTranslations.length; i++) {
                        var targetDirectory = product.sourceDirectory + "/" + FileInfo.path(product.i18n.additionalTranslations[i]);
                        if (!File.exists(targetDirectory))
                            console.error("Directory '" + targetDirectory + "' does not exists. Please create it.");
                        f.writeLine("TRANSLATIONS += " + product.i18n.additionalTranslations[i]);
                    }
                } finally {
                    f.close();
                }
            }
            return [proCmd];
        }

        Artifact {
            filePath: product.sourceDirectory + "/" + product.name + ".i18n.pro"
            fileTags: ["i18n.pro"]
        }
    }

    Rule {
        condition: buildWithPro
        inputs: ["i18n.pro"]

        prepare: {
            var lupdateName = product.i18n.lupdateName;
            var cmdLupdate = new Command(product.Qt.core.binPath + '/' + lupdateName, ["-verbose", input.filePath]);
            cmdLupdate.description = "Invoking '" + lupdateName + "' program";
            cmdLupdate.highlight = 'filegen';

            var cmdClean = new JavaScriptCommand();
            cmdClean.description = "Removing " + input.fileName;
            cmdClean.highlight = "filegen";
            cmdClean.sourceCode = function() {
                File.remove(input.filePath);
            }
            return [cmdLupdate, cmdClean]
        }

        outputFileTags: ["i18n"]
    }

    Rule {
        condition: update && !buildWithPro
        multiplex: true
        inputs: ["i18n.hpp", "i18n.src", "i18n.ui", "i18n.ts"]

        prepare: {
            var proCmd = new JavaScriptCommand();
            proCmd.description = 'generating ' + output.filePath;
            proCmd.highlight = 'codegen';
            proCmd.sourceCode = function() {
                var f = new TextFile(output.filePath, TextFile.WriteOnly);
                try {
                    // Since Qt 5.13 lupdate supports passing a project description in JSON file. For producing such a
                    // description from .pro file we can use new tool lprodump. But tehnically we don't need it. We can
                    // totally fake format.
                    // JSON file structure:
                    // Project ::= {
                    //    string projectFile     // Name of the project file. (required)
                    //    string codec           // Source code codec. Valid values are
                    //                           // currently "utf-16" or "utf-8" (default).
                    //    string[] translations  // List of .ts files of the project. (required)
                    //    string[] includePaths  // List of include paths.
                    //    string[] sources       // List of source files. (required)
                    //    string[] excluded      // List of source files, which are
                    //                           // excluded for translation.
                    //    Project[] subProjects  // List of sub-projects.
                    // }
                    // It seems all we need are projectFile, sources and translations options.

                    var sources = [];
                    var includePaths = [];
                    if (inputs["i18n.hpp"] !== undefined)
                        for (var i = 0; i < inputs["i18n.hpp"].length; i++) {
                            sources.push(inputs["i18n.hpp"][i].filePath);
                            if (!includePaths.includes(FileInfo.path(inputs["i18n.hpp"][i].filePath)))
                                includePaths.push(FileInfo.path(inputs["i18n.hpp"][i].filePath));
                        }

                    if (inputs["i18n.src"] !== undefined)
                        for (var i = 0; i < inputs["i18n.src"].length; i++)
                            sources.push(inputs["i18n.src"][i].filePath);

                    if (inputs["i18n.ui"] !== undefined)
                        for (var i = 0; i < inputs["i18n.ui"].length; i++)
                            sources.push(inputs["i18n.ui"][i].filePath);

                    // lupdate processes QML files that are listed in the .qrc file
                    if (inputs["i18n.res"] !== undefined)
                        for (var i = 0; i < inputs["i18n.res"].length; i++)
                            sources.push(inputs["i18n.res"][i].filePath);

                    var translations = [];
                    if (inputs["i18n.ts"] !== undefined)
                        for (var i = 0; i < inputs["i18n.ts"].length; i++)
                            translations.push(inputs["i18n.ts"][i].filePath);

                    for (var i = 0; i < product.i18n.additionalTranslations.length; i++) {
                        var targetDirectory = product.sourceDirectory + "/" + FileInfo.path(product.i18n.additionalTranslations[i]);
                        if (!File.exists(targetDirectory))
                            console.error("Directory '" + targetDirectory + "' does not exists. Please create it.");
                        translations.push(product.i18n.additionalTranslations[i]);
                    }

                    var project = {
                        projectFile: "", // Looks like can be empty
                        includePaths: includePaths.sort(),
                        sources: sources.sort(),
                        translations: translations.sort()
                    };

                    f.write(JSON.stringify([project], null, 2));
                } finally {
                    f.close();
                }
            }
            return [proCmd];
        }

        Artifact {
            filePath: product.sourceDirectory + "/" + product.name + ".i18n.json"
            fileTags: ["i18n.json"]
        }
    }

    Rule {
        condition: !buildWithPro
        inputs: ["i18n.json"]

        prepare: {
            var lupdateName = product.i18n.lupdateName;
            var cmdLupdate = new Command(product.Qt.core.binPath + '/' + lupdateName, ["-verbose", "-project", input.filePath]);
            cmdLupdate.description = "Invoking '" + lupdateName + "' program";
            cmdLupdate.highlight = 'filegen';

            var cmdClean = new JavaScriptCommand();
            cmdClean.description = "Removing " + input.fileName;
            cmdClean.highlight = "filegen";
            cmdClean.sourceCode = function() {
                File.remove(input.filePath);
            }
            return [cmdLupdate, cmdClean]
        }

        outputFileTags: ["i18n"]
    }

    Probe {
        id: qtTranslationsProbe

        readonly property string binPath: product.Qt.core.binPath

        // TODO: If minimal qbs version is 1.23 replace with FileInfo.executableSuffix()
        readonly property string executableSuffix: product.qbs.targetOS.contains("windows") ? ".exe" : ""

        property string qtTranslationsPath

        configure: {
            var qmakeProcess = new Process();
            try {
                var qmakePath = FileInfo.joinPaths(binPath, "qmake" + executableSuffix);
                qmakeProcess.exec(qmakePath, ["-query"]);
                if (qmakeProcess.exitCode() !== 0) {
                    throw "The qmake executable '" + FileInfo.toNativeSeparators(qmakePath) + "' failed with exit code " +
                            qmakeProcess.exitCode() + ".";
                }
                while (!qmakeProcess.atEnd()) {
                    var line = qmakeProcess.readLine();
                    var index = (line || "").indexOf(":");
                    if (index !== -1) {
                        if (line.slice(0, index) === "QT_INSTALL_TRANSLATIONS") {
                            var path = line.slice(index + 1).trim();
                            if (path)
                                qtTranslationsPath = FileInfo.fromNativeSeparators(path);
                            break;
                        }
                    }
                }
            } finally {
                qmakeProcess.close();
            }
        }
    }
}