#-*- 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
import sys
import re
from os import path
from calculate.lib.datavars import Variable,VariableError,ReadonlyVariable
from calculate.lib.utils.files import (
        typeFile,process,listDirectory,MAGIC_SYMLINK,MAGIC_COMPRESS)

from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_install3',sys.modules[__name__])
from operator import itemgetter

from calculate.lib.utils.files import readLinesFile,pathJoin
from calculate.lib.utils.common import (getKernelUid,getTupleVersion,
    getValueFromCmdLine)
from calculate.lib.utils.device import getUdevDeviceInfo
from itertools import *
from calculate.install.distr import DistributiveError

class VariableOsInstallKernelScheduler(Variable):
    """
    Install scheduler opts (cfq,bfq,none,deadline)
    """
    type = "choice"
    opt = ["--scheduler"]
    metavalue = "SCHEDULER"

    def init(self):
        self.help = _("toggle the I/O scheduler")
        self.label = _("I/O scheduler")

    def get(self):
        """Get scheduler for install root device"""
        if self.Get('os_root_type') == 'livecd':
            root_dev = self.Select('os_install_disk_parent',
                                   where='os_install_disk_mount',
                                   eq='/',limit=1)
            if root_dev:
                dev_ssd = self.Select('os_device_ssd_set',
                                       where='os_device_dev',
                                       eq=root_dev,limit=1)
                if dev_ssd == 'on':
                    return "noop"
        else:
            currentScheduler = getValueFromCmdLine('elevator',0)
            if currentScheduler in map(lambda x:x[0],self.choice()):
                return currentScheduler
        return "cfq"
    
    def choice(self):
        schedulers = [("deadline","Deadline"),("cfq","CFQ"),("noop","No-op")]
        if self.Get('os_install_kernel_bfq_set') == 'on':
            return schedulers + [("bfq","BFQ")]
        else:
            return schedulers

    def uncompatible(self):
        """
        Unavailable for flash installation
        """
        if self.Get('os_install_root_type') == 'flash':
            return _("I/O scheduler unavailable for Flash install")

class VariableOsInstallKernelBfqSet(ReadonlyVariable):
    """
    Available BFQ in kernel
    """
    type = "bool"

    def get(self):
        image = self.Get('cl_image')
        if image:
            with image as distr:
                try:
                    distrPath = image.getDirectory()
                    kernelConfig = path.join(distrPath,"usr/src/linux/.config")
                    if filter(lambda x:"CONFIG_IOSCHED_BFQ=y" in x,
                        readLinesFile(kernelConfig)):
                        return "on"
                except DistributiveError as e:
                    return "off"
        return "off"

class VariableOsInstallKernelAttr(Variable):
    """
    Install kernel attributes
    """
    def get(self):
        # on usb-hdd install must be "delay=5"
        attr = ""
        rdauto = ""
        if self.Get('os_install_root_type') == 'usb-hdd':
            attr = " scandelay=5"
        if self.Get('os_install_mdadm_set') == 'on':
            attr += " domdadm"
            rdauto = " rd.auto"
        if self.Get('os_install_lvm_set') == 'on':
            attr += " dolvm"
            if not rdauto:
                rdauto = " rd.auto"
        return attr + rdauto

class VariableOsInstallKernelResume(ReadonlyVariable):
    """
    Install kernel resume
    """
    def get(self):
        """install kernel resume parameter"""
        for dev, install in zip(self.Get('os_install_disk_use'),
                                self.Get('os_install_disk_mount')):
            if install == "swap":
                if self.Get('os_install_linux_system') == 'desktop':
                    return \
                        "tuxonice tuxonice_resume=%s real_resume=%s"%(dev,dev)
                else:
                    return "real_resume=%s"%dev
        return ""

class KernelHelper:
    """
    Helper for kernel variables
    """
    reFindVer = re.compile(
        "(?<=version )(\d+\.?\d*\.?\d*\.?\d*)([^\d* ])*(\d*)")

    def getFilesByType(self,pathname,descr):
        """Get files from "pathname" has "descr" in descriptions"""
        filelist = map(lambda x:path.join(pathname,x),os.listdir(pathname))
        ftype = typeFile(magic=MAGIC_COMPRESS|MAGIC_SYMLINK).getMType
        filesWithType = map(lambda x:(x,ftype(x)),
                        filter(path.exists,
                        filelist))
        return filter(lambda x:descr in x[1],filesWithType)

    def getInitrd(self,arch,shortname,chroot,kernel,suffix="",notsuffix=""):
        """Get initrd for kernel"""
        reInitrdVer = re.compile("(initrd|initramfs)-(.+?)(-install)?$",re.S)
        def initrd_version_by_name(filename):
            resInitrdVer = reInitrdVer.search(filename)
            if resInitrdVer:
                return resInitrdVer.groups()[1]
            return ""

        ftype = typeFile(magic=MAGIC_COMPRESS|MAGIC_SYMLINK).getMType
        kernelfile = path.join(chroot,'boot',kernel)
        typeKernelFile = ftype(kernelfile)
        if typeKernelFile == None:
            return ""
        resKernelVer = self.reFindVer.search(ftype(kernelfile))
        if resKernelVer:
            kernelVersion = "%s-%s-%s"% \
               (resKernelVer.group().replace('-calculate',''),
                arch, shortname)
            origKernelVer = resKernelVer.group()

            bootdir = path.join(chroot,'boot')
            initramfsFiles = self.getFilesByType(bootdir,"ASCII cpio archive")
            initramfsWithVer = \
                    filter(lambda x: (kernelVersion in x[1] or 
                              origKernelVer in x[1]) and \
                              x[0].endswith(suffix) and \
                              (not notsuffix or not x[0].endswith(notsuffix)),
                           map(lambda x:(x[0],initrd_version_by_name(x[0])),
                           initramfsFiles))
            if initramfsWithVer:
                return path.split(min(initramfsWithVer,
                                     key=itemgetter(0))[0])[-1]
        return ""


class VariableOsInstallKernel(ReadonlyVariable,KernelHelper):
    """
    Kernel filename
    """

    def get(self):
        bootdir = path.join(self.Get('cl_chroot_path'),'boot')
        modulesdir = path.join(self.Get('cl_chroot_path'),'lib/modules')
        validKernel = listDirectory(modulesdir)
        kernelFiles = self.getFilesByType(bootdir,"Linux kernel")
        installMarch = self.Get('os_install_arch_machine')
        kernelsWithVer = \
            map(lambda x:(x[0],(getTupleVersion("".join(x[1].groups()[0:3:2])),
                                path.getmtime(x[0]))),
            # convert version to tuple( versionTuple, mtime)
            # version detect, for this version lib contains moudules
            # kernel arch equal install arch
            ifilter(lambda x:x[1] and x[1].group() in validKernel and
                       installMarch in x[0].rpartition('/')[2],
            # (filename,version)
            imap(lambda x:(x[0],self.reFindVer.search(x[1])),
            kernelFiles)))
        if kernelsWithVer:
            return path.split(max(kernelsWithVer,key=itemgetter(1))[0])[-1]
        else:
            return "vmlinuz"

class VariableOsInstallInitrd(ReadonlyVariable,KernelHelper):
    """
    Optimized initramfs filename
    """

    def get(self):
        return self.getInitrd(self.Get('os_install_arch_machine'),
                              self.Get('os_install_linux_shortname'),
                              self.Get('cl_chroot_path'),
                              self.Get('os_install_kernel'),
                              suffix="",notsuffix="-install") or \
               self.getInitrd(self.Get('os_install_arch_machine'),
                              self.Get('os_install_linux_shortname'),
                              self.Get('cl_chroot_path'),
                              self.Get('os_install_kernel'),
                              suffix="-install")[:-8] \
                              or "initrd"

class VariableOsInstallInitrdInstall(ReadonlyVariable,KernelHelper):
    """
    Install initramfs filename
    """
    def get(self):
        return self.getInitrd(self.Get('os_install_arch_machine'),
                              self.Get('os_install_linux_shortname'),
                              self.Get('cl_chroot_path'),
                              self.Get('os_install_kernel'),
                              suffix="-install") or "initrd-install"


class VariableOsInstallKernelConfig(ReadonlyVariable):
    """
    Install config kernel filename
    """
    value = ""

class VariableOsInstallSystemMap(ReadonlyVariable):
    """
    Install system map filename
    """
    def get(self):
        systemmapfile = self.Get('os_install_kernel').replace('vmlinuz',
                        'System.map')
        if systemmapfile.startswith('System.map') and path.exists(
                path.join(self.Get('cl_chroot_path'),'boot',systemmapfile)):
            return systemmapfile
        else:
            return ""

class VariableOsInstallKernelCpufreq(ReadonlyVariable):
    """
    Cpufreq modules
    """
    def get(self):
        """Get cpufreq (and other from modules_3= param) from conf.d/modules"""
        cpufreqmods = map(lambda x:x.partition('=')[2].strip("\n '\""),
                      filter(lambda x:x.startswith('modules_3'),
                      readLinesFile('/etc/conf.d/modules')))
        if cpufreqmods:
            return cpufreqmods[0]
        else:
            return ""

class VariableClInstallKernelUid(ReadonlyVariable):
    """
    Variable install kernel UID
    """
    def get(self):
        return getKernelUid(self.Get('os_install_root_dev'))

