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

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

from calculate.lib.utils.device import (Sysfs, UdevAdm, sysfs, Devfs, Udev, Lvm,
                                        UdevAdmNull, LvmCommand, MdadmCommand,
                                        Raid)
from calculate.lib.utils.files import writeFile
from calculate.lib.utils.vfs import (VFSKeeper, VFSSysfsImporter,
                                     SafeVFS,
                                     VFSDevfsImporter)
import os
import re


class VirtualUdevAdm(UdevAdmNull):
    """
    Виртуальный udevadm
    """
    subsystems = ("block",)

    def __init__(self, data, parent):
        super(VirtualUdevAdm, self).__init__()
        self.export_data = data
        self.path_map = {}
        self.name_map = {}
        self.prepare_cache()
        self.parent = parent

    def _filter_udev_data(self, info):
        return info.get("SUBSYSTEM") in self.subsystems

    def prepare_cache(self):
        re_envblock = re.compile("^E: ([^=]+)=(.*)$", re.M)
        for block in (x for x in self.export_data.split("\n\n") if x.strip()):
            d = {x.group(1): x.group(2) for x in re_envblock.finditer(block)}
            if not self._filter_udev_data(d):
                continue
            if "DEVNAME" in d:
                self.name_map[d["DEVNAME"]] = d
            if "DEVPATH" in d:
                self.path_map[d["DEVPATH"]] = d

    def info_property(self, path=None, name=None):
        if path is not None:
            path = self.parent.sysfs.realpath(path)
            cache_path = os.path.join("/", os.path.relpath(path, sysfs.base_dn))
            return self.path_map.get(cache_path, {})
        else:
            name = self.parent.devfs.realpath(name)
            return self.name_map.get(name, {})

    def info_export(self):
        return self.export_data

    def trigger(self, subsystem=None):
        pass

    def settle(self, timeout=15):
        pass


class VirtualLvmCommand(object):
    def __init__(self, output):
        self.output = output

    def get_physical_extent_size(self):
        return "4096"

    def get_pvdisplay_output(self, output):
        return self.output

    def vgscan(self):
        return True

    def vgchange(self):
        return True

    def lvchange(self, groups):
        return True

    def execute(self, *command):
        return True

    def double_execute(self, *command):
        return True

    def remove_lv(self, vg, lv):
        return True

    def remove_vg(self, vg):
        return True

    def remove_pv(self, pv):
        return True


class VirtualMdadmCommand(object):
    def zero_superblock(self, devices):
        pass

    def stop_raid(self, devraid):
        pass

class VirtualDeviceModule(object):
    def __init__(self):
        self.sysfs = Sysfs()
        self.devfs = Devfs()
        self.udev = Udev()
        self.lvm = Lvm(LvmCommand())
        self.raid = Raid(MdadmCommand())

    def set_sysfs(self, f):
        self.sysfs = Sysfs(SafeVFS(VFSKeeper.load(f)))

    def set_devfs(self, f):
        self.devfs = Devfs(SafeVFS(VFSKeeper.load(f)))

    def set_udev(self, f):
        self.udev.udevadm = VirtualUdevAdm(f.read(), self)

    def set_raid(self):
        self.raid = Raid(VirtualMdadmCommand())

    def set_lvm(self, f):
        if f is None:
            self.lvm = Lvm(VirtualLvmCommand(""))
        else:
            self.lvm = Lvm(VirtualLvmCommand(f.read()))

class DumpError(Exception):
    pass


def dump_real_sysfs(fn):
    """
    Сохранить снимок sysfs в файл
    :param fn:
    :return:
    """
    try:
        with writeFile(fn) as f:
            VFSKeeper.save(f, VFSSysfsImporter().importfs())
    except IOError as e:
        raise DumpError(str(e))


def dump_real_udev(fn):
    """
    Сохранить снимок udev в файл
    :param fn:
    :return:
    """
    try:
        with writeFile(fn) as f:
            udevadm = UdevAdm()
            udevadm.trigger("block")
            f.write(udevadm.info_export())
    except IOError as e:
        raise DumpError(str(e))


def dump_real_devfs(fn):
    """
    Сохранить снимок /dev в файл
    :param fn:
    :return:
    """
    try:
        with writeFile(fn) as f:
            VFSKeeper.save(f, VFSDevfsImporter().importfs())
    except IOError as e:
        raise DumpError(str(e))

def dump_real_lvm(fn):
    """
    Сохранить снимок /dev в файл
    :param fn:
    :return:
    """
    try:
        with writeFile(fn) as f:
            lvm = Lvm(LvmCommand())
            lvm.refresh()
            f.write(lvm.pvdisplay_output("vg_name,lv_name,pv_name"))
    except IOError as e:
        raise DumpError(str(e))
