#!/usr/bin/env python

# Run this script from top-level wxWidgets directory to update the contents of
# include/wx/intl.h and src/common/intl.cpp using information from langtabl.txt
#
# Warning: error detection and reporting here is rudimentary, check if the
# files were updated correctly with "git diff" before committing them!

import os
import string
import sys

def ReadRegionGroupTable():
    regiongrouptable = []
    try:
        f = open('misc/languages/regiongrouptabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        regiongrouptable.append((ispl[0], ispl[1]))
    f.close()
    return regiongrouptable

def ReadMatchingTable():
    matchingtable = []
    try:
        f = open('misc/languages/matchingtabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        matchingtable.append((ispl[0], ispl[1], ispl[2]))
    f.close()
    return matchingtable

def ReadLikelyTable():
    likelytable = []
    try:
        f = open('misc/languages/likelytabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        likelytable.append((ispl[0], ispl[1]))
    f.close()
    return likelytable

def ReadScriptTable():
    scripttable = []
    try:
        f = open('misc/languages/scripttabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        scripttable.append((ispl[0], ispl[1]))
    f.close()
    return scripttable

def ReadSynonymTable():
    synonymtable = []
    try:
        f = open('misc/languages/synonymtabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        synonymtable.append((ispl[0], ispl[1], ispl[2], ispl[3]))
    f.close()
    return synonymtable

def ReadTable():
    table = []
    try:
        f = open('misc/languages/langtabl.txt')
    except:
        print("Did you run the script from top-level wxWidgets directory?")
        raise

    for i in f.readlines():
        ispl = i.split()
        table.append((ispl[0], ispl[1], ispl[2], ispl[3], ispl[4], ispl[5], ispl[6], ispl[7], ' '.join(ispl[8:])))
    f.close()
    return table


# Kind may be "include" or "interface".
def WriteEnum(f, table, synonymtable, kind = 'include'):
   f.write("""
enum wxLanguage
{
    /// User's default/preferred language as got from OS.
    wxLANGUAGE_DEFAULT,

    /// Unknown language, returned if wxLocale::GetSystemLanguage fails.
    wxLANGUAGE_UNKNOWN,

""");
   knownLangs = []
   output = ''
   for i in table:
       lang = i[0]
       wxver = i[1]
       if lang not in knownLangs:
          output += '    %s,' % lang
          if kind == 'interface' and wxver != '-':
              output += '%s///< @since_wx{%s}' % (' ' * (56 - len(lang)), wxver)
          output += '\n'
          knownLangs.append(lang)
   output += """
    /// For custom, user-defined languages.
    wxLANGUAGE_USER_DEFINED,
"""

   if kind == 'include':
      output += '\n    /// Synonyms.'

   output += '\n'

   for i in synonymtable:
      lang = i[0]
      synonym = i[1]
      wxver = i[3]
      output += '    %s' % lang
      if kind == 'include':
         output += ' = %s,\n' % synonym
      elif kind == 'interface':
         if wxver != '-':
           output += ',%s///< Synonym for %s. @since_wx{%s}\n' % (' ' * (42 - len(lang)), synonym, wxver)
         else:
           output += ',%s///< Synonym for %s.\n' % (' ' * (42 - len(lang)), synonym)
      else:
        print("Unknown kind of generated enum")
        raise

   output += '};\n\n'
   f.write(output)

def WriteTable(f, table, synonymtable):
   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/langtabl.txt

#if !defined(__WIN32__)

#define SETWINLANG(info,lang,sublang)

#else

#define SETWINLANG(info,lang,sublang) \\
    info.WinLang = tabLangData[j].lang; \\
    info.WinSublang = tabLangData[j].sublang;

#endif // __WIN32__

// Include actual data tables
#include "wx/private/lang_info.h"
#include "wx/private/lang_scripts.h"
#include "wx/private/lang_likely.h"
#include "wx/private/lang_match.h"
#include "wx/private/lang_regions.h"

void wxUILocaleImpl::InitLanguagesDB()
{
    wxLanguageInfo info;
    int j;

    // Known languages
    for (j = 0; tabLangData[j].wxlang != 0; ++j)
    {
        info.Language = tabLangData[j].wxlang;
        info.LocaleTag = tabLangData[j].bcp47tag;
        info.CanonicalName = tabLangData[j].canonical;
        info.CanonicalRef = tabLangData[j].canonicalref;
        info.LayoutDirection = tabLangData[j].layout;
        info.Description = wxString::FromUTF8(tabLangData[j].desc);
        info.DescriptionNative = wxString::FromUTF8(tabLangData[j].descnative);
        SETWINLANG(info, winlang, winsublang)
        wxUILocale::AddLanguage(info);
    }

    // Known language scripts
    for (j = 0; tabScriptData[j].scname; ++j)
    {
        gs_scmap_name2alias[tabScriptData[j].scname] = tabScriptData[j].scalias;
        gs_scmap_alias2name[tabScriptData[j].scalias] = tabScriptData[j].scname;
    }

    // Known likely subtags
    for (j = 0; tabLikelyData[j].tagfrom; ++j)
    {
        gs_likely_subtags_map[tabLikelyData[j].tagfrom] = tabLikelyData[j].tagto;
    }

    // Known matching language tags
    for (j = 0; tabMatchData[j].matchkey; ++j)
    {
        gs_matching_tags_map[tabMatchData[j].matchkey] = tabMatchData[j].matchval;
    }

    // Known region groups
    wxString langPrev;
    wxString lang;
    std::unordered_set<wxString>* regionGroup = nullptr;
    for (j = 0; regionGroupData[j].language; ++j)
    {
        lang = regionGroupData[j].language;
        if (!langPrev.IsSameAs(lang))
        {
            langPrev = lang;
            gs_region_groups_map[lang] = std::unordered_set<wxString>();
            regionGroup = &gs_region_groups_map[lang];
        }
        regionGroup->insert(regionGroupData[j].country);
    }
}

""" )

def WriteInfoTable(f, table, synonymtable):
   lngtable = ''

   for i in table:
       ibcp47 = '"%s"' % i[2]
       ican = '"%s"' % i[3]
       if ican == '"-"': ican = '""'
       icanbase = '"%s"' % i[4]
       if icanbase == '"-"': icanbase = '""'
       ilang = i[5]
       if ilang == '-': ilang = '0'
       isublang = i[6]
       if isublang == '-': isublang = '0'
       if (i[7] == "LTR") :
           ilayout = "wxLayout_LeftToRight"
       elif (i[7] == "RTL"):
           ilayout = "wxLayout_RightToLeft"
       else:
           print("ERROR: Invalid value for the layout direction")
       lngtable += '    { %-60s %-17s, %-28s, %-15s, %-4s, %-4s, %s, %s },\n' % \
                     ((i[0]+','), ibcp47, ican, icanbase, ilang, isublang, ilayout, i[8])

   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/langtabl.txt

// Data table for known languages
static const struct langData_t
{
    int   wxlang;
    const char* bcp47tag;
    const char* canonical;
    const char* canonicalref;
    wxUint32 winlang;
    wxUint32 winsublang;
    wxLayoutDirection layout;
    const char* desc;
    const char* descnative;
}
tabLangData[] =
{
%s
    { 0, nullptr, nullptr, nullptr, 0, 0, wxLayout_Default, nullptr, nullptr }
};
""" % (lngtable))


def WriteScriptTable(f, scripttable, synonymtable):
   sctable = ''
   for i in scripttable:
       scname = '"%s"' % i[0]
       scalias = '"%s"' % i[1]
       sctable += '    { %s, %s },\n' % (scname, scalias)

   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/scripttabl.txt

// Data table for known language scripts
static const struct scriptData_t
{
    const char* scname;
    const char* scalias;
}
tabScriptData[] =
{
%s
    { nullptr, nullptr }
};
""" % (sctable))

def WriteLikelyTable(f, likelytable, synonymtable):
   tagtable = ''
   for i in likelytable:
       tagfrom = '"%s"' % i[0]
       tagto = '"%s"' % i[1]
       tagtable += '    { %-11s %s },\n' % (tagfrom+',', tagto)

   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/likelytabl.txt

// Data table for likely subtags
static const struct likelyData_t
{
    const char* tagfrom;
    const char* tagto;
}
tabLikelyData[] =
{
%s
    { nullptr, nullptr }
};
""" % (tagtable))

def WriteMatchingTable(f, matchingtable, synonymtable):
   matchtable = ''
   for i in matchingtable:
       matchkey = '"%s:%s"' % (i[0], i[1])
       matchval = '%3s' % i[2]
       matchtable += '    { %-20s %s },\n' % (matchkey+',', matchval)

   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/matchingtabl.txt

// Data table for matching languages
static const struct matchData_t
{
    const char* matchkey;
    int matchval;
}
tabMatchData[] =
{
%s
    { nullptr, 0 }
};
""" % (matchtable))


def WriteRegionsTable(f, regiongrouptable, synonymtable):
   regiongrptable = ''
   for i in regiongrouptable:
       rg_language = '"%s"' % i[0]
       rg_country = '"%s"' % i[1]
       regiongrptable += '    { %-8s %s },\n' % (rg_language+',', rg_country)

   f.write("""
// The following data tables are generated by misc/languages/genlang.py
// When making changes, please put them into misc/languages/regiongrouptabl.txt

// Data table for region groups
static const struct regionGroupData_t
{
    const char* language;
    const char* country;
}
regionGroupData[] =
{
%s
    { nullptr, nullptr }
};
""" % (regiongrptable))


def ReplaceGeneratedPartOfFile(fname, func, table1, table2):
    """
        Replaces the part of file marked with the special comments with the
        output of func.

        fname is the name of the input file and func must be a function taking
        a file and language table on input and writing the appropriate chunk to
        this file, e.g. WriteEnum or WriteTable.

        table1 and table2 are data tables that are passed to func.
    """
    fin = open(fname, 'rt')
    fnameNew = fname + '.new'
    fout = open(fnameNew, 'wt')
    betweenBeginAndEnd = 0
    afterEnd = 0
    for l in fin.readlines():
        if l == '// --- --- --- generated code begins here --- --- ---\n':
            if betweenBeginAndEnd or afterEnd:
                print('Unexpected starting comment.')
            betweenBeginAndEnd = 1
            fout.write(l)
            func(fout, table1, table2)
        elif l == '// --- --- --- generated code ends here --- --- ---\n':
            if not betweenBeginAndEnd:
                print('End comment found before the starting one?')
                break

            betweenBeginAndEnd = 0
            afterEnd = 1

        if not betweenBeginAndEnd:
            fout.write(l)

    if not afterEnd:
        print('Failed to process %s.' % fname)
        os.remove(fnameNew)
        sys.exit(1)

    fout.close()
    fin.close()
    os.remove(fname)
    os.rename(fnameNew, fname)


dummytable = []
table = ReadTable()
scripttable = ReadScriptTable()
synonymtable = ReadSynonymTable()
likelytable = ReadLikelyTable()
matchingtable = ReadMatchingTable()
regiongrouptable = ReadRegionGroupTable()

ReplaceGeneratedPartOfFile('include/wx/language.h', WriteEnum, table, synonymtable)
ReplaceGeneratedPartOfFile('interface/wx/language.h', lambda f, table1, table2: WriteEnum(f, table1, table2, 'interface'), table, synonymtable)
ReplaceGeneratedPartOfFile('src/common/languageinfo.cpp', WriteTable, table, synonymtable)
ReplaceGeneratedPartOfFile('include/wx/private/lang_info.h', WriteInfoTable, table, synonymtable)
ReplaceGeneratedPartOfFile('include/wx/private/lang_scripts.h', WriteScriptTable, scripttable, dummytable)
ReplaceGeneratedPartOfFile('include/wx/private/lang_likely.h', WriteLikelyTable, likelytable, dummytable)
ReplaceGeneratedPartOfFile('include/wx/private/lang_match.h', WriteMatchingTable, matchingtable, dummytable)
ReplaceGeneratedPartOfFile('include/wx/private/lang_regions.h', WriteRegionsTable, regiongrouptable, dummytable)
