# Copyright (c) 2017-2026 Soft8Soft, LLC. All rights reserved.
#
# Use of this software is subject to the terms of the Verge3D license
# agreement provided at the time of installation or download, or which
# otherwise accompanies this software in either electronic or hard copy form.
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'

NODE_NAME = 'v3dReflectionPlane'
NODE_SHELF_NAME = 'v3dReflectionPlaneShape'

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

COL_LIGHT_BLUE = om.MColor((0.1329, 0.4078, 0.9463, 1))
COL_LIGHT_GREEN = om.MColor((0.0562, 1, 0.366, 1))

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)

        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)

        halfEdgeSize = data.influenceDistance / data.planeScaleY / mayaUtils.getScaleFactor()
        fallOff = data.fallOff

        planeSize = PLANE_SIZE / mayaUtils.getScaleFactor()

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

        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)

        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)

        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
