import random

import ngSkinTools2.api
from PySide2 import QtWidgets, QtCore
from ngSkinTools2.api import Mirror
from ngSkinTools2.api.layers import generate_layer_name
from ngSkinTools2.ui import qt, dialogs

from maya import cmds

from ngSkinTools2 import signal
from ngSkinTools2.decorators import undoable
from ngSkinTools2.log import getLogger
from ngSkinTools2.ui.action import Action
from ngSkinTools2.ui.options import config
from ngSkinTools2.ui.session import session, withSession

logger = getLogger("layer operations")


@withSession
@undoable
def initializeLayers(createFirstLayer=True):
    if session.state.layersAvailable:
        return  # ignore

    target = session.state.selectedSkinCluster

    layers = ngSkinTools2.api.init_layers(target)
    with ngSkinTools2.api.suspend_updates(target):
        if createFirstLayer:
            layer = layers.add("Base weights")
            layer.set_current()
        Mirror(target).set_mirror_config(config.mirrorInfluencesDefaults)

    session.events.targetChanged.emitIfChanged()

    if ngSkinTools2.api.is_slow_mode_skin_cluster(target):
        dialogs.info(
            "ngSkinTools switched to slow maya API for setting skin cluster weights for this skinCluster, to workaround a Maya bug when skinCluster uses dg nodes as inputs"
        )


@undoable
def addLayer():
    layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)

    def guessParent():
        currentLayer = layers.current_layer()
        if currentLayer is None:
            return None

        # current layer is a parent?
        if currentLayer.num_children > 0:
            return currentLayer

        return currentLayer.parent

    with ngSkinTools2.api.suspend_updates(layers.mesh):
        new_layer = layers.add(generate_layer_name(session.state.all_layers, "New Layer"))
        new_layer.parent = guessParent()

        session.events.layerListChanged.emitIfChanged()
        setCurrentLayer(new_layer)

    return new_layer


def build_action_initialize_layers(session, parent):
    from ngSkinTools2.ui import actions
    from . import import_v1_actions

    def do_initialize():
        if import_v1_actions.can_import(session):
            q = (
                "Skinning layers from previous version of ngSkinTools are present on this mesh. This operation will initialize "
                "skinning layers from scratch, discarding previous layers information. Do you want to continue?"
            )
            if not dialogs.yesNo(q):
                return

        initializeLayers()

    result = actions.define_action(parent, "Initialize Skinning Layers", callback=do_initialize)

    @signal.on(session.events.nodeSelectionChanged)
    def update():
        result.setEnabled(session.state.selectedSkinCluster is not None)

    update()

    return result


def buildAction_createLayer(session, parent):
    from ngSkinTools2.ui import actions

    result = actions.define_action(parent, "Create Layer", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)

    @signal.on(session.events.targetChanged)
    def update_to_target():
        result.setEnabled(session.state.layersAvailable)

    update_to_target()

    return result


def buildAction_deleteLayer(session, parent):
    from ngSkinTools2.ui import actions

    result = actions.define_action(parent, "Delete Layer", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)

    @signal.on(session.context.selected_layers.changed, session.events.targetChanged, qtParent=parent)
    def update_to_target():
        result.setEnabled(session.state.layersAvailable and bool(session.context.selected_layers(default=[])))

    update_to_target()

    return result


@undoable
def setCurrentLayer(layer):
    """
    :type layer: ngSkinTools2.api.layers.Layer
    """
    if not session.active():
        logger.info("didn't set current layer: no session")

    if not session.state.layersAvailable:
        logger.info("didn't set current layer: layers not enabled")

    logger.info("setting current layer to %r on %r", layer, session.state.selectedSkinCluster)
    layer.set_current()
    session.events.currentLayerChanged.emitIfChanged()


def getCurrentLayer():
    layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
    return layers.current_layer()


@undoable
def renameLayer(layer, newName):
    layer.name = newName
    cmds.evalDeferred(session.events.layerListChanged.emitIfChanged)


@undoable
def deleteSelectedLayers():
    layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
    for i in session.context.selected_layers(default=[]):
        layers.delete(i)

    session.events.layerListChanged.emitIfChanged()
    session.events.currentLayerChanged.emitIfChanged()


class ToggleEnabledAction(Action):
    name = "Enabled"
    checkable = True

    def __init__(self, session):
        Action.__init__(self, session)
        self.selected_layers = session.context.selected_layers

    def checked(self):
        """
        return true if most of selected layers are enabled
        :return:
        """
        layers = self.selected_layers(default=[])
        if not layers:
            return True

        majorityEnabled = 0
        for layer in layers:
            try:
                # eat up the exception if layer id is invalid
                majorityEnabled += 1 if layer.enabled else -1
            except:
                pass

        return majorityEnabled >= 0

    def run(self):
        enabled = not self.checked()
        selected_layers = self.selected_layers()
        if not selected_layers:
            return

        for i in selected_layers:
            i.enabled = enabled

        logger.info("layers toggled: %r", selected_layers)

        session.events.layerListChanged.emitIfChanged()

    def enabled(self):
        return session.state.layersAvailable and bool(self.selected_layers(default=[]))

    def update_on_signals(self):
        return [self.selected_layers.changed, session.events.layerListChanged, session.events.targetChanged]


def build_action_randomize_influences_colors(session, parent):
    """
    builds a UI action for randomly choosing new colors for influences
    :type session: ngSkinTools2.ui.session.Session
    """

    result = QtWidgets.QAction("Randomize colors", parent)
    result.setToolTip("Choose random colors for each influence, selecting from Maya's pallete of indexed colors")

    def color_filter(c):
        brightness = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]
        return brightness > 0.001 and brightness < 0.99  # just a fancy way to skip white and black

    colors = set([tuple(cmds.colorIndex(i, q=True)) for i in range(1, 30)])
    colors = [c for c in colors if color_filter(c)]

    @qt.on(result.triggered)
    def triggered():
        if session.state.selectedSkinCluster is None:
            return
        layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
        layers.config.influence_colors = {i.logicalIndex: random.choice(colors) for i in layers.list_influences()}

    return result
