#!/usr/bin/env python3
# -*- coding: utf-8 -*-

##########################################################################
##### PUBLIC DOMAIN ######################################################
# This script is released into the public domain via the CC0 dedication.
# See <https://creativecommons.org/publicdomain/zero/1.0/> for details.
##########################################################################

##########################################################################
##### ABOUT THIS SCRIPT ##################################################
# This script updates the translation template files (*.pot) or
# translation files (*.po) of all mods by using the gettext tools.
# It requires you have the 'gettext' software installed on your system.
# This script is tailored towards Lazarr!
#
# INVOCATION:
# ./update_locale_files.py [mode]
#
#     where "mode" is either "pot" if you want to update the *.pot files
#     or "po" if you want to update the *.po files. If "mode" is omitted,
#     it defaults to "pot".
##########################################################################

# e-mail address to send problems with the original strings ("msgids") to
MSGID_BUGS_ADDRESS = "Wuzzy@disroot.org"
# name of the package
PACKAGE_NAME = "Lazarr!"
# version of the package (optional)
PACKAGE_VERSION = "2.0.0"

import os
import re
import sys

# pattern for the 'name' in mod.conf
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
# file name pattern for gettext translation template files (*.pot)
pattern_pot = re.compile(r'(.*)\.pot$')
# file name pattern for gettext translation files (*.po)
pattern_po = re.compile(r'(.*)\.po$')
# matches the basename of a file (the part before the first dot)
pattern_basename = re.compile(r'([^.]*)\.')

# Takes a file name (PO or POT) and returns true if it is deemed "untouchable",
# i.e. it should be left alone by the updater script.
def is_file_untouchable(name):
    return (
        # Core level names are updated in a special function
        name == "lzr_levels_core_level_names.pot" or
        # Core level NPC texts are updated in a special function
        name == "lzr_levels_core_npc_texts.pot" or
        # Trigger type abbreviations. Need to be updated manually when new trigger types are added
        name == "_lzr_triggers_abbreviations.pot")

def invoke_msgmerge(template_file, mod_folder, modname):
    containing_path = os.path.dirname(template_file)
    template_file_no_path = os.path.basename(template_file)

    match_pot_basename = pattern_basename.match(template_file_no_path)
    pot_basename = None
    if match_pot_basename:
       pot_basename = match_pot_basename.group(1)
    else:
       print("ERROR: POT file does not match regex: " + str(template_file))
       exit(1)

    po_files = []
    for root, dirs, files in os.walk(os.path.join(mod_folder)):
       for f in files:
          equal_basename = False
          match = pattern_po.match(f)
          if match:
             match_po_basename = pattern_basename.match(f)
             if match_po_basename:
                po_basename = match_po_basename.group(1)
                equal_basename = pot_basename == po_basename
             else:
                print("ERROR: PO file does not match regex: " + str(f))
                exit(1)
          if equal_basename:
             po_files.append(f)
    if len(po_files) > 0:
        for po_file in po_files:
            po_file = os.path.join("locale", po_file)
            po_file = os.path.join(mod_folder, po_file)
            command = "msgmerge --backup=none -U '"+po_file+"' '"+template_file+"'"
            return_value = os.system(command)
            if return_value != 0:
                print("ERROR: msgmerge invocation returned with "+str(return_value))
                exit(1)

def invoke_xgettext(template_file, mod_folder, modname):
    containing_path = os.path.dirname(template_file)
    lua_files = [os.path.join(mod_folder, "*.lua")]
    for root, dirs, files in os.walk(os.path.join(mod_folder)):
       for dirname in dirs:
           if dirname != "sounds" and dirname != "textures" and dirname != "models" and dirname != "locale" and dirname != "media" and dirname != "schematics" and dirname != "data" and dirname != "solutions":
               lua_path = os.path.join(mod_folder, dirname)
               lua_files.append(os.path.join(lua_path, "*.lua"))

    lua_search_string = " ".join(lua_files)

    package_string = "--package-name='"+PACKAGE_NAME+"'"
    if PACKAGE_VERSION:
        package_string += " --package-version='"+PACKAGE_VERSION+"'"

    command = "xgettext -L lua -kS -kNS -kFS -kNFS -kPS:1,2 -kminetest.translate:1c,2 -kminetest.translate_n:1c,2,3 -d '"+modname+"' --add-comments='~' -o '"+template_file+"' --from-code=UTF-8 --msgid-bugs-address='"+MSGID_BUGS_ADDRESS+"' "+package_string+" "+lua_search_string

    return_value = os.system(command)
    if return_value != 0:
        print("ERROR: xgettext invocation returned with "+str(return_value))
        exit(1)

# Invoke xgettext for a level pack mod to collect the special strings for level names and NPC texts
def invoke_xgettext_level_pack(modname, domain_level_names, domain_npc_texts):
    lua_search_string = os.path.join("mods", modname, "_locale.lua")
    package_string = "--package-name='"+PACKAGE_NAME+"'"
    if PACKAGE_VERSION:
        package_string += " --package-version='"+PACKAGE_VERSION+"'"

    file_level_names = domain_level_names + ".pot"
    file_npc_texts = domain_npc_texts + ".pot"

    path_level_names = os.path.join("mods", modname, "locale", file_level_names)
    path_npc_texts = os.path.join("mods", modname, "locale", file_npc_texts)

    # Level names
    command = "xgettext -L lua -kNS_LEVEL_NAME -d '"+domain_level_names+"' --add-comments='~' -o '"+path_level_names+"' --from-code=UTF-8 --no-location --msgid-bugs-address='"+MSGID_BUGS_ADDRESS+"' "+package_string+" "+lua_search_string
    return_value = os.system(command)
    if return_value != 0:
        print("ERROR: xgettext invocation for level names for "+modname+" returned with "+str(return_value))
        exit(1)

    # NPC texts
    command = "xgettext -L lua -kNS_NPC_TEXT -d '"+domain_npc_texts+"' --add-comments='~' -o '"+path_npc_texts+"' --from-code=UTF-8 --no-location --msgid-bugs-address='"+MSGID_BUGS_ADDRESS+"' "+package_string+" "+lua_search_string
    return_value = os.system(command)
    if return_value != 0:
        print("ERROR: xgettext invocation for NPC texts for "+modname+" returned with "+str(return_value))
        exit(1)


def update_locale_template(folder, modname, mode):
    for root, dirs, files in os.walk(os.path.join(folder, 'locale')):
        for name in files:
            code_match = pattern_pot.match(name)
            if code_match == None:
                continue
            fname = os.path.join(root, name)
            if mode == "pot":
                if not is_file_untouchable(name):
                   invoke_xgettext(fname, folder, modname)
            elif mode == "po":
                invoke_msgmerge(fname, folder, modname)
            else:
                print("ERROR: invalid locale template mode!")
                exit(1)

def get_modname(folder):
    try:
        with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
            for line in mod_conf:
                match = pattern_name.match(line)
                if match:
                    return match.group(1)
    except FileNotFoundError:
        if not os.path.isfile(os.path.join(folder, "modpack.txt")):
            folder_name = os.path.basename(folder)
            return folder_name
        else:
            return None
    return None

def update_mod(folder, mode):
    modname = get_modname(folder)
    if modname != None:
       print("Updating '"+modname+"' ...")
       update_locale_template(folder, modname, mode)

def main():
    mode = ""
    if len(sys.argv) >= 2:
        mode = sys.argv[1]
    if mode == "":
        mode = "pot"
    if mode != "po" and mode != "pot":
        print("ERROR: invalid mode specified. Provide either 'po', 'pot' or nothing as command line argument")
        exit(1)

    if mode == "pot":
       # Lazarr! special: The special locale files for the core level pack
       print("Updating special strings for core level pack [lzr_levels_core] ...")
       invoke_xgettext_level_pack("lzr_levels_core", "lzr_levels_core_level_names", "lzr_levels_core_npc_texts")

    print("Updating regular strings for mods ...")
    for modfolder in [f.path for f in os.scandir("./mods") if f.is_dir() and not f.name.startswith('.')]:
        is_modpack = os.path.exists(os.path.join(modfolder, "modpack.txt")) or os.path.exists(os.path.join(modfolder, "modpack.conf"))
        if is_modpack:
            subfolders = [f.path for f in os.scandir(modfolder) if f.is_dir() and not f.name.startswith('.')]
            for subfolder in subfolders:
                update_mod(subfolder, mode)
        else:
            update_mod(modfolder, mode)

    print("All done.")

main()
