# -*- 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.

import sys
import os
import re
from calculate.lib.cl_template import TemplateFormat
import hashlib

from calculate.lib.cl_lang import setLocalTranslate
from calculate.lib.utils.files import (getProgPath, pathJoin, process, STDOUT,
                                       readFile, writeFile, listDirectory)

_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])


class backgrounds(TemplateFormat):
    """
    Формат для модификации принадлежности файлов пакетам
    """
    text = ""
    source = None
    convert = None
    prefix = None
    stretch = False
    mirror = False
    fallback_md5fn = "md5sum"

    def prepare(self):
        self.chroot_cmd = None
        self.chroot_path = None
        self.convert_cmd = getProgPath('/usr/bin/convert')
        self.bash_cmd = getProgPath('/bin/bash')
        self.identify_cmd = getProgPath('/usr/bin/identify')
        self.source_width = None
        self.source_height = None

    def textToXML(self):
        return self.text

    def setRootPath(self, rpath):
        self.convert_cmd = '/usr/bin/convert'
        self.identify_cmd = '/usr/bin/identify'
        if not os.path.exists(pathJoin(rpath, self.convert_cmd)):
            self.convert_cmd = None
        if not os.path.exists(pathJoin(rpath, self.identify_cmd)):
            self.identify_cmd = None
        self.chroot_cmd = getProgPath('/bin/chroot')
        self.chroot_path = rpath

    def setMirror(self):
        self.mirror = True

    def setSource(self, source):
        self.source = source

    def setConvert(self, convert):
        self.convert = convert

    def setPrefix(self, prefix):
        self.prefix = prefix

    def getImageMD5(self, source, resolutions=()):
        if source:
            data = readFile(source)
        else:
            data = ""
        return hashlib.md5(data+"\n".join(resolutions)).hexdigest()

    def setStretch(self, stretch):
        self.stretch = stretch

    def trim_chroot_path(self, p):
        return "/%s" % os.path.relpath(p, self.chroot_path)

    def _create_image(self, source, target, width, height, force=False):
        res = "%dx%d" % (width, height)
        if not force and (width > self.source_width or
                          height > self.source_height):
            return False
        if self.convert != "gfxboot" and (
            (width == self.source_width and height == self.source_height) and
                (source.rpartition('.')[2] == target.rpartition('.')[2])):
            with writeFile(target) as sf:
                sf.write(readFile(source))
            return True
        if self.chroot_cmd:
            source = self.trim_chroot_path(source)
            target = self.trim_chroot_path(target)

        command = [self.convert_cmd, "-quality", "95",
                   source, "-resize", "%s^" % res,
                   "-strip", "-gravity", "center",
                   "-crop", "%s+0+0" % res]
        if self.convert == "gfxboot":
            command.extend(
                ["-sampling-factor", "2x2",
                 "-interlace", "none",
                 "-set", "units", "PixelsPerSecond"])
        command += [target]
        if self.chroot_cmd:
            convert = process(self.chroot_cmd, self.chroot_path,
                              self.bash_cmd, "-c",
                              " ".join(command), stderr=STDOUT)
        else:
            convert = process(*command, stderr=STDOUT)
        convert.write(self.text)
        if convert.success():
            return True
        return False

    def get_image_resolution(self, source):
        if self.chroot_cmd:
            source = self.trim_chroot_path(source)
            identify = process(self.chroot_cmd, self.chroot_path,
                               self.bash_cmd, "-c",
                               " ".join([self.identify_cmd,
                                        "-format '%[w] %[h]'", source]))
        else:
            identify = process(self.identify_cmd, "-format", "%[w] %[h]", source)
        if identify.success():
            swidth, _sep, sheight = identify.read().partition(" ")
            if swidth.isdigit() and sheight.isdigit():
                return int(swidth), int(sheight)
            return None, None
        return None, None

    def processingFile(self, textConfigFile, rootPath=None, nameFile=None):
        """Обработка конфигурационного файла"""
        if not (self.convert_cmd and self.identify_cmd):
            self.setError("%s. %s" % (_("Format 'backgrounds' is unavailable"),
                                      _("Need to install {packages}").format(
                                          packages="media-gfx/imagemagick")))
            return False
        if not self.source:
            if not self.mirror:
                return ""
        if not self.convert:
            self.convert = self.source.rpartition(".")[2].lower()
        if self.convert not in self.objVar.Get('cl_image_formats'):
            self.setError(_("Wrong image format '%s'") % self.convert)
            return False
        reRule = re.compile("^(\d+)x(\d+)(?:-[0-9]+)?$")
        if not rootPath:
            rootPath = '/'

        if self.prefix is None:
            workdir, prefix = os.path.split(nameFile)
        else:
            workdir = nameFile
            prefix = ""
        if prefix:
            md5_fn = os.path.join(workdir, "%s.md5" %
                                  re.sub("[-._]+$", "", prefix))
        else:
            md5_fn = os.path.join(workdir, self.fallback_md5fn)
        if not self.source:
            source = None
        else:
            source = os.path.normpath(pathJoin(rootPath, self.source))
            if not os.path.exists(source):
                source = None
            else:
                self.source_width, self.source_height = \
                    self.get_image_resolution(source)
                if not self.source_width:
                    self.setError(
                        _("Failed to detect resolution for image '%s'") %
                        source)
                    return False

        if self.text.strip():
            text_list = self.text.split("\n")
        else:
            text_list = self.objVar.Get('cl_resolutions')
        image_files = []
        resolutions = [x.strip() for x in text_list
                       if not x.startswith("#") and x.strip()]
        image_md5 = self.getImageMD5(source, resolutions=resolutions)
        only_one = len(resolutions) == 1 and self.text.strip()
        if only_one:
            image_fn = pathJoin(workdir, prefix)
            fn_base = image_fn.rpartition(".")[0]
            if not fn_base:
                fn_base = image_fn
            md5_fn = "%s.md5" % fn_base
        if os.path.exists(md5_fn):
            md5sum = readFile(md5_fn).strip()
            if md5sum == image_md5:
                return ""
        if only_one:
            removed_files = [pathJoin(workdir, prefix)]
        else:
            removed_files = []
            extensions = self.objVar.Get('cl_image_formats')
            re_remove = re.compile(r"^%s\d{3,4}x\d{3,4}\.(%s)$"
                                   % (prefix, "|".join(extensions)))
            for remove_fn in listDirectory(workdir, fullPath=True):
                fn = os.path.split(remove_fn)[1]
                if re_remove.match(fn):
                    removed_files.append(remove_fn)
        if source:
            for resolution in resolutions:
                rule_match = reRule.search(resolution)
                if not rule_match:
                    self.setError(
                        _("Wrong 'backgrounds' resolution: %s") % resolution)
                    return False
                width, height = rule_match.groups()
                width = int(width)
                height = int(height)

                if only_one:
                    image_fn = pathJoin(workdir, prefix)
                else:
                    ext_map = {'gfxboot': 'jpg'}
                    image_fn = pathJoin(workdir, "%s%s.%s" % (
                        prefix, resolution,
                        ext_map.get(self.convert, self.convert)))
                if self._create_image(source, image_fn, width, height,
                                      force=self.stretch):
                    if image_fn in removed_files:
                        removed_files.remove(image_fn)
                    image_files.append(image_fn)
                else:
                    self.parent.printWARNING(
                        _("Skipped creation of the image with %s resolution") % (
                            "%dx%d" % (width, height)))
            with writeFile(md5_fn) as f:
                f.write("%s\n" % image_md5)
        else:
            removed_files.append(md5_fn)
        self.changed_files.append(md5_fn)
        for remove_fn in removed_files:
            try:
                if os.path.lexists(remove_fn):
                    os.unlink(remove_fn)
            except (IOError, OSError):
                self.parent.printWARNING(_("Failed to remove %s") % remove_fn)
        self.changed_files.extend(image_files)
        self.changed_files.extend(removed_files)
        return ""
