# 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 reflectionCubemapTpl

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

NODE_TYPE = 'v3dReflectionCubemap'

NODE_NAME = 'v3dReflectionCubemap'
NODE_SHELF_NAME = 'v3dReflectionCubemapShape'

DRAW_CLASSIFICATION = 'drawdb/geometry/v3dReflectionCubemap'
DRAW_REGISTRANT_ID = 'v3dReflectionCubemap'

VOLUME_TYPE_SPHERE = 0
VOLUME_TYPE_BOX = 1
VOLUME_NAME_ENUM = ['ELIPSOID', 'BOX']
VOLUME_NAME_ENUM_UI = ['Sphere', 'Box']

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

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()

        influenceType = eAttr.create('influenceType', 'it', 0)
        eAttr.addField(VOLUME_NAME_ENUM_UI[0], VOLUME_TYPE_SPHERE)
        eAttr.addField(VOLUME_NAME_ENUM_UI[1], VOLUME_TYPE_BOX)
        cls.addAttribute(influenceType)

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

        intensity = nAttr.create('intensity', 'in', om.MFnNumericData.kFloat, 1)
        nAttr.setMin(0)
        nAttr.setSoftMax(1000)
        cls.addAttribute(intensity)

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

        clipEnd = nAttr.create('clipEnd', 'ce',
                om.MFnNumericData.kFloat, 100)
        nAttr.setMin(0)
        nAttr.setSoftMax(1000)
        cls.addAttribute(clipEnd)

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

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

        useCustomParallax = nAttr.create('useCustomParallax', 'ucp',
                om.MFnNumericData.kBoolean, False)
        cls.addAttribute(useCustomParallax)

        parallaxType = eAttr.create('parallaxType', 'pt', 0)
        eAttr.addField(VOLUME_NAME_ENUM_UI[0], VOLUME_TYPE_SPHERE)
        eAttr.addField(VOLUME_NAME_ENUM_UI[1], VOLUME_TYPE_BOX)
        cls.addAttribute(parallaxType)

        parallaxDistance = nAttr.create('parallaxDistance', 'pd',
                om.MFnNumericData.kFloat, 1)
        nAttr.setMin(0)
        nAttr.setSoftMax(1000)
        cls.addAttribute(parallaxDistance)

        useCustomInfluence = nAttr.create('useCustomInfluence', 'uci',
                om.MFnNumericData.kBoolean, False)
        cls.addAttribute(useCustomInfluence)

        influenceSelSet = mAttr.create('influenceSelectionSet', 'isset')
        cls.addAttribute(influenceSelSet)

        invertInfluenceSelSet = nAttr.create('invertInfluenceSelectionSet',
                'iisset', om.MFnNumericData.kBoolean, False)
        cls.addAttribute(invertInfluenceSelSet)

        cls.influenceType = influenceType
        cls.influenceDistance = influenceDistance

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

    def setDependentsDirty(self, plugBeingDirtied, affectedPlugs):
        if (plugBeingDirtied == self.influenceType
                or plugBeingDirtied == self.influenceDistance):
            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.influenceType = VOLUME_TYPE_SPHERE
        self.influenceDistance = 1
        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.influenceType = depNode.findPlug('influenceType', True).asInt()
        data.influenceDistance = depNode.findPlug('influenceDistance', True).asDouble()
        data.selected = shapeIsSelected(objPath)

        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)

        scale = mayaUtils.getScaleFactor()

        if data.influenceType == VOLUME_TYPE_SPHERE:
            radius = data.influenceDistance / scale
            drawManager.circle(ORIGIN, AXIS_X, radius)
            drawManager.circle(ORIGIN, AXIS_Y, radius)
            drawManager.circle(ORIGIN, AXIS_Z, radius)

        elif data.influenceType == VOLUME_TYPE_BOX:
            halfEdgeSize = data.influenceDistance / scale
            drawManager.box(ORIGIN, AXIS_Y, AXIS_X, halfEdgeSize, halfEdgeSize,
                    halfEdgeSize)

        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 getVolumeTypeName(volumeTypeIdx):
    return VOLUME_NAME_ENUM[volumeTypeIdx]

def register(mplugin):
    try:
        reflectionCubemapTpl.register(NODE_TYPE)
        mplugin.registerShape(NODE_TYPE, const.V3D_REFLECTION_CUBEMAP_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:
        reflectionCubemapTpl.unregister()
        omr.MDrawRegistry.deregisterDrawOverrideCreator(
                DRAW_CLASSIFICATION, DRAW_REGISTRANT_ID)
        mplugin.deregisterNode(const.V3D_REFLECTION_CUBEMAP_ID)
    except:
        log.error('Failed to unregister shape node: {}'.format(NODE_TYPE))
        raise
