#-*- 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
from os import path
import re
import platform
from calculate.lib.datavars import Variable, SimpleDataVars
from calculate.lib.utils.portage import isPkgInstalled, searchProfile
from calculate.lib.utils.files import readFile
from env import VariableClMakeProfile
from configparser import ConfigParser

class Linux:
    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)
            reMakeProfileLink = re.compile('/calculate/(desktop|server)/(%s)/'%
                        "|".join(self.dictLinuxName.keys()),re.S)
            shortnameSearch = reMakeProfileLink.search(link)
            if shortnameSearch:
                return shortnameSearch.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):
            FD = open(inifile)
            data = FD.readlines()
            FD.close()
            shortNameList = filter(lambda y:y,
                                   map(lambda x:\
                                       len(x.split("="))==2 and\
                                       x.split("=")[0]=="calculate" and\
                                       x.split("=")[1].strip(), data))
            if shortNameList:
                return shortNameList[0]

    def detectOtherShortname(self,systemroot):
        """Detect other system. Now only Gentoo."""
        gentooFile = path.join(systemroot, "etc/gentoo-release")
        if path.exists(gentooFile):
            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
        """
        overlayPath = 'var/lib/layman/calculate'
        releaseFile = 'profiles/calculate-release'
        return readFile(path.join(systemroot, overlayPath, releaseFile))

    def getVersionFromMetapackage(self,systemroot,shortname):
        """Get version from meta package"""
        for pkg in ("app-misc/calculate-meta", "app-misc/%s-meta" % shortname):
            calcMeta = isPkgInstalled(pkg, systemroot)
            if calcMeta:
                return calcMeta[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):
            FD = open(pathname)
            data = FD.readlines()
            FD.close()
            verList = filter(lambda y:y,
                                   map(lambda x:\
                                       len(x.split("="))==2 and\
                                       x.split("=")[0]=="linuxver" and\
                                       x.split("=")[1].strip(), data))
            if verList:
                reVer=re.compile("^(\d+\.)*\d$",re.S)
                reRes = filter(reVer.search,verList)
                if reRes:
                    return reRes[0]

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

    def getVersionFromUname(self):
        """Get version from uname"""
        reVer=re.search("^(\d+\.)*\d",platform.release(),re.S)
        if reVer:
            return reVer.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):
        linuxShortName = self.Get(self.source_variable)
        return self.dictLinuxName.get(linuxShortName,"Linux")

class VariableOsLinuxSubname(Variable,Linux):
    """
    Subname of linux (KDE, GNOME, XFCE and etc)
    """
    source_variable = "os_linux_shortname"
    
    def get(self):
        linuxShortName = self.Get(self.source_variable)
        return self.dictLinuxSubName.get(linuxShortName,"")

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

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

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

    def get(self):
        """Get system version"""
        linuxShortName = self.Get("os_linux_shortname")
        return self.getVersionFromOverlay(self.systemRoot) or \
            self.getVersionFromMetapackage(self.systemRoot,
                                              linuxShortName) 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 calculate3.env"""
        return self.Get('os_linux_filesnum')
        #return str(countFiles(self.systemRoot))


class VariableOsLinuxPkglist(Variable):
    type = "list"

    def generate_shortnames(self, make_profile):
        for fn in searchProfile(make_profile, "calculate.env"):
            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')
        yield "base"

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


class LinuxDataVars(SimpleDataVars):
    def __init__(self, systemRoot="/"):
        self.systemRoot = systemRoot
        SimpleDataVars.__init__(self,
            VariableClMakeProfile(systemRoot=systemRoot),
            VariableOsLinuxShortname(systemRoot=systemRoot),
            VariableOsLinuxName(systemRoot=systemRoot),
            VariableOsLinuxSubname(systemRoot=systemRoot),
            VariableOsLinuxFilesnum(systemRoot=systemRoot),
            VariableOsLinuxFiles(systemRoot=systemRoot),
            VariableOsLinuxSystem(systemRoot=systemRoot),
            VariableOsLinuxVer(systemRoot=systemRoot),
            VariableOsLinuxBuild(systemRoot=systemRoot))
        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)
        for inifile in ("etc/calculate/calculate.env",
                        "etc/calculate/calculate3.env",
                        "etc/calculate/calculate2.env"):
            self.flIniFileFrom(path.join(systemRoot, inifile))
