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

# Copyright 2008-2016 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.
import os
import re
from ..datavars import Variable, ReadonlyVariable, DataVarsError
from ..utils.files import process, FilesError, readFile
from ..utils.common import (
    getValueFromCmdLine, getVideoFromXorgLog, getVideoFromXorgConf,
    getVideoFromCmdLine, getCompositeFromXorgconf, getTupleVersion,
    getVideoFromModules, getVideoFromVendor, getInstalledVideo, CmdlineParams)
from ..utils.portage import isPkgInstalled
from math import sqrt


class VariableOsX11Resolution(Variable):
    """
    X11 resolution (1024x768 and etc)
    """

    def getResByXDpyInfo(self):
        """Get resolution by xdpyinfo utility"""
        try:
            processXDpy = process('xdpyinfo')
            if processXDpy.failed():
                return ""
        except (FilesError, OSError):
            return ""
        lines = processXDpy.readlines()
        reRes = re.compile("dimensions:\s+(\d+)x(\d+)\s+pixels")
        searchRes = False
        for line in lines:
            searchRes = reRes.search(line)
            if searchRes:
                break
        if searchRes:
            return "%sx%s" % (searchRes.group(1), searchRes.group(2))
        return ""

    def getResByXrandr(self):
        """Get resolution by xrandr utility"""
        try:
            processXrandr = process('xrandr')
            if processXrandr.failed():
                return ""
        except (FilesError, OSError):
            return ""
        lines = processXrandr.readlines()
        reRes = re.compile("current\s+(\d+)\s?x\s?(\d+)")
        for line in lines:
            searchRes = reRes.search(line)
            if searchRes:
                break
        if searchRes:
            return "%sx%s" % (searchRes.group(1), searchRes.group(2))
        return ""

    def get(self):
        """
        Return current screen resolution (width, height).
        Try detect by xdpyinfo, then Xorg.log, xorg.conf
        """
        resolution = self.getResByXDpyInfo() or self.getResByXrandr()
        if resolution:
            return resolution

        if self.Get('os_root_type') != 'usb-hdd':
            xlog = "/var/log/Xorg.0.log"
            if os.access(xlog, os.R_OK):
                logdata = ''
                encoding = ['utf-8', 'cp1252', 'utf-16']
                for enc in encoding:
                    try:
                        with open(xlog, 'r', encoding=enc) as f:
                            logdata = f.read()
                        break
                    except UnicodeDecodeError:
                        continue
                for re_pat in (
                    "Output [\S]+ using initial mode (\d+)x(\d+)",
                    "Virtual screen size determined to be"
                    " ([0-9]+)\s*x\s*([0-9]+)",
                    'Setting mode "(\d+)x(\d+)[0-9@]"',
                    "Virtual size is (\d+)x(\d+)"):
                    reXorgLogParser = re.compile(re_pat, re.S)
                    resXorgLogParser = reXorgLogParser.search(logdata)
                    if resXorgLogParser:
                        tmp = [x for x in resXorgLogParser.groups() if x]
                        return "%sx%s" % tuple(tmp[:2])

            # get resolution from xorg.conf
            xorgconf = "/etc/X11/xorg.conf"
            reScreenSections = re.compile('Section "Screen"(.*?)EndSection',
                                          re.S)
            reModes = re.compile('Modes\s+"(\d+x\d+)')
            if os.access(xorgconf, os.R_OK):
                sectionsScreen = [x for x 
                        in reScreenSections.findall(readFile('/etc/X11/xorg.conf')) 
                        if "Modes" in x]
                modes = [x.groups()[0] for x in map(reModes.search, sectionsScreen) if x]
                if modes:
                    return max(modes, key=lambda x: int(x.partition('x')[0]))

        # get resolution from command line
        reRightResolution = re.compile("^(\d+x\d+|auto)$", re.S)
        kernelResolution = getValueFromCmdLine(
            CmdlineParams.Calculate, CmdlineParams.Resolution)
        if kernelResolution and reRightResolution.match(kernelResolution):
            return {'auto': ''}.get(kernelResolution, kernelResolution)
        else:
            return ""


class VariableOsX11Height(ReadonlyVariable):
    """
    Vertical resolution for X server
    """

    def get(self):
        return self.Get('os_x11_resolution').partition('x')[2] or "768"


class VariableOsX11Width(ReadonlyVariable):
    """
    Horizontal resolution for X server
    """

    def get(self):
        return self.Get('os_x11_resolution').partition('x')[0] or "1024"


class VariableOsX11Standart(ReadonlyVariable):
    """
    Nearest standard size of image relative current
    """

    def get(self):
        widthVal = self.Get('os_x11_width')
        heightVal = self.Get('os_x11_height')
        if not widthVal or not heightVal:
            return ""
        width = int(widthVal)
        height = int(heightVal)
        gep = sqrt(height ** 2 + width ** 2)
        k = float(width) / float(height)
        for themePkg in ['media-gfx/cldx-themes',
                         'media-gfx/cld-themes',
                         'media-gfx/cldg-themes']:
            installed = isPkgInstalled(themePkg,
                                       prefix=self.Get('cl_chroot_path'))
            if installed and getTupleVersion(
                    installed[0]['PV']) < getTupleVersion("12"):
                res = [(1024, 600), (1024, 768),
                       (1280, 1024), (1280, 800),
                       (1366, 768), (1440, 900),
                       (1600, 1200), (1680, 1050),
                       (1920, 1200)]
                break
        else:
            res = [(1024, 576), (1024, 600),
                   (1024, 768), (1200, 800), (1280, 1024),
                   (1280, 720), (1280, 768), (1280, 800),
                   (1360, 768), (1366, 768), (1368, 768),
                   (1400, 1050), (1440, 900), (1600, 1200),
                   (1600, 768), (1600, 900), (1680, 1050),
                   (1680, 945), (1920, 1080), (1920, 1200),
                   (2048, 1152), (2560, 1440), (2560, 1600),
                   (640, 480), (800, 480)]
        bestRes = min(res,
                      key=lambda x: (abs(x[0] / float(x[1]) - k),
                                     abs(gep - sqrt(x[0] ** 2 + x[1] ** 2))))
        return "%sx%s" % bestRes


class VariableOsX11Composite(ReadonlyVariable):
    """
    On or off composite mode
    """

    def get(self):
        state = getCompositeFromXorgconf('/')
        return state or "off"


class VariableOsX11VideoDrv(ReadonlyVariable):
    """
    Video driver used by xorg
    """

    def get(self):
        """Get video driver used by xorg"""
        list_video = getInstalledVideo('/')
        methods = ((getVideoFromXorgLog, ('/', list_video)),
                   (getVideoFromXorgConf, ('/',)),
                   (getVideoFromCmdLine, ()),
                   (getVideoFromModules, ()),
                   (getVideoFromVendor, (self.Get('hr_video'), list_video)))
        for func, args in methods:
            drv = func(*args)
            if drv in list_video:
                return drv
        return "default"


class VariableClResolutions(Variable):
    """
    Список поддерживаемых разрешений для фоновых изображений по умолчанию
    """
    type = "list"
    fallback_value = \
        ["1024x768", "640x480", "800x480", "800x600", "1024x576", "1024x600",
         "1200x800", "1280x800", "1280x720",
         "1280x768", "1280x1024", "1360x768", "1366x768",
         "1368x768", "1400x1050", "1440x900", "1680x945",
         "1680x1050", "1920x1080", "1920x1200", "1600x768",
         "1600x900", "1600x1200", "2048x1152", "2560x1440",
         "2560x1600"]

    def get(self):
        if self.Get('cl_chroot_status') == 'off':
            try:
                # values = list(filter(None, list({
                #     self.Get('install.os_install_fb_resolution'),
                #     self.Get('install.os_install_x11_resolution')})))
                values = [x for x in list({
                    self.Get('install.os_install_fb_resolution'),
                    self.Get('install.os_install_x11_resolution')}) if x]
                if values:
                    return values
            except DataVarsError:
                pass
            return ["1024x768"]
        return self.fallback_value


class VariableClImageFormats(Variable):
    """
    Список поддерживаемых форматов изображений для фона
    """
    type = "list"
    value = ["gif", "jpg", "png", "gfxboot"]
