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

# Copyright 2014 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.

from collections import Mapping
from functools import total_ordering

import os
from os import path
import bz2
import re
try:
    from calculate.lib.utils.files import readFile, listDirectory, pathJoin
    from calculate.lib.datavars import SimpleDataVars
except ImportError:
    from cl_utils import readFile, listDirectory, pathJoin

from cl_datavars import DataVars

class SimpleRepositoryMapper(Mapping):
    """
    Определение пути до репозитория
    """
    map_repository = {'gentoo': '/usr/portage',
                      'calculate': '/var/lib/layman/calculate'}
    layman_path = '/var/lib/layman'

    def __init__(self, prefix='/'):
        self.prefix = prefix

    def __getitem__(self, item):
        if item in self.map_repository:
            return pathJoin(self.prefix, self.map_repository[item])
        return pathJoin(self.prefix, self.layman_path, item)

    def __iter__(self):
        return iter(self.map_repository)

    def __len__(self):
        return len(self.map_repository)


class EbuildInfoError(Exception):
    pass


class EbuildInfo(object):
    """
    Информация о ebuild (DEPEND) из metadata
    """
    map_repository = SimpleRepositoryMapper()
    meta_suffix_path = 'metadata/md5-cache'
    support_keys = ('DEPEND', 'RDEPEND', 'PDEPEND')

    def __init__(self, atom, repository):
        meta_path = path.join(self.map_repository[repository],
                              self.meta_suffix_path)
        self._meta_info_path = path.join(meta_path, atom)
        if not path.exists(self._meta_info_path):
            raise EbuildInfoError("Package is not found")
        self._info = self._get_info()

    def _get_info(self):
        with open(self._meta_info_path, 'r') as f:
            return {k.strip(): v.strip() for k, v in (line.partition('=')[::2]
                                                      for line in f.readlines())
                    if k in self.support_keys}

    def __getitem__(self, item):
        if item in self.support_keys:
            return self._info.get(item,'')
        raise KeyError(item)

    @classmethod
    def set_repository_mapper(cls, mapper):
        cls.map_repository = mapper

    def __eq__(self, other):
        return all(other[k] == self[k] for k in self.support_keys)

    def __ne__(self, other):
        return any(other[k] != self[k] for k in self.support_keys)


class InstalledPackageInfoError(Exception):
    pass


class InstalledPackageInfo(object):
    """
    Информация об установленном пакете (DEPEND) из /var/db/pkg
    """
    pkg_dir = '/var/db/pkg'
    depend_pattern = 'declare (?:-x )?({0})="([^"]+)"'
    re_depend = re.compile(depend_pattern.format("RDEPEND|PDEPEND|DEPEND"),
                           re.DOTALL)
    re_multispace = re.compile("\s+", re.DOTALL)

    def __init__(self, atom):
        self.atom = atom
        self._pkg_path = path.join(self.pkg_dir, atom)
        if not path.exists(self._pkg_path):
            raise InstalledPackageInfoError("Package is not found")
        self._info = self._get_info()

    def _get_info(self):
        info = {k: "" for k in ("RDEPEND", "DEPEND", "PDEPEND")}
        env_path = path.join(self._pkg_path, 'environment.bz2')
        if path.exists(env_path):
            with bz2.BZ2File(env_path, 'r') as f:
                for r in self.re_depend.finditer(f.read()):
                    key, value = r.groups()
                    value = self.re_multispace.sub(" ", value)
                    info[key] = value.strip()
        rep_path = path.join(self._pkg_path, 'repository')
        info['repository'] = readFile(rep_path).strip()
        return info

    def __getitem__(self, item):
        return self._info[item]

    @classmethod
    def get_install_packages(cls):
        for category in listDirectory(cls.pkg_dir):
            for pkg in listDirectory(path.join(cls.pkg_dir, category)):
                yield InstalledPackageInfo("%s/%s" % (category, pkg))

    def __str__(self):
        return self.atom

    def __repr__(self):
        return "InstalledPackageInfo(%s)" % self.atom

class SimpleVariable(object):
    def __init__(self, varname, fallback_value="", section="main", type="string"):
        self.varname = varname
        self.fallback_value = fallback_value
        self.section = section
        self.type = type

    def get(self):
        return self.fallback_value

    def getVariableName(self):
        return self.varname

class ArchVariable(SimpleVariable):
    def __init__(self, varname, fallback_value="", section="main", type="string", prefix="/"):
        self.varname = varname
        self.fallback_value = fallback_value
        self.section = section
        self.type = type
        self.prefix = prefix

    def get(self):
        if path.lexists(path.join(self.prefix,'lib64')):
            return 'x86_64'
        elif path.lexists(path.join(self.prefix,'lib')):
            return 'i686'
        else:
            d['os_arch_machine'] = ''

class ProfileVariable(SimpleVariable):
    def __init__(self, varname, fallback_value="", section="main", type="string", prefix="/"):
        self.varname = varname
        self.fallback_value = fallback_value
        self.section = section
        self.type = type
        self.prefix = prefix

    def get(self):
        for profile in ('etc/portage/make.profile',
                        'etc/make.profile'):
            profile = path.join(self.prefix, profile)
            if path.exists(profile):
                return os.readlink(profile)
        return ""
