import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui
import maya.api.OpenMayaRender as omr
import maya.cmds as cmds

import const, mayaUtils
import reflectionPlaneTpl

import pluginUtils
log = pluginUtils.log.getLogger('V3D-MY')

NODE_TYPE = 'v3dReflectionPlane'

# just for convenience to use from other modules
NODE_NAME = 'v3dReflectionPlane'
NODE_SHELF_NAME = 'v3dReflectionPlaneShape'

DRAW_CLASSIFICATION = 'drawdb/geometry/v3dReflectionPlane'
DRAW_REGISTRANT_ID = 'v3dReflectionPlane'

# colors in the linear space, converted to sRGB in Maya's viewport
COL_LIGHT_BLUE = om.MColor((0.1329, 0.4078, 0.9463, 1))
COL_LIGHT_GREEN = om.MColor((0.0562, 1, 0.366, 1))

# setting it other than 1 leads to rendering issues on certain drivers/devices
LINE_WIDTH = 1

PLANE_SIZE = 1 # in sys units
ORIGIN = om.MPoint(0, 0, 0)
AXIS_X = om.MVector(1, 0, 0)
AXIS_Y = om.MVector(0, 1, 0)
AXIS_Z = om.MVector(0, 0, 1)

def maya_useNewAPI():
    pass

class Shape(om.MPxSurfaceShape):

    @classmethod
    def creator(cls):
        return cls()

    @classmethod
    def initializer(cls):
        eAttr = om.MFnEnumAttribute()
        nAttr = om.MFnNumericAttribute()
        mAttr = om.MFnMessageAttribute()

        influenceDistance = nAttr.create('influenceDistance', 'id',
                om.MFnNumericData.kFloat, .1)
        nAttr.setMin(0)
        nAttr.setSoftMax(50)
        cls.addAttribute(influenceDistance)

        fallOff = nAttr.create('falloff', 'f', om.MFnNumericData.kFloat, .5)
        nAttr.setMin(0)
        nAttr.setMax(1)
        cls.addAttribute(fallOff)

        clipStart = nAttr.create('clipStart', 'cs',
                om.MFnNumericData.kFloat, 0.1)
        nAttr.setMin(0)
        nAttr.setSoftMax(50)
        cls.addAttribute(clipStart)

        visibilitySelSet = mAttr.create('visibilitySelectionSet', 'vsset')
        cls.addAttribute(visibilitySelSet)

        invertVisibilitySelSet = nAttr.create('invertVisibilitySelectionSet',
                'ivsset', om.MFnNumericData.kBoolean, False)
        cls.addAttribute(invertVisibilitySelSet)

        planeScaleY = nAttr.create('planeScaleY', 'sy', om.MFnNumericData.kFloat, 1)
        nAttr.hidden = True
        nAttr.storable = True
        cls.addAttribute(planeScaleY)

        cls.attributeAffects(planeScaleY, influenceDistance)

        # "dirty" plugs - for updating the viewport on change
        cls.influenceDistance = influenceDistance
        cls.fallOff = fallOff
        cls.planeScaleY = planeScaleY

    def __init__(self):
        super(Shape, self).__init__()

    def setDependentsDirty(self, plugBeingDirtied, affectedPlugs):
        if (plugBeingDirtied == self.influenceDistance
                or plugBeingDirtied == self.fallOff
                or plugBeingDirtied == self.planeScaleY):
            omr.MRenderer.setGeometryDrawDirty(self.thisMObject(), True)
        return super(Shape, self).setDependentsDirty(plugBeingDirtied, affectedPlugs)

    def getShapeSelectionMask(self):
        return om.MSelectionMask(om.MSelectionMask.kSelectMeshes)


class ShapeUI(omui.MPxSurfaceShapeUI):

    @classmethod
    def creator(cls):
        return cls()

    def __init__(self):
        super(ShapeUI, self).__init__()


class ShapeUserData(om.MUserData):

    def __init__(self):
        super(ShapeUserData, self).__init__()

        self.influenceDistance = .1
        self.fallOff = .5
        self.selected = False


class ShapeDrawOverride(omr.MPxDrawOverride):

    @classmethod
    def creator(cls, obj):
        return cls(obj)

    def __init__(self, obj):
        super(ShapeDrawOverride, self).__init__(obj, None, False)

    def hasUIDrawables(self):
        return True

    def supportedDrawAPIs(self):
        return omr.MRenderer.kAllDevices

    def prepareForDraw(self, objPath, cameraPath, frameContext, oldData):
        depNode = om.MFnDependencyNode(objPath.node())

        data = ShapeUserData()
        data.influenceDistance = depNode.findPlug('influenceDistance', True).asDouble()
        data.fallOff = depNode.findPlug('falloff', True).asDouble()
        data.selected = shapeIsSelected(objPath)
        data.planeScaleY = depNode.findPlug('planeScaleY', True).asDouble()

        return data

    def addUIDrawables(self, objPath, drawManager, frameContext, data):
        drawManager.beginDrawable()
        drawManager.setLineWidth(LINE_WIDTH)
        drawManager.setColor(COL_LIGHT_GREEN if data.selected else COL_LIGHT_BLUE)

        # no Y scaling, then sys -> cm
        halfEdgeSize = data.influenceDistance / data.planeScaleY / mayaUtils.getScaleFactor()
        fallOff = data.fallOff

        # sys -> cm
        planeSize = PLANE_SIZE / mayaUtils.getScaleFactor()

        drawManager.rect(ORIGIN, AXIS_X, AXIS_Y, planeSize, planeSize)

        # distance rects
        drawManager.rect(om.MPoint(0, halfEdgeSize, 0), AXIS_X, AXIS_Y, planeSize, planeSize)
        drawManager.rect(om.MPoint(0, -halfEdgeSize, 0), AXIS_X, AXIS_Y, planeSize, planeSize)

        # fallOff rects
        drawManager.rect(om.MPoint(0, halfEdgeSize * (1 - fallOff), 0), AXIS_X, AXIS_Y, planeSize, planeSize)
        drawManager.rect(om.MPoint(0, -halfEdgeSize * (1 - fallOff), 0), AXIS_X, AXIS_Y, planeSize, planeSize)

        # up lines
        drawManager.line(om.MPoint(planeSize, halfEdgeSize, -planeSize), om.MPoint(planeSize, -halfEdgeSize, -planeSize))
        drawManager.line(om.MPoint(planeSize, halfEdgeSize, planeSize), om.MPoint(planeSize, -halfEdgeSize, planeSize))
        drawManager.line(om.MPoint(-planeSize, halfEdgeSize, -planeSize), om.MPoint(-planeSize, -halfEdgeSize, -planeSize))
        drawManager.line(om.MPoint(-planeSize, halfEdgeSize, planeSize), om.MPoint(-planeSize, -halfEdgeSize, planeSize))

        drawManager.endDrawable()


def shapeIsSelected(objPath):
    selList = om.MGlobal.getActiveSelectionList()
    if selList.hasItem(objPath):
        return True

    parents = cmds.listRelatives(om.MDagPath(objPath), parent=True, fullPath=True)
    if parents:
        parentSelList = om.MSelectionList()
        parentSelList.add(parents[0])
        parentDagPath = parentSelList.getDagPath(0)
        if selList.hasItem(parentDagPath):
            return True

    return False


def register(mplugin):
    try:
        reflectionPlaneTpl.register(NODE_TYPE)
        mplugin.registerShape(NODE_TYPE, const.V3D_REFLECTION_PLANE_ID, Shape.creator,
                Shape.initializer, ShapeUI.creator, DRAW_CLASSIFICATION)
        omr.MDrawRegistry.registerDrawOverrideCreator(
                DRAW_CLASSIFICATION, DRAW_REGISTRANT_ID,
                ShapeDrawOverride.creator)
    except:
        log.error('Failed to register shape node: {}'.format(NODE_TYPE))
        raise


def unregister(mplugin):
    try:
        reflectionPlaneTpl.unregister()
        omr.MDrawRegistry.deregisterDrawOverrideCreator(
                DRAW_CLASSIFICATION, DRAW_REGISTRANT_ID)
        mplugin.deregisterNode(const.V3D_REFLECTION_PLANE_ID)
    except:
        log.error('Failed to unregister shape node: {}'.format(NODE_TYPE))
        raise
