#!/usr/bin/python
"""
Locale selection tool which allows for browsing locales
"""

import subprocess
import glob
import os
import sys
import gettext
import signal
from collections import defaultdict


BACKEND = '/usr/sbin/translations-enus1'


def popen(cmd):
    return subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout


def get_status():
    try:
        return map(int, popen([BACKEND, 'status']))
    except OSError, e:
        print >> sys.stderr, BACKEND + ':', e
        # XXX debug code for when I don't have the backend installed
        return 'unknown', 'unknown'


def run_action(action):
    subprocess.call(['sudo', BACKEND, action])


def zenity(args):
    return popen(['zenity'] + args).read()


def parse_mo(filename):
    try:
        return gettext.GNUTranslations(open(filename, 'rb'))._catalog
    except LookupError, e:
        # e.g. LookupError: unknown encoding: iso-8859-9e
        print >> sys.stderr, filename + ':', e
        return {}


class NullProgressBar(object):

    def start(self):
        pass

    def update(self, cur, total):
        pass

    def done(self):
        pass


class ZenityProgressBar(NullProgressBar):

    def __init__(self, title='Processing locales', text='Please wait'):
        self.title = title
        self.text = text

    def start(self):
        self.pipe = subprocess.Popen(['zenity', '--progress',
                                      '--title', self.title,
                                      '--text', self.text],
                                    stdin=subprocess.PIPE)

    def update(self, cur, total):
        percentage = cur * 100 / max(1, total)
        self.pipe.stdin.write('%d\n' % percentage)
        self.pipe.stdin.flush()

    def done(self):
        self.pipe.stdin.close()
        os.kill(self.pipe.pid, signal.SIGTERM)


class LocaleInfo(object):

    def __init__(self, progress=NullProgressBar()):
        self.locales = []
        self.locale_to_domain_to_key_to_value = defaultdict(lambda: defaultdict(dict))
        self.key_to_domain_to_locale = defaultdict(lambda: defaultdict(dict))
        self.value_to_key_domain_locale = defaultdict(dict)
        self.progress = progress

    def load(self):
        localedirs = sorted(glob.glob('/usr/share/locale/*/LC_MESSAGES'))
        self.progress.start()
        for n, localedir in enumerate(localedirs):
            self.progress.update(n, len(localedirs))
            locale = os.path.basename(os.path.dirname(localedir))
            self.locales.append(locale)
            mo_files = glob.glob(os.path.join(localedir, '*.mo'))
            for m, mo in enumerate(mo_files):
                self.progress.update(n + m * 1.0 / len(mo_files),
                                     len(localedirs))
                domain = os.path.splitext(os.path.basename(mo))[0]
                for key, value in parse_mo(mo).items():
                    if not key:
                        continue
                    # XXX key can be a tuple -- plural forms!
                    if isinstance(key, tuple):
                        key = '%s [%d]' % key
                    self.key_to_domain_to_locale[key][domain][locale] = value
                    self.locale_to_domain_to_key_to_value[locale][domain][key] = value
                    self.value_to_key_domain_locale[value][key + domain + locale] = (key, domain, locale)
        self.progress.done()


def warning(text):
    zenity(['--warning', '--text', text])


def getinput(title, prompt):
    return zenity(['--entry', '--title', title, '--text', prompt]).strip()


def choice(title, prompt, *args):
    return zenity(['--list', '--title', title, '--text=', '--hide-column=1',
                   '--column=', '--column', prompt] + list(args)).strip()


def ask_preferred_source():
    old, new = get_status()
    args = ['--list',
            '--title=Select preferred source for English',
            '--text=',
            '--column=Provider',
            'Default' + (' (current)' if new == 0 else ''),
            'Native Spealer' + (' (current)' if old == 0 else ''),
            'Advanced options']
    if new and old:
        args += ['Mixed (Default: %s, Native: %s)' % (old, new)]
    return zenity(args).strip()


def main():
    provider = ask_preferred_source()
    if not provider:
        return

    if provider == 'Native Speaker':
        run_action('install')
    elif provider == 'Default':
        run_action('uninstall')
    elif provider == 'Advanced options':
        locale_info = LocaleInfo(ZenityProgressBar())
        locale_info.load()
        transtools(locale_info)


actions = defaultdict(lambda: lambda locale_info: None)


def action(fn):
    actions[fn.__name__] = fn
    return fn


@action
def searchstrings(locale_info):
    text = getinput("Search Localization Messages", "Enter substring")
    if not text:
        return
    matches = sorted([(value, key_domain_locale)
                      for value, key_domain_locale in
                            locale_info.value_to_key_domain_locale.items()
                      if text in value])
    results = []
    for value, key_domain_locale in matches:
        for (key, domain, locale) in sorted(key_domain_locale.values()):
            results += [locale, domain, key, value]
    zenity(['--list', '--title=Translation Search',
            '--text=For: ' + text,
            '--column=Locale', '--column=Domain', '--column=ID',
            '--column=Value'] + results)


@action
def searchids(locale_info):
    text = getinput("Search Localization IDs", "Enter substring")
    if not text:
        return
    matches = sorted((key, domain_to_locale_to_value)
                     for key, domain_to_locale_to_value in
                            locale_info.key_to_domain_to_locale.items()
                     if text in key)
    results = []
    for key, domain_to_locale_to_value in matches:
        for domain, locale_to_value in sorted(domain_to_locale_to_value.items()):
            for locale, value in sorted(locale_to_value.items()):
                results += [locale, domain, key, value]
    zenity(['--list', '--title=ID Search',
            '--text=For: ' + text,
            '--column=Locale', '--column=Domain', '--column=ID',
            '--column=Value'] + results)


@action
def lookup(locale_info):
    value = getinput("Retrieve identifier", "Enter user interface string")
    if not value:
        return
    if value not in locale_info.value_to_key_domain_locale:
        warning("No matching string")
        return
    results = []
    key_domain_locale = locale_info.value_to_key_domain_locale[value]
    for (key, domain, locale) in sorted(key_domain_locale.values()):
        results += [locale, domain, key]
    zenity(['--list', '--title=Matching identifiers',
            '--text=For: ' + value,
            '--column=Locale', '--column=Domain', '--column=ID',
            ] + results)


@action
def view(locale_info):
    key = getinput("Review identifier translations", "Enter identifier")
    if not key:
        return
    if key not in locale_info.key_to_domain_to_locale:
        warning("No such identifier")
        return
    results = []
    key_domain_locale = locale_info.key_to_domain_to_locale[key]
    for domain, locale_to_value in sorted(key_domain_locale.items()):
        for locale, value in sorted(locale_to_value.items()):
            results += [locale, domain, value]
    zenity(['--list', '--title=Matching strings',
            '--text=For: ' + key,
            '--column=Locale', '--column=Domain', '--column=Value',
            '--column=Value'] + results)


@action
def browse(locale_info):
    locales = sorted(locale_info.locale_to_domain_to_key_to_value)
    while True:
        locale = zenity(['--list', '--title=Select locale',
                         '--text=', '--column=Locale'] + locales).strip()
        if not locale:
            break
        domains = sorted(locale_info.locale_to_domain_to_key_to_value[locale])
        while True:
            domain = zenity(['--list', '--title=Select domain',
                             '--text=Locale (%s)' % locale,
                             '--column=Domain'] + domains).strip()
            if not domain:
                break
            key_to_value = locale_info.locale_to_domain_to_key_to_value[locale][domain]
            results = []
            for key, value in sorted(key_to_value.items()):
                results += [key, value]
            zenity(['--list', '--title=Domain: %s' % domain,
                    '--text=Locale: %s' % locale,
                    '--column=ID', '--column=Value'] + results)


def transtools(locale_info):

    while True:
        action = choice("Translator Tool", "What would you like to do?",
                        "searchstrings", "Search within translations",
                        "searchids", "Search within identifiers",
                        "lookup", "Lookup identifier",
                        "view", "View identifier translations",
                        "browse", "Browse translations")
        if not action:
            return
        actions[action](locale_info)


if __name__ == '__main__':
    main()
