"""Provide access to Blink's resources"""

import __main__
import imghdr
import os
import platform
import sys
import pathlib

from PyQt5.QtCore import Qt, QBuffer
from PyQt5.QtGui import QIcon, QPixmap

from application.python.descriptor import classproperty
from application.python.types import Singleton
from application.system import makedirs, unlink

from sipsimple.configuration.datatypes import Path
from blink.util import run_in_gui_thread


__all__ = ['ApplicationData', 'Resources', 'IconManager']


class DirectoryContextManager(str):
    def __enter__(self):
        self.directory = os.getcwd()
        os.chdir(self)

    def __exit__(self, type, value, traceback):
        os.chdir(self.directory)


class ApplicationData(object):
    """Provide access to user data"""

    _cached_directory = None

    @classproperty
    def directory(cls):
        if cls._cached_directory is None:
            if platform.system() == 'Darwin':
                from Foundation import NSApplicationSupportDirectory, NSSearchPathForDirectoriesInDomains, NSUserDomainMask
                cls._cached_directory = os.path.join(NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], 'Blink')
            elif platform.system() == 'Windows':
                cls._cached_directory = os.path.join(os.environ['APPDATA'], 'Blink')
            else:
                cls._cached_directory = Path('~/.blink').normalized
        return DirectoryContextManager(cls._cached_directory)

    @classmethod
    def get(cls, resource):
        return os.path.join(cls.directory, os.path.normpath(resource))


class Resources(object):
    """Provide access to Blink's resources"""

    _cached_directory = None

    @classproperty
    def directory(cls):
        if cls._cached_directory is None:
            parent_dir = pathlib.Path(__file__).parent.absolute()
            try:
                binary_directory = os.path.dirname(os.path.realpath(__main__.__file__))
            except AttributeError:
                if hasattr(sys, 'frozen'):
                    application_directory = os.path.dirname(os.path.realpath(sys.executable))
                else:
                    application_directory = os.path.realpath('')  # executed in interactive interpreter
            else:
                if os.path.basename(binary_directory) == 'bin':
                    application_directory = os.path.dirname(binary_directory)
                else:
                    application_directory = binary_directory
            unit_test_dir = '%s/../../../../resources' % parent_dir
            if os.path.exists(os.path.join(unit_test_dir, 'blink.ui')):
                # this is to make happy the self unit-testing, there must be a better way -adi
                cls._cached_directory = unit_test_dir
            elif os.path.exists(os.path.join(application_directory, 'resources', 'blink.ui')):
                cls._cached_directory = os.path.join(application_directory, 'resources')
            else:
                cls._cached_directory = os.path.join(application_directory, 'share', 'blink')
        return DirectoryContextManager(cls._cached_directory)

    @classmethod
    def get(cls, resource):
        return os.path.join(cls.directory, os.path.normpath(resource))


class IconManager(object, metaclass=Singleton):
    max_size = 256

    def __init__(self):
        self.iconmap = {}

    @run_in_gui_thread(wait=True)
    def get(self, id):
        id = id.replace('/', '_')
        try:
            return self.iconmap[id]
        except KeyError:
            pixmap = QPixmap()
            filename = ApplicationData.get(os.path.join('images', id + '.png'))
            try:
                with open(filename, 'rb') as f:
                    data = f.read()
            except (IOError, OSError):
                data = None
            if data is not None and pixmap.loadFromData(data):
                icon = QIcon(pixmap)
                icon.filename = filename
                icon.content = data
                icon.content_type = 'image/png'
            else:
                icon = None
            return self.iconmap.setdefault(id, icon)

    @run_in_gui_thread(wait=True)
    def store_data(self, id, data):
        data = data if isinstance(data, bytes) else data.encode()
        id = id.replace('/', '_')
        directory = ApplicationData.get('images')
        filename = os.path.join(directory, id + '.png')
        makedirs(directory)
        pixmap = QPixmap()
        if data is not None and pixmap.loadFromData(data):
            image_size = pixmap.size()
            if image_size.width() > self.max_size or image_size.height() > self.max_size:
                pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            if imghdr.what(None, data) != 'png' or pixmap.size() != image_size:
                buffer = QBuffer()
                pixmap.save(buffer, 'png')
                data = str(buffer.data())
            with open(filename, 'wb') as f:
                data = data if isinstance(data, bytes) else data.encode()
                f.write(data)
            icon = QIcon(pixmap)
            icon.filename = filename
            icon.content = data
            icon.content_type = 'image/png'
        else:
            unlink(filename)
            icon = None
        self.iconmap[id] = icon
        return icon

    @run_in_gui_thread(wait=True)
    def store_file(self, id, file):
        id = id.replace('/', '_')
        directory = ApplicationData.get('images')
        filename = os.path.join(directory, id + '.png')
        if filename == os.path.normpath(file):
            return self.iconmap.get(id, None)
        makedirs(directory)
        pixmap = QPixmap()
        if file is not None and pixmap.load(file):
            if pixmap.size().width() > self.max_size or pixmap.size().height() > self.max_size:
                pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            buffer = QBuffer()
            pixmap.save(buffer, 'png')
            data = buffer.data()
            with open(filename, 'wb') as f:
                f.write(data)
            icon = QIcon(pixmap)
            icon.filename = filename
            icon.content = data
            icon.content_type = 'image/png'
        else:
            unlink(filename)
            icon = None
        self.iconmap[id] = icon
        return icon

    @run_in_gui_thread(wait=True)
    def remove(self, id):
        id = id.replace('/', '_')
        self.iconmap.pop(id, None)
        unlink(ApplicationData.get(os.path.join('images', id + '.png')))