#!/usr/bin/env python

from __future__ import print_function
import json
import os
import io
import sys

TRANSLATION_CPP = "Translation.cpp"

try :
    to_unicode = unicode
except NameError:
    to_unicode = str


# Loading a single JSON file
def loadJson(fileName, skipFirstLine):
    with io.open(fileName, mode="r", encoding="utf-8") as f:
        if skipFirstLine:
            f.readline()

        obj = json.loads(f.read())

    return obj


# Reading all language translations into a dictionary by langCode
def readTranslations(jsonDir):
    langDict = {}
    
    # Read all translation files from the input dir
    for fileName in os.listdir(jsonDir):
        
        fileWithPath = os.path.join(jsonDir, fileName)
        lf = fileName.lower()
    
        # Read only translation_XX.json
        if lf.startswith("translation_") and lf.endswith(".json"):
            try:
                lang = loadJson(fileWithPath, False)
            except json.decoder.JSONDecodeError as e:
                print("Failed to decode " + lf)
                print(str(e))
                sys.exit(2)

            # Extract lang code from file name
            langCode = fileName[12:-5].upper()
            # ...and the one specified in the JSON file...
            try:
                langCodeFromJson = lang['languageCode']
            except KeyError:
                langCodeFromJson = "(missing)"

            # ...cause they should be the same!
            if langCode != langCodeFromJson:
                raise ValueError("Invalid languageCode " + langCodeFromJson + " in file " + fileName)
            
            langDict[langCode] = lang
    
    return langDict


def writeStart(f):
    f.write(to_unicode("""// WARNING: THIS FILE WAS AUTO GENERATED BY make_translation.py. PLEASE DO NOT EDIT.

#include "Translation.h"
#ifndef LANG
#define LANG_EN
#endif
"""))


def escapeC(s):
    return s.replace("\"", "\\\"")


def writeLanguage(languageCode, defs, f):
    print("Generating block for " + languageCode)
    lang = langDict[languageCode]
    
    f.write(to_unicode("\n#ifdef LANG_" + languageCode + "\n"))
    try:
        langName = lang['languageLocalName']
    except KeyError:
        langName = languageCode
         
    f.write(to_unicode("// ---- " + langName + " ----\n\n"))

    try:
        cyrillic = lang['cyrillicGlyphs']
    except KeyError:
        cyrillic = False

    if cyrillic :
        f.write(to_unicode("#define CYRILLIC_GLYPHS\n\n"))
        
    # ----- Writing SettingsDescriptions
    obj = lang['menuOptions']
    f.write(to_unicode("const char* SettingsDescriptions[] = {\n"))
    
    maxLen = 25
    for mod in defs['menuOptions']:
        eid = mod['id']
        if 'feature' in mod:
            f.write(to_unicode("#ifdef " + mod['feature'] + "\n"))
        f.write(to_unicode("  /* " + eid.ljust(maxLen)[:maxLen] + " */ "))
        f.write(to_unicode("\"" + escapeC(obj[eid]['desc']) + "\",\n"))
        if 'feature' in mod:
            f.write(to_unicode("#endif\n"))

    f.write(to_unicode("};\n\n"))
    
    # ----- Writing Message strings
    
    obj = lang['messages']
    
    for mod in defs['messages']:
        eid = mod['id']
        f.write(to_unicode("const char* " + eid + " = \"" + escapeC(obj[eid]) + "\";\n"))

    f.write(to_unicode("\n"))

    # ----- Writing Characters
    
    obj = lang['characters']
    
    for mod in defs['characters']:
        eid = mod['id']
        f.write(to_unicode("const char " + eid + " = '" + obj[eid] + "';\n"))

    f.write(to_unicode("\n"))
    
    # ----- Menu Options

    # Menu type
    f.write(to_unicode("const enum ShortNameType SettingsShortNameType = SHORT_NAME_" + ("DOUBLE" if lang['menuDouble'] else "SINGLE") + "_LINE;\n"))

    # ----- Writing SettingsDescriptions
    obj = lang['menuOptions']
    f.write(to_unicode("const char* SettingsShortNames[][2] = {\n"))
    
    maxLen = 25
    for mod in defs['menuOptions']:
        eid = mod['id']
        if 'feature' in mod:
            f.write(to_unicode("#ifdef " + mod['feature'] + "\n"))
        f.write(to_unicode("  /* " + eid.ljust(maxLen)[:maxLen] + " */ "))
        if lang['menuDouble']:
            f.write(to_unicode("{ \"" + escapeC(obj[eid]['text2'][0]) + "\", \"" + escapeC(obj[eid]['text2'][1]) + "\" },\n"))
        else:
            f.write(to_unicode("{ \"" + escapeC(obj[eid]['text']) + "\" },\n"))
        if 'feature' in mod:
            f.write(to_unicode("#endif\n"))
                
    f.write(to_unicode("};\n\n"))

    # ----- Writing Menu Groups
    obj = lang['menuGroups']
    f.write(to_unicode("const char* SettingsMenuEntries[" + str(len(obj)) + "] = {\n"))
    
    maxLen = 25
    for mod in defs['menuGroups']:
        eid = mod['id']
        f.write(to_unicode("  /* " + eid.ljust(maxLen)[:maxLen] + " */ "))
        f.write(to_unicode("\"" + escapeC(obj[eid]['text2'][0] + "\\n" + obj[eid]['text2'][1]) + "\",\n"))
                
    f.write(to_unicode("};\n\n"))

    # ----- Writing Menu Groups Descriptions
    obj = lang['menuGroups']
    f.write(to_unicode("const char* SettingsMenuEntriesDescriptions[" + str(len(obj)) + "] = {\n"))
    
    maxLen = 25
    for mod in defs['menuGroups']:
        eid = mod['id']
        f.write(to_unicode("  /* " + eid.ljust(maxLen)[:maxLen] + " */ "))
        f.write(to_unicode("\"" + escapeC(obj[eid]['desc']) + "\",\n"))
                
    f.write(to_unicode("};\n\n"))

    # ----- Block end    
    f.write(to_unicode("#endif\n"))


def read_opts():
    """ Reading input parameters
    First parameter = json directory
    Second parameter = target directory
    """
    if len(sys.argv) > 1:
        jsonDir = sys.argv[1]
    else:
        jsonDir = "."

    if len(sys.argv) > 2:
        outFile = sys.argv[2]
    else:
        outDir = os.path.relpath(jsonDir + "/../workspace/TS100/src/")
        outFile = os.path.join(outDir, TRANSLATION_CPP)

    if len(sys.argv) > 3:
        raise Exception("Too many parameters!")

    return jsonDir, outFile


def orderOutput(langDict):
    # These languages go first
    mandatoryOrder = ['EN']

    # Then add all others in alphabetical order
    sortedKeys = sorted(langDict.keys())

    # Add the rest as they come
    for key in sortedKeys:
        if key not in mandatoryOrder:
            mandatoryOrder.append(key)

    return mandatoryOrder
    

def writeTarget(outFile, defs, langCodes):
    # Start writing the file
    with io.open(outFile, 'w', encoding='utf-8', newline="\n") as f:
        writeStart(f)

        for langCode in langCodes:
            writeLanguage(langCode, defs, f)


if __name__ == "__main__":
    try:
        jsonDir, outFile = read_opts()
    except:
        print("usage: make_translation.py {json dir} {cpp dir}")
        sys.exit(1)

    print("Making " + outFile + " from " + jsonDir)

    langDict = readTranslations(jsonDir)
    defs = loadJson(os.path.join(jsonDir, "translations_def.js"), True)
    langCodes = orderOutput(langDict)
    writeTarget(outFile, defs, langCodes)

    print("Done")
