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'

# just for convenience to use from other modules
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']

# 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

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)

        # "dirty" plugs - for updating the viewport on change
        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:
            # sys -> cm
            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:
            # sys -> cm
            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
