#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Created on 2019年7月28日
@author: Irony
@site: https://pyqt5.com https://github.com/PyQt5
@email: 892768447@qq.com
@file: CustomWidgets.CFontIcon
@description: 字体图标
"""
import json
import os

from PyQt5.QtCore import Qt, QPoint, QRect, QTimer
from PyQt5.QtGui import QFontDatabase, QIcon, QIconEngine, QPixmap, QPainter,\
    QFont


__Author__ = 'Irony'
__Copyright__ = 'Copyright (c) 2019'
__Version__ = 'Version 1.0'


class CIconEngine(QIconEngine):
    """图标绘制引擎
    """

    def __init__(self, font, *args, **kwargs):
        super(CIconEngine, self).__init__(*args, **kwargs)
        self.font = font
        self.icon = None

    def setIcon(self, icon):
        self.icon = icon

    def paint(self, painter, rect, mode, state):
        painter.save()
        self.font.setPixelSize(round(0.875 * min(rect.width(), rect.height())))
        painter.setFont(self.font)
        if self.icon:
            if self.icon.animation:
                self.icon.animation.paint(painter, rect)
            ms = self.icon._getMode(mode) * self.icon._getState(state)
            text, color = self.icon.icons.get(ms, (None, None))
            if text == None and color == None:
                return
            painter.setPen(color)
            self.text = text if text else self.text
            painter.drawText(
                rect, int(Qt.AlignCenter | Qt.AlignVCenter), self.text)
        painter.restore()

    def pixmap(self, size, mode, state):
        pixmap = QPixmap(size)
        pixmap.fill(Qt.transparent)
        self.paint(QPainter(pixmap), QRect(QPoint(0, 0), size), mode, state)
        return pixmap


class CIconAnimationSpin:

    def __init__(self, parent, interval=10, step=4):
        self.parent = parent
        self.angle = 0
        self.timer = None
        self.interval = interval
        self.step = step

    def update(self):
        if self.angle >= 360:
            self.angle = 0
        self.angle += self.step
        self.parent.update()

    def paint(self, painter, rect):
        if not self.timer:
            self.timer = QTimer(self.parent, timeout=self.update)
            self.timer.start(self.interval)
        else:
            x_center = rect.width() * 0.5
            y_center = rect.height() * 0.5
            painter.translate(x_center, y_center)
            painter.rotate(self.angle)
            painter.translate(-x_center, -y_center)


class CIcon(QIcon):

    # New Mode
    Normal = int(QIcon.Normal) + 2
    Disabled = int(QIcon.Disabled) + 2
    Active = int(QIcon.Active) + 2
    Selected = int(QIcon.Selected) + 2

    # New State
    Off = int(QIcon.Off) + 6
    On = int(QIcon.On) + 6

    def __init__(self, engine, fontMap, animation=None):
        super(CIcon, self).__init__(engine)
        engine.setIcon(self)
        self.animation = animation
        self.fontMap = fontMap
        self.icons = {}
        self.modestate = [m * s for m in [
            self.Normal, self.Disabled, self.Active, self.Selected] for s in [self.Off, self.On]]

    def setAnimation(self, animation):
        self.animation = animation

    def add(self, name, color=Qt.black, mode=QIcon.Normal, state=QIcon.Off):
        """添加或者更新一个指定mode和state的字体和颜色
        :param name:
        :param color:
        :param mode:
        :param state:
        """
        ms = self._getMode(mode) * self._getState(state)
        self.icons[ms] = [self.fontMap.get(name, ''), color]
        return self

    def _getMode(self, mode):
        """修改QIcon::Mode值+2,比如
        QIcon::Normal    0          ->        2
        QIcon::Disabled    1        ->        3
        QIcon::Active    2          ->        4
        QIcon::Selected    3        ->        5
        :param mode:
        """
        return int(mode) + 2

    def _getState(self, state):
        """修改QIcon::State值+6,比如
        QIcon::Off    1             ->        7
        QIcon::On    0              ->        6
        :param state:
        """
        return int(state) + 6


class CIconLoader:
    """字体图标加载器
    """

    def __init__(self, ttfFile, mapFile):
        """
        :param ttfFile:            ttf字体文件路径
        :param mapFile:            ttf字体文件对应的字符映射 json格式
        """
        fontId = QFontDatabase.addApplicationFont(ttfFile)
        fontFamilies = QFontDatabase.applicationFontFamilies(fontId)
        if fontFamilies:
            self._font = QFont(fontFamilies[0])
            self.fontMap = json.loads(open(mapFile, 'rb').read().decode(
                encoding='utf_8', errors='ignore'), encoding='utf_8', object_hook=self.object_hook)
        else:
            self._font = QFont()
            self.fontMap = {}

    @classmethod
    def fontAwesome(cls):
        """FontAwesome字体
        :param cls:
        """
        dirPath = os.path.dirname(__file__)
        return cls(
            os.path.join(dirPath, 'Fonts', 'fontawesome-webfont.ttf'),
            os.path.join(dirPath, 'Fonts', 'fontawesome-webfont.json'),
        )

    @classmethod
    def fontMaterial(cls):
        """material字体
        :param cls:
        """
        dirPath = os.path.dirname(__file__)
        return cls(
            os.path.join(dirPath, 'Fonts', 'materialdesignicons-webfont.ttf'),
            os.path.join(dirPath, 'Fonts', 'materialdesignicons-webfont.json'),
        )

    def icon(self, name, animation=None):
        """根据键值返回一个字体图标
        :param name:
        """
        return CIcon(CIconEngine(self._font), self.fontMap, animation).add(name)

    @property
    def font(self):
        return self._font

    def value(self, name):
        """返回对应的字符
        :param name:
        """
        return self.fontMap.get(name, '')

    def object_hook(self, obj):
        result = {}
        for key in obj:
            result[key] = chr(int(obj[key], 16))
        return result