#!/usr/bin/env python3
"""
languageImport.py

Import LCD language strings from a CSV file or Google Sheets
and write Marlin LCD language files based on the data.

Use languageExport.py to export CSV from the language files.

Google Sheets Link:
https://docs.google.com/spreadsheets/d/12yiy-kS84ajKFm7oQIrC4CF8ZWeu9pAR4zrgxH4ruk4/edit#gid=84528699

TODO: Use the defines and comments above the namespace from existing language files.
      Get the 'constexpr uint8_t CHARSIZE' from existing language files.
      Get the correct 'using namespace' for languages that don't inherit from English.

"""

import sys, re, requests, csv, datetime
from languageUtil import namebyid

LANGHOME = "Marlin/src/lcd/language"
OUTDIR = 'out-language'

# Get the file path from the command line
FILEPATH = sys.argv[1] if len(sys.argv) > 1 else None

download = FILEPATH == 'download'

if not FILEPATH or download:
    SHEETID = "12yiy-kS84ajKFm7oQIrC4CF8ZWeu9pAR4zrgxH4ruk4"
    FILEPATH = 'https://docs.google.com/spreadsheet/ccc?key=%s&output=csv' % SHEETID

if FILEPATH.startswith('http'):
    response = requests.get(FILEPATH)
    assert response.status_code == 200, 'GET failed for %s' % FILEPATH
    csvdata = response.content.decode('utf-8')
else:
    if not FILEPATH.endswith('.csv'): FILEPATH += '.csv'
    with open(FILEPATH, 'r', encoding='utf-8') as f: csvdata = f.read()

if not csvdata:
    print("Error: couldn't read CSV data from %s" % FILEPATH)
    exit(1)

if download:
    DLNAME = sys.argv[2] if len(sys.argv) > 2 else 'languages.csv'
    if not DLNAME.endswith('.csv'): DLNAME += '.csv'
    with open(DLNAME, 'w', encoding='utf-8') as f: f.write(csvdata)
    print("Downloaded %s from %s" % (DLNAME, FILEPATH))
    exit(0)

lines = csvdata.splitlines()
print(lines)
reader = csv.reader(lines, delimiter=',')
gothead = False
columns = ['']
numcols = 0
strings_per_lang = {}
for row in reader:
    if not gothead:
        gothead = True
        numcols = len(row)
        if row[0] != 'name':
            print('Error: first column should be "name"')
            exit(1)
        # The rest of the columns are language codes and names
        for i in range(1, numcols):
            elms = row[i].split(' ')
            lang = elms[0]
            style = ('Wide' if elms[-1] == '(wide)' else 'Tall' if elms[-1] == '(tall)' else 'Narrow')
            columns.append({ 'lang': lang, 'style': style })
            if not lang in strings_per_lang: strings_per_lang[lang] = {}
            if not style in strings_per_lang[lang]: strings_per_lang[lang][style] = {}
        continue
    # Add the named string for all the included languages
    name = row[0]
    for i in range(1, numcols):
        str = row[i]
        if str:
            col = columns[i]
            strings_per_lang[col['lang']][col['style']][name] = str

# Create a folder for the imported language outfiles
from pathlib import Path
Path.mkdir(Path(OUTDIR), exist_ok=True)

FILEHEADER = '''
/**
 * Marlin 3D Printer Firmware
 * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
 *
 * Based on Sprinter and grbl.
 * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */
#pragma once

/**
 * %s
 *
 * LCD Menu Messages
 * See also https://marlinfw.org/docs/development/lcd_language.html
 *
 * Substitutions are applied for the following characters when used in menu items titles:
 *
 *   $ displays an inserted string
 *   { displays  '0'....'10' for indexes 0 - 10
 *   ~ displays  '1'....'11' for indexes 0 - 10
 *   * displays 'E1'...'E11' for indexes 0 - 10 (By default. Uses LCD_FIRST_TOOL)
 *   @ displays an axis name such as XYZUVW, or E for an extruder
 */

'''

# Iterate over the languages which correspond to the columns
# The columns are assumed to be grouped by language in the order Narrow, Wide, Tall
# TODO: Go through lang only, then impose the order Narrow, Wide, Tall.
#       So if something is missing or out of order everything still gets built correctly.

f = None
gotlang = {}
for i in range(1, numcols):
    #if i > 6: break # Testing
    col = columns[i]
    lang, style = col['lang'], col['style']

    # If we haven't already opened a file for this language, do so now
    if not lang in gotlang:
        gotlang[lang] = {}
        if f: f.close()
        fn = "%s/language_%s.h" % (OUTDIR, lang)
        f = open(fn, 'w', encoding='utf-8')
        if not f:
            print("Failed to open %s." % fn)
            exit(1)

        # Write the opening header for the new language file
        #f.write(FILEHEADER % namebyid(lang))
        f.write('/**\n * Imported from %s on %s at %s\n */\n' % (FILEPATH, datetime.date.today(), datetime.datetime.now().strftime("%H:%M:%S")))

    # Start a namespace for the language and style
    f.write('\nnamespace Language%s_%s {\n' % (style, lang))

    # Wide and tall namespaces inherit from the others
    if style == 'Wide':
        f.write('  using namespace LanguageNarrow_%s;\n' % lang)
        f.write('  #if LCD_WIDTH >= 20 || HAS_DWIN_E3V2\n')
    elif style == 'Tall':
        f.write('  using namespace LanguageWide_%s;\n' % lang)
        f.write('  #if LCD_HEIGHT >= 4\n')
    elif lang != 'en':
        f.write('  using namespace Language_en; // Inherit undefined strings from English\n')

    # Formatting for the lines
    indent = '  ' if style == 'Narrow' else '    '
    width = 34 if style == 'Narrow' else 32
    lstr_fmt = '%sLSTR %%-%ds = %%s;%%s\n' % (indent, width)

    # Emit all the strings for this language and style
    for name in strings_per_lang[lang][style].keys():
        # Get the raw string value
        val = strings_per_lang[lang][style][name]
        # Count the number of bars
        if val.startswith('|'):
            bars = val.count('|')
            val = val[1:]
        else:
            bars = 0
        # Escape backslashes, substitute quotes, and wrap in _UxGT("...")
        val = '_UxGT("%s")' % val.replace('\\', '\\\\').replace('"', '$$$')
        # Move named references outside of the macro
        val = re.sub(r'\(([A-Z0-9]+_[A-Z0-9_]+)\)', r'") \1 _UxGT("', val)
        # Remove all empty _UxGT("") that result from the above
        val = re.sub(r'\s*_UxGT\(""\)\s*', '', val)
        # No wrapper needed for just spaces
        val = re.sub(r'_UxGT\((" +")\)', r'\1', val)
        # Multi-line strings start with a bar...
        if bars:
            # Wrap the string in MSG_#_LINE(...) and split on bars
            val = re.sub(r'^_UxGT\((.+)\)', r'_UxGT(MSG_%s_LINE(\1))' % bars, val)
            val = val.replace('|', '", "')
        # Restore quotes inside the string
        val = val.replace('$$$', '\\"')
        # Add a comment with the English string for reference
        comm = ''
        if lang != 'en' and 'en' in strings_per_lang:
            en = strings_per_lang['en']
            if name in en[style]: str = en[style][name]
            elif name in en['Narrow']: str = en['Narrow'][name]
            if str:
                cfmt = '%%%ss// %%s' % (50 - len(val) if len(val) < 50 else 1)
                comm = cfmt % (' ', str)

        # Write out the string definition
        f.write(lstr_fmt % (name, val, comm))

    if style == 'Wide' or style == 'Tall': f.write('  #endif\n')

    f.write('}\n') # End namespace

    # Assume the 'Tall' namespace comes last
    if style == 'Tall': f.write('\nnamespace Language_%s {\n  using namespace LanguageTall_%s;\n}\n' % (lang, lang))

# Close the last-opened output file
if f: f.close()