# -*- coding: UTF-8 -*-
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it 
# under the terms of the GNU Lesser General Public License as published by 
# the Free Software Foundation, either version 3 of the License, or 
# (at your option) any later version. This library is distributed in the 
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.

import os
import logging

import studiolibrary

from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
from studiovendor.Qt import QtWidgets

from studiolibrarymaya import baseitem
from studiolibrarymaya import baseloadwidget

try:
    import mutils
    import maya.cmds
except ImportError as error:
    print(error)


logger = logging.getLogger(__name__)


def save(path, *args, **kwargs):
    """Convenience function for saving a PoseItem."""
    PoseItem(path).safeSave(*args, **kwargs)


def load(path, *args, **kwargs):
    """Convenience function for loading a PoseItem."""
    PoseItem(path).load(*args, **kwargs)


class PoseLoadWidget(baseloadwidget.BaseLoadWidget):

    @classmethod
    def createFromPath(cls, path, theme=None):

        item = PoseItem(path)
        widget = cls(item)

        if not theme:
            import studiolibrary.widgets
            theme = studiolibrary.widgets.Theme()
            widget.setStyleSheet(theme.styleSheet())

        widget.show()

    def __init__(self, *args, **kwargs):
        super(PoseLoadWidget, self).__init__(*args, **kwargs)

        self._options = None
        self._pose = mutils.Pose.fromPath(self.item().transferPath())

        self.ui.blendFrame = QtWidgets.QFrame(self)

        layout = QtWidgets.QHBoxLayout(self)
        self.ui.blendFrame.setLayout(layout)

        if self.item().libraryWindow():
            self.item().libraryWindow().itemsWidget().itemSliderMoved.connect(self._sliderMoved)
            self.item().libraryWindow().itemsWidget().itemSliderReleased.connect(self._sliderReleased)
            self.item().libraryWindow().itemsWidget().itemDoubleClicked.connect(self._itemDoubleClicked)

        self.ui.blendSlider = QtWidgets.QSlider(self)
        self.ui.blendSlider.setObjectName("blendSlider")
        self.ui.blendSlider.setMinimum(-30)
        self.ui.blendSlider.setMaximum(130)
        self.ui.blendSlider.setOrientation(QtCore.Qt.Horizontal)
        self.ui.blendSlider.sliderMoved.connect(self._sliderMoved)
        self.ui.blendSlider.sliderReleased.connect(self._sliderReleased)

        self.ui.blendEdit = QtWidgets.QLineEdit(self)
        self.ui.blendEdit.setObjectName("blendEdit")
        self.ui.blendEdit.setText("0")
        self.ui.blendEdit.editingFinished.connect(self._blendEditChanged)

        validator = QtGui.QIntValidator(-200, 200, self)
        self.ui.blendEdit.setValidator(validator)

        layout.addWidget(self.ui.blendSlider)
        layout.addWidget(self.ui.blendEdit)

        self.setCustomWidget(self.ui.blendFrame)

    def _itemDoubleClicked(self):
        """Triggered when the user double-clicks a pose."""
        self.accept()

    def _sliderMoved(self, value):
        """Triggered when the user moves the slider handle."""
        self.load(blend=value, batchMode=True)

    def _sliderReleased(self):
        """Triggered when the user releases the slider handle."""
        try:
            self.load(blend=self.ui.blendSlider.value(), refresh=False)
            self._options = {}
        except Exception as error:
            self.item().showErrorDialog(u"错误", error)
            raise

    def _blendEditChanged(self, *args):
        """Triggered when the user changes the blend edit value."""
        try:
            self._options = {}
            self.load(blend=int(self.ui.blendEdit.text()), clearSelection=False)
        except Exception as error:
            self.item().showErrorDialog(u"错误", error)
            raise

    def loadValidator(self, *args, **kwargs):
        self._options = {}
        return super(PoseLoadWidget, self).loadValidator(*args, **kwargs)

    def accept(self):
        """Triggered when the user clicks the apply button."""
        try:
            self._options = {}
            self.load(clearCache=True,  clearSelection=False)
        except Exception as error:
            self.item().showErrorDialog(u"错误", error)
            raise

    def load(
        self,
        blend=100.0,
        refresh=True,
        batchMode=False,
        clearCache=False,
        clearSelection=True,
    ):
        """
        Load the pose item with the current user settings from disc.

        :type blend: float
        :type refresh: bool
        :type batchMode: bool
        :type clearCache: bool
        :type clearSelection: bool
        """
        if batchMode:
            self.formWidget().setValidatorEnabled(False)
        else:
            self.formWidget().setValidatorEnabled(True)

        if not self._options:

            self._options = self.formWidget().values()
            self._options['mirrorTable'] = self.item().mirrorTable()
            self._options['objects'] = maya.cmds.ls(selection=True) or []

            if not self._options["searchAndReplaceEnabled"]:
                self._options["searchAndReplace"] = None

            del self._options["namespaceOption"]
            del self._options["searchAndReplaceEnabled"]

        self.ui.blendEdit.blockSignals(True)
        self.ui.blendSlider.setValue(blend)
        self.ui.blendEdit.setText(str(int(blend)))
        self.ui.blendEdit.blockSignals(False)

        if self.item().libraryWindow():

            self.item().libraryWindow().itemsWidget().blockSignals(True)
            self.item().setSliderValue(blend)
            self.item().libraryWindow().itemsWidget().blockSignals(False)

            if batchMode:
                self.item().libraryWindow().showToastMessage(u"混合比例: {0}%".format(blend))

        try:
            self._pose.load(
                blend=blend,
                refresh=refresh,
                batchMode=batchMode,
                clearCache=clearCache,
                clearSelection=clearSelection,
                **self._options
            )
        finally:
            self.item().setSliderDown(batchMode)


def findMirrorTable(path):
    """
    Get the mirror table object for this item.

    :rtype: mutils.MirrorTable or None
    """
    mirrorTable = None

    mirrorTablePaths = list(studiolibrary.walkup(
            path,
            match=lambda path: path.endswith(".mirror"),
            depth=10,
        )
    )

    if mirrorTablePaths:
        mirrorTablePath = mirrorTablePaths[0]

        path = os.path.join(mirrorTablePath, "mirrortable.json")

        if path:
            mirrorTable = mutils.MirrorTable.fromPath(path)

    return mirrorTable


class PoseItem(baseitem.BaseItem):

    NAME = "Pose"
    EXTENSION = ".pose"
    ICON_PATH = os.path.join(os.path.dirname(__file__), "icons", "pose.png")
    LOAD_WIDGET_CLASS = PoseLoadWidget
    TRANSFER_CLASS = mutils.Pose
    TRANSFER_BASENAME = "pose.json"

    def __init__(self, *args, **kwargs):
        """
        Create a new instance of the pose item from the given path.

        :type path: str
        :type args: list
        :type kwargs: dict
        """
        super(PoseItem, self).__init__(*args, **kwargs)

        self.setSliderEnabled(True)

    def mirrorTableSearchAndReplace(self):
        """
        Get the values for search and replace from the mirror table.

        :rtype: (str, str)
        """
        mirrorTable = findMirrorTable(self.path())

        return mirrorTable.leftSide(), mirrorTable.rightSide()

    def switchSearchAndReplace(self):
        """
        Switch the values of the search and replace field.

        :rtype: (str, str)
        """
        values = self.currentLoadValue("searchAndReplace")
        return values[1], values[0]

    def clearSearchAndReplace(self):
        """
        Clear the search and replace field.

        :rtype: (str, str)
        """
        return '', ''

    def doubleClicked(self):
        """Triggered when the user double-click the item."""
        pass

    def loadSchema(self):
        """
        Get schema used to load the pose item.

        :rtype: list[dict]
        """
        schema = [
            {
                "name": "optionsGroup",
                "title": u"Pose选项",
                "type": "group",
                "order": 2,
            },
            {
                "name": "key",
                "title": u"k帧",
                "type": "bool",
                "inline": True,
                "default": False,
                "persistent": True,
            },
            {
                "name": "mirror",
                "title": u"镜像",
                "type": "bool",
                "inline": True,
                "default": False,
                "persistent": True,
            },
            {
                "name": "additive",
                "title": u"叠加",
                "type": "bool",
                "inline": True,
                "default": False,
                "persistent": True,
            },
            {
                "name": "searchAndReplaceEnabled",
                "title": u"搜索和替换",
                "type": "bool",
                "inline": True,
                "default": False,
                "persistent": True,
            },
            {
                "name": "searchAndReplace",
                "title": "",
                "type": "stringDouble",
                "default": ("", ""),
                "placeholder": (u"搜索", u"替换"),
                "persistent": True,
                "actions": [
                    {
                        "name": u"切换",
                        "callback": self.switchSearchAndReplace,
                    },
                    {
                        "name": u"清除",
                        "callback": self.clearSearchAndReplace,
                    },
                    {
                        "name": u"从镜像表",
                        "enabled": bool(findMirrorTable(self.path())),
                        "callback": self.mirrorTableSearchAndReplace,
                    },
                ]
            },
        ]

        schema.extend(super(PoseItem, self).loadSchema())

        return schema
    
    def mirrorTable(self):
        return findMirrorTable(self.path())

    def loadValidator(self, **values):
        """
        Using the validator to change the state of the mirror option.

        :type values: dict
        :rtype: list[dict]
        """
        # Mirror check box
        mirrorTip = "Cannot find a mirror table!"
        mirrorTable = findMirrorTable(self.path())
        if mirrorTable:
            mirrorTip = "Using mirror table: %s" % mirrorTable.path()

        fields = [
            {
                "name": "mirror",
                "toolTip": mirrorTip,
                "enabled": mirrorTable is not None,
            },
            {
                "name": "searchAndReplace",
                "visible": values.get("searchAndReplaceEnabled")
            },
        ]

        fields.extend(super(PoseItem, self).loadValidator(**values))

        return fields

    def save(self, objects, **kwargs):
        """
        Save all the given object data to the item path on disc.

        :type objects: list[str]
        :type kwargs: dict
        """
        super(PoseItem, self).save(**kwargs)

        # Save the pose to the temp location
        mutils.savePose(
            self.transferPath(),
            objects,
            metadata={"description": kwargs.get("comment", "")}
        )
