#!/usr/bin/env python
# Version: MPL 1.1 / GPLv3+ / LGPLv3+
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License or as specified alternatively below. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Initial Developer of the Original Code is
#       Miklos Vajna <vmiklos@frugalware.org>
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
# Major Contributor(s):
#
# For minor contributions see the git repository.
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 3 or later (the "GPLv3+"), or
# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
# instead of those above.

import getopt, sys, os, re

class Options:
    """Options of this script."""

    def __init__(self):
        self.input = None
        self.output = None
        self.language = None
        self.template = None

class Entry:
    """Represents a single line in an SDF file."""

    def __init__(self, items):
        self.items = items # list of 15 fields
        path = self.items[1].split('\\')
        self.po = "%s/%s/%s.po" % (options.input.replace('\\', '/'), self.items[0], "/".join(path[:-1]))
        prefix = ""
        if len(self.items[5]):
            prefix += "%s." % self.items[5]
        if len(self.items[3]):
            prefix += "%s." % self.items[3]
        self.keys = []
        # 10..13 are translation types
        for idx in [10,12,13]:
            try:
                if len(self.items[idx]):
                    t = {10:'text', 12:'quickhelptext', 13:'title'}[idx]
                    self.keys.append((idx, self.sdf2po("%s#%s.%s%s" % (path[-1], self.items[4], prefix, t))))
            except IndexError:
                print("Tried to access field #%d in sdf file, but no such column! Broken sdf file?" % idx)
                print("Fields: %s" % self.items)
                sys.exit(1)

    def translate(self, translations):
        """Translates text in the entry based on translations."""

        self.items[9] = options.language
        for idx, key in self.keys:
            try:
                self.items[idx] = translations.data[(self.po, key)]

                self.items[14] = "2002-02-02 02:02:02"
            except KeyError:
                pass
        self.items[14] = self.items[14].strip()

    def sdf2po(self, s):
        """Escapes special chars in po key names."""

        return s.translate(normalizetable)

class Template:
    """Represents a reference template in SDF format."""

    def __init__(self, path):
        sock = xopen(path, "r", encoding='utf-8')
        self.lines = []
        for line in sock:
            entry = Entry(line.split('\t'))
            if os.path.exists(entry.po):
                self.lines.append(entry)

    def translate(self, translations):
        """Translates entires in the template based on translations."""

        sock = xopen(options.output, "w", encoding='utf-8')
        for line in self.lines:
            line.translate(translations)
            sock.write("\t".join(line.items)+"\r\n")
        sock.close()

class Translations:
    """Represents a set of .po files, containing translations."""

    def __init__(self):
        self.data = {}
        for root, dirs, files in os.walk(options.input):
            for file in files:
                path = "%s/%s" % (root, file)
                sock = xopen(path, "r", encoding='utf-8')
                key = None
                buf = []
                multiline = False
                fuzzy = False
                for line in sock:
                    if line.startswith("#: "):
                        key = line.strip()[3:]
                    elif line.startswith("#, fuzzy"):
                        fuzzy = True
                    elif line.startswith("msgstr "):
                        trans = line.strip()[8:-1]
                        if len(trans):
                            if fuzzy:
                                fuzzy = False
                            else:
                                self.setdata(path, key, trans)
                                multiline = False
                        else:
                            buf = []
                            buf.append(trans)
                            multiline = True
                    elif multiline and line.startswith('"'):
                        buf.append(line.strip()[1:-1])
                    elif multiline and not len(line.strip()) and len("".join(buf)):
                        if fuzzy:
                            fuzzy = False
                        else:
                            self.setdata(path, key, "".join(buf))
                        buf = []
                        multiline = False
                if multiline and len("".join(buf)) and not fuzzy:
                    self.setdata(path, key, "".join(buf))

    def setdata(self, path, key, s):
        """Sets the translation for a given path and key, handling (un)escaping
        as well."""
        if key:
            # unescape the po special chars
            s = s.replace('\\"', '"')
            if key.split('#')[0].endswith(".xhp"):
                s = self.escape_help_text(s)
            else:
                s = s.replace('\\\\', '\\')
            self.data[(path.replace('\\', '/'), key)] = s

    def escape_help_text(self, text):
        """Escapes the help text as it would be in an SDF file."""

        for tag in helptagre.findall(text):
            # <, >, " are only escaped in <[[:lower:]]> tags. Some HTML tags make it in in
            # lowercase so those are dealt with. Some LibreOffice help tags are not
            # escaped.
            escapethistag = False
            for escape_tag in ["ahelp", "link", "item", "emph", "defaultinline", "switchinline", "caseinline", "variable", "bookmark_value", "image", "embedvar", "alt"]:
                if tag.startswith("<%s" % escape_tag) or tag == "</%s>" % escape_tag:
                    escapethistag = True
            if tag in ["<br/>", "<help-id-missing/>"]:
                escapethistag = True
            if escapethistag:
                escaped_tag = ("\\<" + tag[1:-1] + "\\>").replace('"', '\\"')
                text = text.replace(tag, escaped_tag)
        return text

def xopen(path, mode, encoding):
    """Wrapper around open() to support both python2 and python3."""
    if sys.version_info >= (3,):
        return open(path, mode, encoding=encoding)
    else:
        return open(path, mode)

def main():
    """Main function of this script."""

    opts, args = getopt.getopt(sys.argv[1:], "si:o:l:t:", ["skipsource", "input=", "output=", "language=", "template="])
    for opt, arg in opts:
        if opt in ("-s", "--skipsource"):
            pass
        elif opt in ("-i", "--input"):
            options.input = arg
        elif opt in ("-o", "--output"):
            options.output = arg
        elif opt in ("-l", "--language"):
            options.language = arg
        elif opt in ("-t", "--template"):
            options.template = arg
    template = Template(options.template)
    translations = Translations()
    template.translate(translations)

# used by ecape_help_text
helptagre = re.compile('''<[/]??[a-z_\-]+?(?:| +[a-z]+?=".*?") *[/]??>''')

options = Options()

# used by sdf2po()
normalfilenamechars = "/#.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
normalizetable = ""
for i in map(chr, list(range(256))):
    if i in normalfilenamechars:
        normalizetable += i
    else:
        normalizetable += "_"

if __name__ == "__main__":
    main()

# vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab:
