# -*- coding: utf-8 -*-

# Copyright 2008-2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

from os import path
import os
from calculate.lib.datavars import ReadonlyVariable
from calculate.lib.utils.common import (getValueFromCmdLine, getXorgConfContent,
                                        getValueFromConfig, CmdlineParams)
from calculate.lib.utils.format import XorgConfig
from collections import OrderedDict

import sys
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate

_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])

__ = getLazyLocalTranslate(_)

class Locale(object):
    # http://www.localeplanet.com/icu/
    langData = OrderedDict([
        ('be_BY', {
            'name': 'Беларуский (Беларусь)',
            'layoutname': 'Беларуская',
            'locale': 'be_BY.utf8',
            'keymap': 'by',
            'timezone': 'Europe/Minsk',
            'dumpkeys_charset': 'koi8-u',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,by',
            'language': 'be',
        }),
        ('bg_BG', {
            'name': 'Български (България)',
            'layoutname': 'Български',
            'locale': 'bg_BG.utf8',
            'keymap': 'bg_bds-utf8',
            'dumpkeys_charset': '',
            'timezone': 'Europe/Sofia',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,bg',
            'language': 'bg',
        }),
        ('bs_BA', {
            'name': 'Bosanski (Bosna i Hercegovina)',
            'layoutname': 'Bosanski',
            'locale': 'bs_BA.utf8',
            'keymap': 'slovene',
            'timezone': 'Europe/Sarajevo',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'ba',
            'language': 'bs',
        }),
        ('cs_CZ', {
            'name': 'Čeština (Česká republika)',
            'layoutname': 'Čeština',
            'locale': 'cs_CZ.utf8',
            'keymap': 'cz',
            'timezone': 'Europe/Prague',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'cz',
            'language': 'cs',
        }),
        ('da_DK', {
            'name': 'Dansk (Danmark)',
            'layoutname': 'Dansk',
            'locale': 'da_DK.utf8',
            'keymap': 'dk-latin1',
            'timezone': 'Europe/Copenhagen',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'dk',
            'language': 'da',
        }),
        ('de_DE', {
            'name': 'Deutsch (Deutschland)',
            'layoutname': 'Deutsch',
            'locale': 'de_DE.utf8',
            'keymap': 'de-latin1',
            'timezone': 'Europe/Berlin',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'de',
            'language': 'de',
        }),
        ('en_AU', {
            'name': 'English (Australia)',
            'layoutname': 'English',
            'locale': 'en_AU.utf8',
            'keymap': 'us',
            'timezone': 'Australia/Canberra',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us',
            'language': 'en_AU',
        }),
        ('en_GB', {
            'name': 'English (United Kingdom)',
            'layoutname': 'United Kingdom',
            'locale': 'en_GB.utf8',
            'keymap': 'uk',
            'timezone': 'Europe/London',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'gb',
            'language': 'en_GB',
        }),
        ('en_US', {
            'name': 'English (United States)',
            'layoutname': 'English',
            'locale': 'en_US.utf8',
            'keymap': 'us',
            'timezone': 'America/New_York',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us',
            'language': 'en',
        }),
        ('es_ES', {
            'name': 'Español (España)',
            'layoutname': 'Español',
            'locale': 'es_ES.utf8',
            'keymap': 'es euro2',
            'timezone': 'Europe/Madrid',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'es',
            'language': 'es',
        }),
        ('es_UY', {
            'name': 'Español (Uruguay)',
            'layoutname': 'Latin American',
            'locale': 'es_UY.utf8',
            'keymap': 'la-latin1',
            'timezone': 'America/Montevideo',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'latam',
            'language': 'es_UY',
        }),
        ('es_VE', {
            'name': 'Español (Venezuela)',
            'layoutname': 'Latin American',
            'locale': 'es_VE.utf8',
            'keymap': 'la-latin1',
            'timezone': 'America/Caracas',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'latam',
            'language': 'es_UY',
        }),
        ('et_EE', {
            'name': 'Eesti (Eesti)',
            'layoutname': 'Eesti',
            'locale': 'et_EE.utf8',
            'keymap': 'et',
            'timezone': 'Europe/Tallinn',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'ee',
            'language': 'et',
        }),
        ('fr_BE', {
            'name': 'Français (Belgique)',
            'layoutname': 'Belgique',
            'locale': 'fr_BE.utf8',
            'keymap': 'be-latin1',
            'timezone': 'Europe/Brussels',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'be',
            'language': 'fr',
        }),
        ('fr_CA', {
            'name': 'Français (Canada)',
            'layoutname': 'French-Canadian',
            'locale': 'fr_CA.utf8',
            'keymap': 'cf',
            'timezone': 'Canada/Pacific',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'ca',
            'language': 'fr',
        }),
        ('fr_FR', {
            'name': 'Français (France)',
            'layoutname': 'Français',
            'locale': 'fr_FR.utf8',
            'keymap': 'fr-latin9',
            'timezone': 'Europe/Paris',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'fr',
            'language': 'fr',
        }),
        ('hr_HR', {
            'name': 'Hrvatski (Hrvatska)',
            'layoutname': 'Hrvatski',
            'locale': 'hr_HR.utf8',
            'keymap': 'croat',
            'timezone': 'Europe/Zagreb',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'hr',
            'language': 'hr',
        }),
        ('is_IS', {
            'name': 'Íslenska (Ísland)',
            'layoutname': 'Íslenska',
            'locale': 'is_IS.utf8',
            'keymap': 'is-latin1',
            'timezone': 'Atlantic/Reykjavik',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'is',
            'language': 'is',
        }),
        ('it_IT', {
            'name': 'Italiano (Italia)',
            'layoutname': 'Italiano',
            'locale': 'it_IT.utf8',
            'keymap': 'it',
            'dumpkeys_charset': '',
            'timezone': 'Europe/Rome',
            'consolefont': 'ter-v14n',
            'xkblayout': 'it',
            'language': 'it',
        }),
        ('kk_KZ', {
            'name': 'Қазақ тілі (Қазақстан)',
            'layoutname': 'Қазақ тілі',
            'locale': 'kk_KZ.utf8',
            'keymap': 'kaz_gost-unicode',
            'timezone': 'Asia/Almaty',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,kz',
            'language': 'kk',
        }),
        ('lt_LT', {
            'name': 'Lietuvių (Lietuva)',
            'layoutname': 'Lietuvių',
            'locale': 'lt_LT.utf8',
            'keymap': 'lt.baltic',
            'timezone': 'Europe/Vilnius',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'lt',
            'language': 'lt',
        }),
        ('lv_LV', {
            'name': 'Latviešu (Latvija)',
            'layoutname': 'Latviešu',
            'locale': 'lv_LV.utf8',
            'keymap': 'lv',
            'timezone': 'Europe/Riga',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'lv',
            'language': 'lv',
        }),
        ('hu_HU', {
            'name': 'Magyar (Magyarország)',
            'layoutname': 'Magyar',
            'locale': 'hu_HU.utf8',
            'keymap': 'hu',
            'timezone': 'Europe/Budapest',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'hu',
            'language': 'hu',
        }),
        ('nl_BE', {
            'name': 'Nederlands (België)',
            'layoutname': 'Dutch',
            'locale': 'nl_BE.utf8',
            'keymap': 'nl',
            'timezone': 'Europe/Brussels',
            'dumpkeys_charset': '',
            'consolefont': 'lat9w-16',
            'xkblayout': 'nl',
            'language': 'nl_BE',
        }),
        ('nl_NL', {
            'name': 'Nederlands (Nederland)',
            'layoutname': 'Dutch',
            'locale': 'nl_NL.utf8',
            'keymap': 'nl',
            'timezone': 'Europe/Amsterdam',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'nl',
            'language': 'nl_NL',
        }),
        ('nb_NO', {
            'name': 'Norsk bokmål (Noreg)',
            'layoutname': 'Nynorsk',
            'locale': 'nb_NO.utf8',
            'keymap': 'no-latin1',
            'timezone': 'Europe/Oslo',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'no',
            'language': 'nb',
        }),
        ('nn_NO', {
            'name': 'Norsk nynorsk (Noreg)',
            'layoutname': 'Nynorsk',
            'locale': 'nn_NO.utf8',
            'keymap': 'no-latin1',
            'timezone': 'Europe/Oslo',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'no',
            'language': 'nn',
        }),
        ('pl_PL', {
            'name': 'Polski (Polska)',
            'layoutname': 'Polski',
            'locale': 'pl_PL.utf8',
            'keymap': 'pl',
            'timezone': 'Europe/Warsaw',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'pl',
            'language': 'pl',
        }),
        ('pt_BR', {
            'name': 'Português (Brasil)',
            'layoutname': 'Brasil',
            'locale': 'pt_BR.utf8',
            'keymap': 'br-abnt2',
            'timezone': 'Brazil/East',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'br',
            'language': 'pt_BR',
        }),
        ('pt_PT', {
            'name': 'Português (Portugal)',
            'layoutname': 'Português',
            'locale': 'pt_PT.utf8@euro',
            'keymap': 'pt-latin9',
            'dumpkeys_charset': '',
            'timezone': 'Europe/Lisbon',
            'consolefont': 'ter-v14n',
            'xkblayout': 'pt',
            'language': 'pt',
        }),
        ('ro_RO', {
            'name': 'Română (România)',
            'layoutname': 'Română',
            'locale': 'ro_RO.utf8',
            'keymap': 'ro_win',
            'timezone': 'Europe/Bucharest',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'ro',
            'language': 'ro',
        }),
        ('ru_RU', {
            'name': 'Русский (Россия)',
            'layoutname': 'Русская',
            'locale': 'ru_RU.utf8',
            'keymap': '-u ruwin_cplk-UTF-8',
            'timezone': 'Europe/Moscow',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,ru',
            'language': 'ru',
        }),
        ('sk_SK', {
            'name': 'Slovenčina (Slovenská republika)',
            'layoutname': 'Slovenčina',
            'locale': 'sk_SK.utf8',
            'keymap': 'sk-qwertz',
            'dumpkeys_charset': '',
            'timezone': 'Europe/Bratislava',
            'consolefont': 'ter-v14n',
            'xkblayout': 'sk',
            'language': 'sk',
        }),
        ('sl_SI', {
            'name': 'Slovenščina (Slovenija)',
            'layoutname': 'Slovenščina',
            'locale': 'sl_SI.utf8',
            'keymap': 'slovene',
            'timezone': 'Europe/Ljubljana',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'si',
            'language': 'sl',
        }),
        ('sq_AL', {
            'name': 'Shqip (Shqipëria)',
            'layoutname': 'Shqip',
            'locale': 'sq_AL.utf8',
            'keymap': 'al',
            'timezone': 'Europe/Tirane',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'al',
            'language': 'sq',
        }),
        ('sr_RS', {
            'name': 'Српски (Србија)',
            'layoutname': 'Српски',
            'locale': 'sr_RS.utf8',
            'keymap': 'sr-unicode',
            'timezone': 'Europe/Belgrade',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,rs',
            'language': 'sr',
        }),
        ('fi_FI', {
            'name': 'Suomi (Suomi)',
            'layoutname': 'Suomi',
            'locale': 'fi_FI.utf8',
            'keymap': 'fi',
            'timezone': 'Europe/Helsinki',
            'dumpkeys_charset': '',
            'consolefont': 'ter-v14n',
            'xkblayout': 'fi',
            'language': 'fi',
        }),
        ('sv_SE', {
            'name': 'Svenska (Sverige)',
            'layoutname': 'Svenska',
            'locale': 'sv_SE.utf8',
            'keymap': 'sv-latin1',
            'timezone': 'Europe/Stockholm',
            'dumpkeys_charset': '',
            'consolefont': 'lat0-16',
            'xkblayout': 'us,se',
            'language': 'sv',
        }),
        ('uk_UA', {
            'name': 'Українська (Україна)',
            'layoutname': 'Українська',
            'locale': 'uk_UA.utf8',
            'keymap': 'ua-utf',
            'timezone': 'Europe/Kiev',
            'dumpkeys_charset': 'koi8-u',
            'consolefont': 'ter-v14n',
            'xkblayout': 'us,ua',
            'language': 'ua',
        })
    ])

    default_lang = "en_US"

    fields = list(langData[default_lang].keys())
    
    aliases = {
        'en_AU': 'en_US',
        'es_UY': 'es_VE',
        'nl_BE': 'nl_NL',
    }

    def getLangs(self):
        return self.langData.keys()

    def getKeyboardLayouts(self):
        skip = self.aliases.keys()
        return [x for x in self.langData.keys() if x not in skip]

    def getLanguages(self):
        return map(lambda x: self.langData[x]['language'],
                   self.langData.keys())

    def isLangExists(self, lang):
        return lang in self.langData.keys()

    def isValueInFieldExists(self, field, value):
        return value in map(lambda x: self.langData[x][field],
                            self.langData.keys())

    def getFields(self, field):
        return [l[1][field] for l in self.langData.items()]

    def getFieldByLang(self, field, lang):
        return self.langData.get(lang, self.langData[self.default_lang])[field]

    def getLang(self, **kw):
        """
        Получить язык по указанному полю.

        getLang(keymap="ua-utf")
        """
        if not kw:
            raise AttributeError("getLang: field missed")
        field = next(iter(kw.keys()))
        if field not in self.fields:
            raise AttributeError("getLang: wrong field")
        return self.getLangByField(field, kw[field])

    def getKeyboardLayout(self, **kw):
        if "lang" in kw:
            if kw["lang"] in self.langData:
                lang = kw["lang"]
            else:
                lang = self.default_lang
        else:
            lang = self.getLang(**kw)
        return self.aliases.get(lang, lang)

    def __getattr__(self, attrname):
        """
        Обработка getKeymap, getXkblayout
        """
        if attrname.startswith('get'):
            fieldname = attrname[3:].lower()
            if fieldname in self.fields:
                def wrapper(fieldvalue):
                    return self.getFieldByLang(fieldname, fieldvalue)
                return wrapper
        raise AttributeError("'Locale' object has no attribute '%s'" % attrname)

    def getLang(self, **kw):
        """
        Получить язык по указанному полю.

        getLang(keymap="ua-utf")
        """
        if not kw:
            raise AttributeError("getLang: field missed")
        field = next(iter(kw.keys()))
        if field not in self.fields:
            raise AttributeError("getLang: wrong field")
        return self.getLangByField(field, kw[field])

    def __getattr__(self, attrname):
        """
        Обработка getKeymap, getXkblayout 
        """
        if attrname.startswith('get'):
            fieldname = attrname[3:].lower()
            if fieldname in self.fields:
                def wrapper(fieldvalue):
                    return self.getFieldByLang(fieldname, fieldvalue) 
                return wrapper
        raise AttributeError("'Locale' object has no attribute '%s'" % attrname)

    def getLangByField(self, field, value):
        value = value or ""
        if field == 'locale':
            value = (value,value.replace('UTF-8','utf8'))
        else:
            value = (value,)

        langs = [lang[0] for lang in self.langData.items()
                 if lang[1][field] in value]
        if not langs:
            return self.default_lang
        else:
            return langs[0]


class VariableOsClockTimezone(ReadonlyVariable):
    """
    Current timezone
    """

    def get(self):
        zoneinfodir = "/usr/share/zoneinfo/"
        timezonefile = "/etc/timezone"
        # try get timezone from kernel calculate param
        timezone = getValueFromCmdLine(
            CmdlineParams.Calculate, CmdlineParams.Timezone)
        if timezone and \
                path.exists(path.join(zoneinfodir, timezone)):
            return timezone
        # get timezone from /etc/timezone
        if path.exists(timezonefile):
            with open(timezonefile, "r") as f:
                return f.read().strip()
        return "UTC"


class VariableOsLocaleLocale(ReadonlyVariable, Locale):
    """
    Locale (example: ru_RU.utf8)
    """
    env_var = "LANG"

    def get(self):
        # получить значение локали из параметров загрузки ядра
        # выбранный язык должен присутствовать в списке поддерживаемых
        locale_val = getValueFromCmdLine(
            CmdlineParams.Calculate, CmdlineParams.Locale)
        if self.isLangExists(locale_val):
            cmd_val = self.getLocale(locale_val)
        else:
            cmd_val = None

        # получить значение из файла локали параметров окружения
        # выбранный язык должен присутствовать в списке поддерживаемых
        locale_file = '/etc/env.d/02locale'
        locale_val = getValueFromConfig(locale_file, self.env_var) or ""
        locale_val = locale_val.replace('UTF-8','utf8')
        if self.isValueInFieldExists('locale', locale_val):
            file_val = locale_val
        else:
            file_val = None

        # если параметры локали получены из параетров ядра или окружения
        if file_val or cmd_val:
            # если система настраивается во время загрузки, то приоритет
            # значения параметров загрузки ядра
            if self.GetBool('cl_system_boot_set'):
                return cmd_val or file_val
            else:
                return file_val or cmd_val
        # если значение окружения не входит с список поддерживаемых
        # то используем это значения как есть
        if self.env_var in os.environ and os.environ[self.env_var] != "C":
            return os.environ[self.env_var]
        # во всех остальных случаях вернуть локаль по умолчанию
        return self.getLocale("default")


class VariableOsLocaleKeyboardLayout(ReadonlyVariable, Locale):
    """
    Раскладка клавиатуры "притянутая" к алиасам ru_RU, en_US и т.д.
    """
    def get_xorg_xkblayout(self):
        xc = XorgConfig(getXorgConfContent())
        for x in (section.option("XkbLayout")
                  for section in xc.get_sections("InputClass")):
            if x:
                return x
        else:
            return ""

    def get(self):
        # получить значение локали из параметров загрузки ядра
        # выбранный язык должен присутствовать в списке поддерживаемых
        lang_val = getValueFromCmdLine(
            CmdlineParams.Calculate, CmdlineParams.Locale)
        if self.isLangExists(lang_val):
            cmd_keylayout = self.getKeyboardLayout(lang=lang_val)
        else:
            cmd_keylayout = None

        # получить значение из xorg.conf
        xorg_xkb = self.get_xorg_xkblayout()
        if self.isValueInFieldExists('xkblayout', xorg_xkb):
            xorg_keylayout = self.getKeyboardLayout(xkblayout=xorg_xkb)
        else:
            xorg_keylayout = None

        # получить значение из файла раскладки
        # выбранный язык должен присутствовать в списке поддерживаемых
        keymap_file = '/etc/conf.d/keymap'
        keymap_val = getValueFromConfig(keymap_file, "keymap") or ""
        if self.isValueInFieldExists('keymap', keymap_val):
            file_keylayout = self.getKeyboardLayout(keymap=keymap_val)
        else:
            file_keylayout = None

        # если параметры локали получены из параетров ядра или окружения
        if file_keylayout or cmd_keylayout or xorg_keylayout:
            # если система настраивается во время загрузки, то приоритет
            # значения параметров загрузки ядра
            if self.GetBool('cl_system_boot_set'):
                return cmd_keylayout or xorg_keylayout or file_keylayout
            else:
                return xorg_keylayout or file_keylayout or cmd_keylayout
        # во всех остальных случаях вернуть локаль по умолчанию
        return self.getKeyboardLayout(lang=self.Get('os_locale_lang'))


class VariableOsLocaleLang(ReadonlyVariable, Locale):
    """
    Language (example: ru_RU)
    """

    def get(self):
        """lang (example: ru_RU)"""
        return self.getLang(locale=self.Get('os_locale_locale'))


class VariableOsLocaleLanguage(ReadonlyVariable, Locale):
    """
    Short lang name (example: ru)
    """

    def get(self):
        return self.getLanguage(self.Get('os_locale_lang'))


class VariableOsLocaleXkb(ReadonlyVariable, Locale):
    """
    Keyboard layout for X server (example: us,ru)
    """

    def get(self):
        """xkb layouts (example: en,ru)"""
        return self.getXkblayout(self.Get('os_locale_keyboard_layout'))


class VariableOsLocaleXkbname(ReadonlyVariable, Locale):
    """
    Keyboard layout name for X server (remove value in hooks)
    """

    def get(self):
        locale_xkb = self.Get("os_locale_xkb")
        if locale_xkb:
            return locale_xkb.split("(")[0]
        return ""


class VariableOsLocaleLatSet(ReadonlyVariable, Locale):
    """
    Локаль содержит раскладку совместимую с латиницей

    Используется для отображения индикатора переключения языка
    """

    def get(self):
        """xkb layouts (example: en,ru)"""
        layout = self.Get('os_locale_xkbname').split(',')
        if len(layout) == 1:
            return self.On
        else:
            return self.Off


class VariableOsLang(ReadonlyVariable, Locale):
    """
    Supported langs
    """
    type = "list"

    def get(self):
        return self.getLangs()

    def humanReadable(self):
        return map(lambda x: self.getName(x), self.Get())

class VariableOsKeyboardLayout(ReadonlyVariable, Locale):
    """
    Supported keyboard layouts
    """
    type = "list"

    def get(self):
        return self.getKeyboardLayouts()

    def humanReadable(self):
        return map(lambda x: self.getLayoutname(x), self.Get())
