import maya.api.OpenMaya as om
import maya.cmds as cmds
import maya.mel as mel

import tplUtils


class VisibilitySetMenu(tplUtils.SetMenu):

    def __init__(self, linkedAttrName):
        super(VisibilitySetMenu, self).__init__(linkedAttrName)

    def createOptionMenu(self):
        super(VisibilitySetMenu, self).createOptionMenu('Visibility Set')
        cmds.optionMenu(self.optMenu, edit=True, annotation=(
                'Limit nodes that should appear on the reflection cubemap to\n'
                'this visibility set. If not specified all scene nodes will be\n'
                'used.'
                ))

class InfluenceSetMenu(tplUtils.SetMenu):

    def __init__(self, linkedAttrName):
        super(InfluenceSetMenu, self).__init__(linkedAttrName)

    def createOptionMenu(self):
        super(InfluenceSetMenu, self).createOptionMenu('Influence Set')
        cmds.optionMenu(self.optMenu, edit=True, annotation=(
                'Limit nodes that should be affected by the reflection cubemap\n'
                'to this influence set. If not specified then the Influence\n'
                'Type and Influence Distance settings are used to determine the\n'
                'influenced nodes.'
                ))

visSetMenu = VisibilitySetMenu('visibilitySelectionSet')
infSetMenu = InfluenceSetMenu('influenceSelectionSet')

# NOTE: needs to be available for import to access them from the MEL callbacks
visSetMenuNew = visSetMenu.callbackNew
visSetMenuReplace = visSetMenu.callbackReplace
infSetMenuNew = infSetMenu.callbackNew
infSetMenuReplace = infSetMenu.callbackReplace


def onUseCustomParallaxChanged(nodeName):
    dimFlag = not cmds.getAttr(nodeName + '.useCustomParallax')
    cmds.editorTemplate(dimControl=[nodeName, 'parallaxType', dimFlag])
    cmds.editorTemplate(dimControl=[nodeName, 'parallaxDistance', dimFlag])

def onUseCustomInfluenceChanged(nodeName):
    dimFlag = not cmds.getAttr(nodeName + '.useCustomInfluence')
    cmds.editorTemplate(dimControl=[nodeName, 'invertInfluenceSelectionSet', dimFlag])
    infSetMenu.dimControl(dimFlag)

def template(nodeName):
    tplUtils.suppressDefaultShapeAttributes()

    # this attribute is hidden, because it's handled via a custom selection menu
    cmds.editorTemplate(suppress='visibilitySelectionSet')
    cmds.editorTemplate(suppress='influenceSelectionSet')

    cmds.editorTemplate(beginLayout='General Settings', collapse=False)
    cmds.editorTemplate('influenceType', addControl=True,
            annotation='The type of the influence volume.')
    cmds.editorTemplate('influenceDistance', addControl=True,
            annotation='The size of the influence volume.')
    cmds.editorTemplate('intensity', addControl=True,
            annotation='The intensity of the reflection cubemap.')
    cmds.editorTemplate('clipStart', addControl=True, label='Clipping Start',
            annotation=(
            'Objects located closer than this value won\'t appear on the\n'
            'reflection cubemap.'
            ))
    cmds.editorTemplate('clipEnd', addControl=True, label='Clipping End',
            annotation=(
            'Objects located further than this value won\'t appear on the\n'
            'reflection cubemap.'
            ))
    cmds.editorTemplate(endLayout=True)

    cmds.editorTemplate(beginLayout='Visibility Set', collapse=False)
    tplUtils.addControlCallCustom([__name__, 'visSetMenuNew'],
            [__name__, 'visSetMenuReplace'])
    cmds.editorTemplate('invertVisibilitySelectionSet', addControl=True,
            label='Invert Visibility', annotation=(
            'If "Visibility Set" is specified then this option makes it so only\n'
            'objects that don\'t belong to the set are rendered into the\n'
            'reflection cubemap.'
            ))
    cmds.editorTemplate(endLayout=True)

    cmds.editorTemplate(beginLayout='Custom Parallax', collapse=False)
    tplUtils.addControlOnChange('useCustomParallax',
            [__name__, 'onUseCustomParallaxChanged'],
            annotation='Enable Custom Parallax options.')
    cmds.editorTemplate('parallaxType', addControl=True,
            annotation='The type of the parallax volume.')
    cmds.editorTemplate('parallaxDistance', addControl=True,
            annotation='The size of the parallax volume.')
    cmds.editorTemplate(endLayout=True)

    cmds.editorTemplate(beginLayout='Custom Influence', collapse=False)
    tplUtils.addControlOnChange('useCustomInfluence',
            [__name__, 'onUseCustomInfluenceChanged'],
            annotation='Enable Custom Influence options.')
    tplUtils.addControlCallCustom([__name__, 'infSetMenuNew'],
            [__name__, 'infSetMenuReplace'])
    cmds.editorTemplate('invertInfluenceSelectionSet', addControl=True,
            label='Invert Influence', annotation=(
            'If "Influence Set" is specified then this option makes it so only\n'
            'objects that don\'t belong to the set are affected by the\n'
            'reflection cubemap.'
            ))
    cmds.editorTemplate(endLayout=True)


addedCbId = None
removedCbId = None
nameChangedCbId = None
afterSceneReadCbId = None

def register(nodeType):
    mel.eval("""
    global proc AE%sTemplate(string $nodeName) {
        python("import %s as tpl");
        python("tpl.template(\\"" + $nodeName + "\\")");
    };""" % (nodeType, __name__))


    menus = [visSetMenu, infSetMenu]

    def objectSetAdded(objectSet, clientData):
        for menu in menus:
            menu.objectSetAddedCb(objectSet)

    def objectSetRemoved(objectSet, clientData):
        for menu in menus:
            menu.objectSetRemovedCb(objectSet)

    def nameChanged(node, prevName, clientData):
        """
        NOTE: Nodes cannot have empty names, an empty prevName means that the
        node was created just now and there was not actual renaming. Also, an
        empty name can collide with the empty menu option ('' means nothing
        selected), hence not doing anything here in this case.
        """
        if prevName == '':
            return

        """
        The "nameChanged" node callback itself doesn't support filtering nodes
        by type like "nodeAdded" and "nodeRemoved"
        """
        if node.apiType() != om.MFn.kSet:
            return

        for menu in menus:
            menu.objectSetNameChangedCb(node, prevName)

    def afterSceneRead(clientData):
        for menu in menus:
            menu.afterSceneReadCb()

    global addedCbId
    global removedCbId
    global nameChangedCbId
    global afterSceneReadCbId
    addedCbId = om.MDGMessage.addNodeAddedCallback(objectSetAdded, 'objectSet')
    removedCbId = om.MDGMessage.addNodeRemovedCallback(objectSetRemoved, 'objectSet')
    nameChangedCbId = om.MNodeMessage.addNameChangedCallback(om.MObject(), nameChanged)
    afterSceneReadCbId = om.MSceneMessage.addCallback(
            om.MSceneMessage.kAfterSceneReadAndRecordEdits, afterSceneRead)

def unregister():
    om.MMessage.removeCallback(addedCbId)
    om.MMessage.removeCallback(removedCbId)
    om.MMessage.removeCallback(nameChangedCbId)
    om.MMessage.removeCallback(afterSceneReadCbId)
