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

# Copyright 2008-2015 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, R_OK
import platform
import re
from calculate.lib.datavars import Variable, ReadonlyVariable
from calculate.lib.utils.files import readFile, process, listDirectory, isMount
from calculate.lib.utils.device import getUdevDeviceInfo, loadEfiVars
from calculate.lib.utils.common import getKernelUid


class VariableOsArchMachine(ReadonlyVariable):
    """
    Computer architecture (i686,x86_64)
    """

    def get(self):
        return platform.machine()


class VariableOsArchMachineGentoo(ReadonlyVariable):
    """
    Архитектура пакетов
    """
    source_arch = "os_arch_machine"

    def get(self):
        map_arch = {'x86_64': 'amd64', 'i686': 'x86', 'i386': 'x86'}
        arch = self.Get(self.source_arch)
        return map_arch.get(arch, arch)


class VariableOsRootDev(ReadonlyVariable):
    """
    Root partition of current system
    """

    def get(self):
        """Root filesystem device"""
        record = readFile('/proc/cmdline').strip()
        re_resRealRoot = re.search('(?:^|\s)real_root=(\S+)(\s|$)', record)
        re_resFakeRoot = re.search('(?:^|\s)root=(\S+)(\s|$)', record)
        # param real_root priority that root
        re_res = re_resRealRoot or re_resFakeRoot
        if re_res:
            rootparam = re_res.group(1)
            # check root for /dev/sd view
            if re.match("^/dev/[a-z]+.*$", rootparam):
                return getUdevDeviceInfo(
                    name=rootparam.strip()).get('DEVNAME', rootparam)
            # check root set by uuid
            if re.match("^UUID=.*$", rootparam):
                uuid = rootparam[5:].strip("\"'")
                blkid_process = process('/sbin/blkid', '-c', '/dev/null', '-U',
                                        uuid)
                if blkid_process.success():
                    return getUdevDeviceInfo(
                        name=blkid_process.read().strip()).get('DEVNAME', '')
            # check root set by label
            if re.match("^LABEL=.*$", rootparam):
                uuid = rootparam[6:].strip("\"'")
                blkid_process = process('/sbin/blkid', '-c', '/dev/null', '-L',
                                        uuid)
                if blkid_process.success():
                    return getUdevDeviceInfo(
                        name=blkid_process.read().strip()).get('DEVNAME', '')
        # get device mounted to root
        df_process = process('df', '/', envdict={'LANG': 'C'})
        if df_process.failed():
            return ""
        df_lines = df_process.readlines()
        if len(df_lines) > 1:
            root_dev = df_lines[1].split(" ")[0].strip()
            if root_dev:
                return {
                    'none': '/dev/ram0',
                    'overlay': '/dev/ram0',
                }.get(root_dev, root_dev)
        return ""


class VariableOsMapperRootDev(ReadonlyVariable):
    """
    Название диска через devicemapper если возможно
    """

    def get(self):
        rootdev = self.Get('os_root_dev')
        dev_links = getUdevDeviceInfo(name=rootdev).get("DEVLINKS", "")
        if dev_links:
            mapper_name = filter(lambda x: "/dev/mapper" in x,
                                 dev_links.split())
            if mapper_name:
                return mapper_name[0]
        return rootdev


class RootType(object):
    """
    Типы загруженной системы
    """
    HDD = "hdd"
    LiveCD = "livecd"
    USB_HDD = "usb-hdd"
    LiveFlash = "flash"
    IsoScan = "isoscan"
    NetBoot = "nfs"

    Live = (LiveCD, LiveFlash, IsoScan, NetBoot)

    RebuildAvailable = (LiveFlash, IsoScan)


class VariableOsRootType(ReadonlyVariable):
    """
    Root type (ram, hdd, usb-hdd, livecd)
    """

    def get(self):
        """Root device type (ram, hdd, livecd)"""
        extended_type = self.Get('os_root_type_ext')
        if extended_type in RootType.Live:
            return "livecd"
        if extended_type == RootType.USB_HDD:
            return "usb-hdd"
        return "hdd"


class VariableOsRootTypeExt(ReadonlyVariable):
    """
    Расширенный тип загруженной системы
    """

    def get(self):
        def link2pair(linkfile):
            """Return pair (target,link) from link"""
            basedir = path.dirname(linkfile)
            targetfile = os.readlink(linkfile)
            return path.normpath(os.path.join(basedir, targetfile)), linkfile

        root_dev = self.Get("os_root_dev")
        if root_dev:
            if "/dev/nfs" in root_dev:
                return RootType.NetBoot

            if "/dev/ram" in root_dev:
                record = readFile('/proc/cmdline').strip()
                if "iso-scan/filename" in record:
                    return RootType.IsoScan
                elif "root=live:UUID" in record:
                    return RootType.LiveFlash
                return RootType.LiveCD

            id_dict = dict(map(link2pair,
                               filter(lambda x: path.islink(x),
                                      map(lambda x: path.join('/dev/disk/by-id',
                                                              x),
                                          listDirectory('/dev/disk/by-id')))))
            if "usb-" in id_dict.get(root_dev, ""):
                return RootType.USB_HDD
        return RootType.HDD


class VariableClChrootStatus(ReadonlyVariable):
    """
    Is chroot active
    """

    def isChroot(self, pid):
        """Detect chroot mode by different mountinfo"""
        if not os.access('/proc/self/mountinfo', R_OK) or \
                not os.access('/proc/1/mountinfo', R_OK):
            return False
        infos = [open(x, 'r').read() for x in ('/proc/1/mountinfo',
                                               '/proc/self/mountinfo')]
        return infos[0] != infos[1]

    def get(self):
        try:
            return "on" if self.isChroot(os.getpid()) else "off"
        except Exception:
            return "off"


class VariableClKernelUid(Variable):
    """
    Kernel UID get by UUID of root device
    """

    def get(self):
        return getKernelUid(self.Get('os_root_dev'))


class VariableOsScratch(ReadonlyVariable):
    """
    Is current system scratch
    """

    def get(self):
        """Current system is scratch"""
        if self.Get('os_root_type') == 'livecd':
            return "on" if isMount('/mnt/scratch/workspace') else "off"
        else:
            return "on" if isMount('/mnt/scratch') else "off"


class VariableOsUefiSet(ReadonlyVariable):
    """
    Current loaded system
    """

    def get(self):
        if not path.exists('/sys/firmware/efi'):
            loadEfiVars()
        if path.exists('/sys/firmware/efi'):
            return "on"
        else:
            return "off"


class VariableClUsedAction(ReadonlyVariable):
    """
    Переменная содержит все найденные при обработке шаблонов
    событийные переменные (ac_)
    """
    type = "table"

    def get(self):
        return []


class VariableClSystemBootSet(ReadonlyVariable):
    """
    Система находится в стадии загрузки
    """
    type = "bool"

    def get(self):
        if os.readlink('/proc/self/fd/0') == '/dev/console':
            return "on"
        else:
            return "off"
