# SPDX-FileCopyrightText: 2017 Florent Rougon
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileComment: Merge translations from one XLIFF file into another one

"""Merge translations from one XLIFF file into another one."""

import argparse
import locale
import logging
import os
import sys

import flightgear.meta.i18n as fg_i18n


PROGNAME = os.path.basename(sys.argv[0])
# 1) This module is “semantically” a script. 2) __name__ would be very long for
# inclusion in log messages
logger = logging.getLogger(PROGNAME)


def processCommandLine():
    parser = argparse.ArgumentParser(
        usage="""\
%(prog)s [OPTION ...] SOURCE INTO
Merge strings from a FlightGear XLIFF localization file into another one.""",
        description="""\
This program merges a FlightGear XLIFF localization file into another one.
This means that every translatable string that:

 (1) exists in both SOURCE and INTO;

 (2) has the same target language, source text, plural status and number of
     plural forms in SOURCE and in INTO;

is updated from SOURCE, i.e.: the target texts, 'approved' status,
translation state and translator comments are copied from SOURCE.

The result is written to INTO unless the -o (--output) option is given.

Note that this program is different from fg-update-translation-files's
'merge-new-master' command, which is for updating an XLIFF file according to
the default translation ("master").

Expected use case: suppose that a translator is working on a translation
file, and meanwhile the official XLIFF file for this translation is updated
in the project repository (new translatable strings added, obsolete strings
marked or removed, etc.). This program can then be used to merge the
translator work into the project file for all strings for which it makes
sense (source text unchanged, same plural status, etc.).""",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        # I want --help but not -h (it might be useful for something else)
        add_help=False)

    parser.add_argument("source", metavar="SOURCE",
                        help="""\
                        input XLIFF file; read updated translated strings
                        from this file""")
    parser.add_argument("into", metavar="INTO",
                        help="""\
                        XLIFF file to compare to SOURCE in order to decide
                        which translated strings to update; unless the -o
                        option is used, updated strings are written to this
                        file""")
    parser.add_argument("-o", "--output",
                        help="""\
                        write the XLIFF merged output to OUTPUT instead of
                        INTO. When this option is used, INTO is read but not
                        modified. If OUTPUT is '-', write the XLIFF merged
                        output to the standard output.""")
    parser.add_argument("--log-level", metavar="LEVEL", default="INFO",
        # No use of 'choices' because we want to ignore the case
        help="""\
      show messages with a level higher than or equal to %(metavar)s
      (default: %(default)s; available values: DEBUG, INFO, WARNING, ERROR,
      CRITICAL)""")
    parser.add_argument("--log-style", metavar="STYLE", default="default",
                        choices=("show-origins", "default", "terse"),
                        help="""\
      select a format style for log messages (available choices: %(choices)s;
      default: %(default)s)""")
    parser.add_argument("--help", action="help",
                        help="display this message and exit")

    params = parser.parse_args()

    uppercaseLogLevel = params.log_level.upper()
    if uppercaseLogLevel not in ("DEBUG", "INFO", "WARNING", "ERROR",
                                 "CRITICAL"):
        print(f"{PROGNAME}: invalid log level: {params.log_level!r}")
        sys.exit(1)

    return params


def setupLogging(params):
    # Normally, processCommandLine() has validated the log level, so raise
    # an exception if it can't be found in the 'logging' module.
    try:
        numericLogLevel = getattr(logging, params.log_level.upper())
    except AttributeError:
        print(f"{PROGNAME}: unexpected log level {params.log_level.upper()!r}")
        raise

    # Same here: if params.log_style is not a key, this is a bug.
    logFormat = {
        "show-origins": "{levelname}:{name}: {message}",
        "default": "{levelname}: {message}",
        "terse": "{message}",
    }

    # Configure the root logger. This is fine because we are “semantically” a
    # script.
    logging.basicConfig(level=numericLogLevel, stream=sys.stderr, style="{",
                        format=logFormat[params.log_style])


def mergeXliffIntoXliff(source, into, output):
    formatHandler = fg_i18n.XliffFormatHandler()

    srcTransl = formatHandler.readTranslation(source)
    transl = formatHandler.readTranslation(into)
    # Merge 'srcTransl' into 'transl'
    transl.mergeNonMasterTransl(srcTransl)

    # File path, or '-' for the standard output
    outputFile = into if output is None else output
    formatHandler.writeTranslation(transl, outputFile)


def main():
    locale.setlocale(locale.LC_ALL, '')
    params = processCommandLine()
    setupLogging(params)

    mergeXliffIntoXliff(params.source, params.into, params.output)

    return 0


if __name__ == "__main__":
    sys.exit(main())
