# -*- 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.

import sys
import os
from os import path
import re
import platform
from calculate.lib.datavars import (Variable, SimpleDataVars, ReadonlyVariable,
                                    VariableError, VariableInterface)

from calculate.lib.utils.portage import (isPkgInstalled, searchProfile,
                                         RepositorySubstituting, RepositoryPath)
from calculate.lib.utils.files import readFile, pathJoin
from calculate.lib.configparser import ConfigParser
from calculate.lib.utils.tools import unique, drop_slash
import env

_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate

setLocalTranslate('cl_lib3', sys.modules[__name__])


class Linux(VariableInterface):
    dictLinuxName = {"CLD": "Calculate Linux Desktop",
                     "CLDX": "Calculate Linux Desktop",
                     "CLDG": "Calculate Linux Desktop",
                     "CDS": "Calculate Directory Server",
                     "CLS": "Calculate Linux Scratch",
                     "CSS": "Calculate Scratch Server",
                     "CMC": "Calculate Media Center",
                     "Gentoo": "Gentoo"}

    dictLinuxSubName = {"CLD": "KDE", "CLDX": "XFCE", "CLDG": "GNOME"}

    dictNameSystem = {'CDS': 'server',
                      'CLD': 'desktop',
                      'CLDG': 'desktop',
                      'CLDX': 'desktop',
                      'CLS': 'desktop',
                      'CMC': 'desktop',
                      'CSS': 'server'}

    def getFullNameByShort(self, shortname):
        """Get full distributive name by short name"""
        subname = self.dictLinuxSubName.get(shortname, '')
        if subname:
            subname = " %s" % subname
        return "%s%s" % (self.dictLinuxName.get(shortname, ''), subname)

    def getShortnameByMakeprofile(self, systemroot):
        """Get shortname by symlink of make.profile"""
        makeprofile = self.Get('cl_make_profile')
        if path.exists(makeprofile):
            link = os.readlink(makeprofile)
            re_make_profile_link = re.compile(
                '/calculate/(desktop|server)/(%s)/' %
                "|".join(self.dictLinuxName.keys()), re.S)
            shortname_search = re_make_profile_link.search(link)
            if shortname_search:
                return shortname_search.groups()[1]
        return None

    def getShortnameByIni(self, systemroot):
        """Get shortname by calculate.ini file"""
        inifile = path.join(systemroot, 'etc/calculate/calculate.ini')
        if path.exists(inifile):
            with open(inifile) as f:
                data = f.readlins()
            short_name_list = filter(
                None, map(lambda x: (len(x.split("=")) == 2 and
                                     x.split("=")[0] == "calculate" and
                                     x.split("=")[1].strip()), data))
            if short_name_list:
                return short_name_list[0]

    def detectOtherShortname(self, systemroot):
        """Detect other system. Now only Gentoo."""
        gentoo_file = path.join(systemroot, "etc/gentoo-release")
        if path.exists(gentoo_file):
            return "Gentoo"
        if all(map(lambda x: path.lexists(path.join(systemroot, x)),
                   ['bin', 'var', 'lib', 'etc'])):
            return "Linux"
        return None

    def getVersionFromOverlay(self, systemroot):
        """
        Get version from overlay calculate-release
        """
        overlay_path = drop_slash(RepositoryPath.Calculate)
        release_file = 'profiles/calculate-release'
        return readFile(path.join(systemroot, overlay_path, release_file))

    def getVersionFromMetapackage(self, systemroot, shortname):
        """Get version from meta package"""
        for pkg in ("app-misc/calculate-meta", "app-misc/%s-meta" % shortname):
            calc_meta = isPkgInstalled(pkg, systemroot)
            if calc_meta:
                return calc_meta[0]['PV']
        return None

    def getVersionFromCalculateIni(self, systemroot):
        """Get version from calculate ini"""
        pathname = path.join(systemroot,
                             'etc/calculate/calculate.ini')
        if path.exists(pathname):
            with open(pathname) as f:
                data = f.readlines()
            ver_list = filter(
                None, map(lambda x:
                          (len(x.split("=")) == 2 and
                           x.split("=")[0] == "linuxver" and
                           x.split("=")[1].strip()), data))
            if ver_list:
                re_ver = re.compile("^(\d+\.)*\d$", re.S)
                re_res = filter(re_ver.search, ver_list)
                if re_res:
                    return re_res[0]

    def getVersionFromGentooFiles(self, systemroot):
        """Get version from gentoo files"""
        gentoo_file = path.join(systemroot, "etc/gentoo-release")
        re_ver = re.compile("^(\d+\.)*\d+$", re.S)
        if path.exists(gentoo_file):
            gentoo_link = self.Get('cl_make_profile')
            if path.islink(gentoo_link):
                vers = filter(re_ver.search,
                              os.readlink(gentoo_link).split('/'))
                if vers:
                    return vers[-1]

    def getVersionFromUname(self):
        """Get version from uname"""
        re_ver = re.search("^(\d+\.)*\d", platform.release(), re.S)
        if re_ver:
            return re_ver.group()


class VariableOsLinuxShortname(Variable, Linux):
    """
    Short system name (Example:CLD)
    """
    systemRoot = "/"

    def get(self):
        return (self.getShortnameByMakeprofile(self.systemRoot) or
                self.getShortnameByIni(self.systemRoot) or
                self.detectOtherShortname(self.systemRoot) or
                "Linux")


class VariableOsLinuxName(Variable, Linux):
    """
    Full system name
    """
    source_variable = "os_linux_shortname"

    def get(self):
        linux_short_name = self.Get(self.source_variable)
        return self.dictLinuxName.get(linux_short_name, "Linux")


class VariableOsLinuxSubname(Variable, Linux):
    """
    Subname of linux (KDE, GNOME, XFCE and etc)
    """
    source_variable = "os_linux_shortname"

    def get(self):
        linux_short_name = self.Get(self.source_variable)
        return self.dictLinuxSubName.get(linux_short_name, "")


class VariableOsLinuxSystem(Variable, Linux):
    """
    System of linux (desktop or server)
    """
    source_variable = "os_linux_shortname"

    def get(self):
        short_name = self.Get(self.source_variable)
        return self.dictNameSystem.get(short_name, "")


class VariableOsLinuxVer(Variable, Linux):
    """
    Version of system (get by metapackage,calculate.ini,gentoo files or 0)
    """
    systemRoot = "/"

    def get(self):
        """Get system version"""
        linux_short_name = self.Get("os_linux_shortname")
        return (self.getVersionFromOverlay(self.systemRoot) or
                self.getVersionFromMetapackage(
                    self.systemRoot, linux_short_name) or
                self.getVersionFromCalculateIni(self.systemRoot) or
                self.getVersionFromGentooFiles(self.systemRoot) or
                self.getVersionFromUname() or "0")


class VariableOsLinuxBuild(Variable, Linux):
    """
    Build of system
    """


class VariableOsLinuxFilesnum(Variable, Linux):
    systemRoot = "/"

    def get(self):
        """Depricated variable which contains count files.
        Get files count. Count files discard because it to long
        operation. This value is already count by calculate-builder and
        is placed in calculate3.env"""
        return str(0)
        # return str(countFiles(self.systemRoot))


class VariableOsLinuxFiles(Variable, Linux):
    systemRoot = "/"

    def get(self):
        """Get files count. Count files discard because it to long
        operation. This value is already count by calculate-builder and
        is placed in calculate.env"""
        return self.Get('os_linux_filesnum')
        # return str(countFiles(self.systemRoot))


class VariableOsLinuxPkglist(Variable):
    type = "list"

    def generate_shortnames(self, make_profile):
        repos = RepositorySubstituting(self)
        for fn in searchProfile(make_profile, "calculate.env",
                                repository_sub=repos):
            config = ConfigParser(strict=False)
            config.read(fn, encoding="utf-8")
            value = SimpleDataVars.unserialize("string",
                                               config.get('main',
                                                          'os_linux_shortname',
                                                          raw=True,
                                                          fallback=''))
            yield value.encode('utf-8')

    def get(self):
        make_profile = self.Get('cl_make_profile')
        if path.exists(make_profile):
            return list(unique(filter(None,
                                   self.generate_shortnames(make_profile))))
        else:
            return []


class VariableClProfileSystem(ReadonlyVariable):
    """
    Профиль системы (симлинк /etc/make.profile')
    """

    def get(self):
        try:
            make_profile = self.Get('cl_make_profile')
            return path.normpath(
                path.join(path.dirname(make_profile),
                          os.readlink(make_profile)))
        except:
            raise VariableError(_("Failed to determine the system profile"))


class VariableClProfileName(ReadonlyVariable):
    source_var = "cl_profile_system"

    def get(self):
        make_profile = self.Get(self.source_var)
        distro, o, name = make_profile.partition('/profiles/')
        if not o:
            return make_profile
        distro = path.basename(distro)
        return "%s:%s" % (distro, name)


class LinuxDataVars(SimpleDataVars):
    class VariableOsArchMachine(ReadonlyVariable):
        systemRoot = '/'

        def get(self):
            if path.exists(path.join(self.systemRoot, 'lib64')):
                return 'x86_64'
            else:
                return 'i686'

    def variables(self):
        os_arch_machine = self.VariableOsArchMachine(systemRoot=self.systemRoot)

        return [env.VariableClMakeProfile(systemRoot=self.systemRoot),
                VariableClProfileName(systemRoot=self.systemRoot),
                VariableClProfileSystem(systemRoot=self.systemRoot),
                env.VariableClChrootPath(),
                os_arch_machine,
                VariableOsLinuxShortname(systemRoot=self.systemRoot),
                VariableOsLinuxName(systemRoot=self.systemRoot),
                VariableOsLinuxSubname(systemRoot=self.systemRoot),
                VariableOsLinuxFilesnum(systemRoot=self.systemRoot),
                env.VariableClEmergeConfig(systemRoot=self.systemRoot),
                VariableOsLinuxFiles(systemRoot=self.systemRoot),
                VariableOsLinuxSystem(systemRoot=self.systemRoot),
                VariableOsLinuxVer(systemRoot=self.systemRoot),
                VariableOsLinuxBuild(systemRoot=self.systemRoot)]

    def __init__(self, systemRoot="/", cache=None):
        self.systemRoot = systemRoot
        SimpleDataVars.__init__(self, *self.variables())
        self.init_variables()
        if cache is None:
            makeprofile = self.Get('cl_make_profile')
            if os.path.exists(makeprofile):
                inifile = path.join(systemRoot, os.path.dirname(makeprofile),
                                    os.readlink(makeprofile))
                self.flIniFileFrom(inifile, system_root=systemRoot)
            self.flCalculateEnv(systemRoot)
        else:
            self.cache = cache

    def flCalculateEnv(self, systemRoot):
        env_data = dict(env._envData)
        for inifile in (env_data['system'],
                        env_data['custom']):
            self.flIniFileFrom(path.join(systemRoot, drop_slash(inifile)),
                               system_root=systemRoot)

    def init_variables(self):
        self['cl_chroot_path'] = self.systemRoot
        return True

    def change_root(self, dn, new_root='/'):
        if dn.startswith(self.systemRoot):
            return pathJoin(new_root, dn[len(self.systemRoot):])
        return dn
