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

# Copyright 2008-2013 Calculate Ltd. 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.

import os
import sys
from os import path
from itertools import *
from calculate.lib.datavars import (TableVariable,Variable,ReadonlyVariable,
                                   VariableError)
import calculate.lib.cl_template as cl_template
from calculate.lib.utils.files import listDirectory,process,PIPE,pathJoin

from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__])

_envData = [('system', '/etc/calculate/calculate.env'),
            ('local', '/var/calculate/calculate.env'),
            ('remote', '/var/calculate/remote/calculate.env')]

class VariableClEnvData(TableVariable):
    """
    Aliases and path to ini files
    """
    source = ["cl_env_location","cl_env_path"]

class VariableClEnvLocation(ReadonlyVariable):
    """
    Aliases to ini files (from cl_env_data)
    """
    type = 'list'
    value = list(zip(*_envData)[0])

class VariableClEnvPath(Variable):
    """
    Path to ini files (from cl_env_data)
    """
    type = 'list'
    value = list(zip(*_envData)[1])

class VariableClTemplateData(TableVariable):
    """
    Aliases to templates path
    """
    source = ["cl_template_location","cl_template_path"]

class VariableClTemplateLocation(Variable):
    """
    Name to templates
    """
    type = 'list'
    value = ["overlay","local","remote"]


class VariableClTemplatePath(Variable):
    """
    Path to information file on server
    """
    type = 'list'
    value = ["/var/lib/layman/calculate/profiles/templates",
             "/var/calculate/templates",
             "/var/calculate/remote/templates"]

class VariableClEnvServerPath(ReadonlyVariable):
    """
    Path to server environment
    """
    value = '/var/calculate/remote/server.env'

class VariableClTemplateCltPath(ReadonlyVariable):
    """
    Paths to clt-template files
    """
    type = 'list'

    def get(self):
        """
        Clt templates path is /etc and CONFIG_PROTECT
        """
        chroot = self.Get('cl_chroot_path')
        if "CONFIG_PROTECT" in os.environ:
            protectPaths = ["/etc"] + filter(lambda x: x.strip(),
                                      os.environ["CONFIG_PROTECT"].split(" "))
        else:
            protectPaths = ["/etc", "/usr/share/X11/xkb", "var/lib/hsqldb",
                            "/usr/share/config"]
        return filter(path.exists, 
               map(lambda x:pathJoin(chroot,x),
               protectPaths))

class VariableClRootPath(Variable):
    """
    Path to directory relative which perform joining templates to system files
    (sandbox)
    """
    value = '/'

class VariableClRootPathNext(Variable):
    """
    Path to directory relative which perform joining templates to system files
    (sandbox). This set for configure packages specified throug merge=
    """
    def get(self):
        return self.Get('cl_root_path')

class VariableClChrootPath(ReadonlyVariable):
    """
    Path to directory which contain other system
    """
    value = '/'

class VariableClPassStep(Variable):
    """
    Pass for templates join 1,2,3,4,5 and etc
    """

class VariableClPassFile(Variable):
    """
    Template file performed at now
    """

class VariableClPassLocation(Variable):
    """
    Текущая обрабатываемая локация
    """
    value = ""

class VariableClSetupSkipProfile(Variable):
    """
    Список пакетов и местоположения шаблонов пропускаемых
    при настройке профиля
    """
    type = "table"
    value = [[]]

class VariableClSetupSkipPatch(Variable):
    """
    Список пакетов и местоположения шаблонов пропускаемых
    при выполнении патчей
    """
    type = "table"
    value = [[]]

class VariableClSetupSkipMerge(Variable):
    """
    Список пакетов и местоположения шаблонов пропускаемых
    при настройке пакетов системы
    """
    type = "table"
    value = [[]]

class VariableClMergeSet(ReadonlyVariable):
    """
    Force by package template appling
    """
    type = "bool"
    value = "off"

class VariableClMergePkg(ReadonlyVariable):
    """
    This variable work with template function belong(package_name)
    if the variable is defined then will use only template, which 
    has package_name in belong equal value of this variable or
    hasn't belong function
    (package_name == value of cl_belong_pkg)
    """
    type = "list"
    def get(self):
        return self.Get('cl_belong_pkg')

class VariableClMergePkgNew(ReadonlyVariable):
    """
    New variable value for cl_merge_pkg
    """
    type = "list"

class VariableClMergePkgPass(ReadonlyVariable):
    """
    Performed packages
    """
    type = "list"

class VariableClBelongPkg(ReadonlyVariable):
    """
    Depricated by cl_merge_pkg
    """
    type = "list"

class VariableClAction(ReadonlyVariable):
    """
    Program action
    Example: install, uninstall, merge, domain, undomain, system, desktop
    """

class VariableClPassState(ReadonlyVariable):
    """
    Program state
    specifies addition to cl_pass_action for needing
    """

class VariableClMerges(Variable):
    """
    Programs have templates setup
    """
    type = 'list'

    def get(self):
        return []

    def set(self,value):
        return []

class VariableClAutoupdateSet(Variable):
    """
    (on or off) autoupdate config from install program
    """
    type = 'bool'

    def get(self):
        if self.Get('cl_dispatch_conf') == 'usenew':
            return "on"
        else:
            return "off"

class VariableClHumanEditSet(Variable):
    """
    Параметр для отметки "ручной правки"
    """
    type = "bool"
    opt = ["--human-edit"]
    value = "off"

    def init(self):
        self.help = _("mark as human modification")
        self.label = _("Mark as human modification")

class VariableClProtectUseSet(ReadonlyVariable):
    """
    Использовать portage интеграцию с конфигурационными файлами
    """
    type = "bool"

    def get(self):
        return "on" if self.Get('cl_human_edit_set') == 'off' else "off"

class VariableClDispatchConf(Variable):
    """

    """
    type = "choice"
    opt = ["--conf"]
    value = "dispatch"
    syntax = "--{choice}-conf"
    metavalue = "METHOD"

    def init(self):
        self.help = "'usenew' - " +_("use the new config files") +\
                    ",\n'skip' - " + _("skip the update of config files") +\
                    ",\n'dispatch' - " + _("manually update config files")
        self.label = _("Method for updating config files")

    def choice(self):
        return [("usenew",_("Use the new config files")),
                ("skip",_("Skip the update of config files")),
                ("dispatch",_("Manually update config files"))]

class VariableClWsdl(Variable):
    """
    Packages with wsdl
    """
    type = "list-choice"

    def choice(self):
        return self.Get('cl_wsdl_available')

class VariableClWsdlAvailable(ReadonlyVariable):
    """
    Packages which has wsdl interfaces
    """
    type = "list"
    
    def get(self):
        sitePackages = map(lambda x:path.join(x,"calculate"),
                       filter(lambda x:x.endswith('site-packages') and \
                                       x.startswith('/usr/lib'),sys.path))
        retList = []
        for module,modDir in chain(*map(lambda x:map(lambda y:(path.basename(y),y),
                             listDirectory(x,True,True)),sitePackages)):
            if path.exists(path.join(modDir,"wsdl_%s.py"%module)):
                if not "calculate-%s"%module in retList:
                    retList.append("calculate-%s"%module)
        return retList

class VariableClConfigProtectMask(ReadonlyVariable):
    """
    Value of CONFIG_PROTECT after source /etc/profile, and /etc append
    """
    type = "list"

    def get(self):
        displayEnv = process('/bin/bash',"-c","source /etc/profile;env",
                             stdout=PIPE)
        for line in displayEnv:
            if line.startswith("CONFIG_PROTECT_MASK="):
                configProtectMask=line.rstrip().partition('=')[2].split()
                break
        else:
            configProtectMask = []
        return configProtectMask

class VariableClConfigProtect(ReadonlyVariable):
    """
    Value of CONFIG_PROTECT after source /etc/profile, and /etc append
    """
    type = "list"

    def get(self):
        displayEnv = process('/bin/bash',"-c","source /etc/profile;env",
                             stdout=PIPE)
        for line in displayEnv:
            if line.startswith("CONFIG_PROTECT="):
                configProtect=line.rstrip().partition('=')[2].split()
                break
        else:
            configProtect = []
        configProtect.append('/etc')
        return configProtect


class VariableClVerboseSet(Variable):
    """
    Verbose output variable
    """
    type = "bool"
    opt = ["-v", "--verbose"]
    value = "off"

    def init(self):
        self.help = _("verbose output")
        self.label = _("Verbose output")

class VariableClEbuildPhase(ReadonlyVariable):
    """
    Current ebuild phase
    """
    def get(self):
        return os.environ.get("EBUILD_PHASE","")

class VariableClEmergeInfo(ReadonlyVariable):
    """
    Emerge --info cache
    """
    type = "list"

    def get(self):
        return filter(lambda x:x.startswith('PORTDIR_OVERLAY=') or \
                               x.startswith('EMERGE') or \
                               x.startswith('PKGDIR=') or \
                               x.startswith('PORTDIR='),
               process("/usr/bin/emerge","--ask=n","--info",
                       envdict=os.environ).read().split('\n'))

class VariableClPkgdir(ReadonlyVariable):
    """
    PKGDIR
    """
    def get(self):
        emergeInfo = self.Get('cl_emerge_info')
        for line in filter(lambda x:x.startswith("PKGDIR="),
                           emergeInfo):
            return line.partition("=")[2].strip('\n"\'')
        return ""

class VariableClPortdir(ReadonlyVariable):
    """
    PORTDIR
    """
    def get(self):
        emergeInfo = self.Get('cl_emerge_info')
        for line in filter(lambda x:x.startswith("PORTDIR="),
                           emergeInfo):
            return line.partition("=")[2].strip('\n"\'')
        return ""

class VariableClPortdirOverlay(ReadonlyVariable):
    """
    Overlays path
    """
    type = "list"

    def get(self):
        emergeInfo = self.Get('cl_emerge_info')
        for line in filter(lambda x:x.startswith("PORTDIR_OVERLAY="),
                    emergeInfo):
            return filter(None,
                   line.partition("=")[2].strip('\n"\'').split(' '))
        return []

class VariableClEmergeDefaultOpts(ReadonlyVariable):
    """
    EMERGE_DEFAULT_OPTS
    """
    type = "list"

    def get(self):
        emergeInfo = self.Get('cl_emerge_info')
        for line in filter(lambda x:x.startswith("EMERGE_DEFAULT_OPTS="),
                           emergeInfo):
            return filter(None,
                          line.partition("=")[2].strip('\n"\'').split(' '))
        return []

class VariableClTemplatesLocate(Variable):
    """
    Выбранные типы хранилищ шаблонов
    """
    type = "choice-list"
    #value = ['overlay','local','remote','clt']
    element = "selecttable"
    opt = ["-T","--templates"]
    metavalue = "TEMPLATES"
    untrusted = True

    descriptionMap = {'overlay': _('Overlay templates'),
                      'local': _('Local templates'),
                      'distro': _('Distribution templates'),
                      'remote': _('Remote templates'),
                      'clt': _('clt templates')}

    def init(self):
        self.label = _("Templates location")
        self.help = _("select location for templates %s") \
                      %",".join(self.get())

    def get(self):
        vals = \
            self.Get('cl_template_location')[:len(self.Get('cl_template_path'))]
        if self.Get('cl_action') == 'desktop':
            return vals
        else:
            return vals + ['clt']

    def choice(self):
        return map(lambda x:(x,self.descriptionMap.get(x,x)),
               self.get())

class VariableClTemplatePathUse(ReadonlyVariable):
    """
    Пути до шаблонов, используемых в настройке системы или профиля пользователя
    """
    type = 'list'

    def get(self):
        return self.Select('cl_template_path',where='cl_template_location',
                          _in=self.Get('cl_templates_locate'))

class VariableClTemplateCltSet(ReadonlyVariable):
    """
    Использовать clt шаблоны для настройки системы
    """
    def get(self):
        return "on" if "clt" in self.Get('cl_templates_locate') else "off"

class VariableClEnvDebugSet(Variable):
    """
    Переменная для включения отладки
    """
    type = "bool"
    value = "on"

class VariableClMakeProfile(Variable):
    """
    Путь до актуального make.profile
    """
    systemRoot = "/"

    def get_work_link(self, *links):
        for link in links:
            if path.exists(link):
                return link

    def get(self):
        files = ["etc/portage/make.profile", "etc/make.profile"]
        val = self.get_work_link(
            *[path.join(self.systemRoot, x) for x in files])
        if not val:
            raise VariableError(
                _("Failed to detect the system profile.") + " " +
                _("Select the profile with command {cmd}").format(
                    cmd="cl-update-profile"))
        return val
