# -*- coding: utf-8 -*-

import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx

kPluginNodeTypeName = "spClosestPointOnCurve"
cpocPluginId = OpenMaya.MTypeId(0x00349)

# Node definition
class closestPointOnCurve(OpenMayaMPx.MPxNode):
    # the plugs
    aInCurve = OpenMaya.MObject()

    aInPosition = OpenMaya.MObject()

    aOutPosition = OpenMaya.MObject()
    aOutPositionX = OpenMaya.MObject()
    aOutPositionY = OpenMaya.MObject()
    aOutPositionZ = OpenMaya.MObject()

    aOutNormal = OpenMaya.MObject()
    aOutNormalX = OpenMaya.MObject()
    aOutNormalY = OpenMaya.MObject()
    aOutNormalZ = OpenMaya.MObject()

    aOutParam = OpenMaya.MObject()

    def __init__(self):
        OpenMayaMPx.MPxNode.__init__(self)

    def compute(self, plug, dataBlock):
        if (
            plug == closestPointOnCurve.aOutPosition
            or plug == closestPointOnCurve.aOutParam
        ):
            dataHandle = dataBlock.inputValue(closestPointOnCurve.aInCurve)
            inputAsCurve = dataHandle.asNurbsCurve()

            # if not inputAsCurve.hasFn(OpenMaya.MFn.kNurbsCurve):
            #    return OpenMaya.kUnknownParameter

            dataHandle = dataBlock.inputValue(closestPointOnCurve.aInPosition)

            inPositionAsFloat3 = dataHandle.asFloat3()
            inPosition = OpenMaya.MPoint(
                inPositionAsFloat3[0], inPositionAsFloat3[1], inPositionAsFloat3[2]
            )

            # connect the MFnNurbsCurve
            # and ask the closest point

            nurbsCurveFn = OpenMaya.MFnNurbsCurve(inputAsCurve)

            # get and set outPosition
            outParam = OpenMaya.MScriptUtil()
            outParam.createFromDouble(0)
            outParamPtr = outParam.asDoublePtr()

            # get position and paramater
            outPosition = nurbsCurveFn.closestPoint(
                inPosition, True, outParamPtr, 0.001, OpenMaya.MSpace.kWorld
            )

            outputHandle = dataBlock.outputValue(closestPointOnCurve.aOutPosition)
            outputHandle.set3Float(outPosition.x, outPosition.y, outPosition.z)

            # get and set outNormal
            # outNormal = nurbsCurveFn.normal(parameter, OpenMaya.MSpace.kWorld)
            # outputHandle = dataBlock.outputValue(closestPointOnCurve.aOutNormal)
            # outputHandle.set3Float(outNormal.x, outNormal.y, outNormal.z)
            # outputHandle.set3Float(0, 1, 0 )

            # get and set the uvs
            outputHandle = dataBlock.outputValue(closestPointOnCurve.aOutParam)
            # outputHandle.setFloat(OpenMaya.MScriptUtil(outParamPtr).asDouble())
            outputHandle.setFloat(OpenMaya.MScriptUtil.getDouble(outParamPtr))

            dataBlock.setClean(plug)
        else:
            return OpenMaya.kUnknownParameter


# creator
def nodeCreator():
    return OpenMayaMPx.asMPxPtr(closestPointOnCurve())


# initializer
def nodeInitializer():
    tAttr = OpenMaya.MFnTypedAttribute()
    nAttr = OpenMaya.MFnNumericAttribute()

    # input curve
    closestPointOnCurve.aInCurve = tAttr.create(
        "inCurve", "ic", OpenMaya.MFnData.kNurbsCurve
    )
    tAttr.setStorable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aInCurve)

    # input position
    closestPointOnCurve.aInPositionX = nAttr.create(
        "inPositionX", "ipx", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(1)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aInPositionX)

    closestPointOnCurve.aInPositionY = nAttr.create(
        "inPositionY", "ipy", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(1)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aInPositionY)

    closestPointOnCurve.aInPositionZ = nAttr.create(
        "inPositionZ", "ipz", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(1)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aInPositionZ)

    closestPointOnCurve.aInPosition = nAttr.create(
        "inPosition",
        "ip",
        closestPointOnCurve.aInPositionX,
        closestPointOnCurve.aInPositionY,
        closestPointOnCurve.aInPositionZ,
    )
    nAttr.setStorable(1)
    nAttr.setKeyable(1)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aInPosition)

    # output position
    closestPointOnCurve.aOutPositionX = nAttr.create(
        "outPositionX", "opx", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutPositionX)

    closestPointOnCurve.aOutPositionY = nAttr.create(
        "outPositionY", "opy", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutPositionY)

    closestPointOnCurve.aOutPositionZ = nAttr.create(
        "outPositionZ", "opz", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutPositionZ)

    closestPointOnCurve.aOutPosition = nAttr.create(
        "outPosition",
        "op",
        closestPointOnCurve.aOutPositionX,
        closestPointOnCurve.aOutPositionY,
        closestPointOnCurve.aOutPositionZ,
    )
    nAttr.setStorable(0)
    nAttr.setKeyable(0)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutPosition)

    # output normal
    closestPointOnCurve.aOutNormalX = nAttr.create(
        "outNormalX", "onx", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutNormalX)

    closestPointOnCurve.aOutNormalY = nAttr.create(
        "outNormalY", "ony", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutNormalY)

    closestPointOnCurve.aOutNormalZ = nAttr.create(
        "outNormalZ", "onz", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setWritable(0)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutNormalZ)

    closestPointOnCurve.aOutNormal = nAttr.create(
        "outNormal",
        "on",
        closestPointOnCurve.aOutNormalX,
        closestPointOnCurve.aOutNormalY,
        closestPointOnCurve.aOutNormalZ,
    )
    nAttr.setStorable(0)
    nAttr.setKeyable(0)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutNormal)

    closestPointOnCurve.aOutParam = nAttr.create(
        "outParam", "opa", OpenMaya.MFnNumericData.kFloat, 0.0
    )
    nAttr.setStorable(0)
    nAttr.setKeyable(0)
    nAttr.setWritable(1)
    closestPointOnCurve.addAttribute(closestPointOnCurve.aOutParam)

    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInCurve, closestPointOnCurve.aOutPosition
    )
    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInPosition, closestPointOnCurve.aOutPosition
    )

    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInCurve, closestPointOnCurve.aOutParam
    )
    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInPosition, closestPointOnCurve.aOutParam
    )

    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInCurve, closestPointOnCurve.aOutNormal
    )
    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aInPosition, closestPointOnCurve.aOutNormal
    )

    closestPointOnCurve.attributeAffects(
        closestPointOnCurve.aOutParam, closestPointOnCurve.aOutPosition
    )


# initialize the script plug-in
def initializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject, "Erkan Ozgur Yilmaz", "1.0.2")
    try:
        mplugin.registerNode(
            kPluginNodeTypeName, cpocPluginId, nodeCreator, nodeInitializer
        )
    except:
        sys.stderr.write("Failed to register node: %s" % kPluginNodeTypeName)
        raise


# uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(cpocPluginId)
    except:
        sys.stderr.write("Failed to deregister node: %s" % kPluginNodeTypeName)
        raise
