#------Multi-Condition Blender Node--------#
#----------------Version 5.0-------------------#
#--------------By Peter Rabel-----------------#
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys
import math

kcompUtilNodeTypeName = "multiConditionBlender"
kcompUtilNodeClassify = "utility/general"
kcompUtilNodeId = OpenMaya.MTypeId(0x87969)

class mcbNode(OpenMayaMPx.MPxNode):

	#define class variables
	inputTerm = OpenMaya.MObject()
	checkTerm = OpenMaya.MObject()
	condition = OpenMaya.MObject()
	logic = OpenMaya.MObject()
	conditionCompound = OpenMaya.MObject()
	valueIfTrue = OpenMaya.MObject()
	valueIfTrueX = OpenMaya.MObject()
	valueIfTrueY = OpenMaya.MObject()
	valueIfTrueZ = OpenMaya.MObject()
	valueIfFalse = OpenMaya.MObject()
	valueIfFalseX = OpenMaya.MObject()
	valueIfFalseY = OpenMaya.MObject()
	valueIfFalseZ = OpenMaya.MObject()
	outValue = OpenMaya.MObject()
	outValueX = OpenMaya.MObject()
	outValueY = OpenMaya.MObject()
	outValueZ = OpenMaya.MObject()
	cRange = OpenMaya.MObject()
	rangeMin = OpenMaya.MObject()
	rangeMax = OpenMaya.MObject()
	blendRange = OpenMaya.MObject()
	blendRangeMin = OpenMaya.MObject()
	blendRangeMax = OpenMaya.MObject()
	
	def __init__(self):
		OpenMayaMPx.MPxNode.__init__(self)
		
	def compute(self, plug, dataBlock):
	
		# if these attributes are requested, recompute their values
		if plug == mcbNode.outValueX or \
			mcbNode.outValueY or mcbNode.outValueZ:
		
			# get MDataHandle's to attributes
			try:
				inputTerm_dataHandle = dataBlock.inputValue( mcbNode.inputTerm )
			except:
				sys.stderr.write( "Failed to get MDataHandle inputTerm input" )
				raise
			try:
				checkTerm_dataHandle = dataBlock.inputValue( mcbNode.checkTerm )
			except:
				sys.stderr.write( "Failed to get MDataHandle checkTerm output" )
				raise
			try:
				condition_dataHandle = dataBlock.inputValue(mcbNode.condition)
			except:
				sys.stderr.write( "Failed to get MDataHandle condition output" )
				raise
			try:
				logic_dataHandle = dataBlock.inputValue( mcbNode.logic )
			except:
				sys.stderr.write( "Failed to get MDataHandle logic output" )
				raise
			try:
				cRange_dataHandle = dataBlock.inputValue( mcbNode.cRange )
			except:
				sys.stderr.write( "Failed to get MDataHandle range output" )
				raise
			try:
				rangeMin_dataHandle = dataBlock.inputValue( mcbNode.rangeMin )
			except:
				sys.stderr.write( "Failed to get MDataHandle rangeMin output" )
				raise
			try:
				rangeMax_dataHandle = dataBlock.inputValue( mcbNode.rangeMax )
			except:
				sys.stderr.write( "Failed to get MDataHandle rangeMax output" )
				raise
			try:
				blendRange_dataHandle = dataBlock.inputValue( mcbNode.blendRange )
			except:
				sys.stderr.write( "Failed to get MDataHandle blendRange output" )
				raise
			try:
				blendRangeMin_dataHandle = dataBlock.inputValue( mcbNode.blendRangeMin )
			except:
				sys.stderr.write( "Failed to get MDataHandle blendRangeMin output" )
				raise
			try:
				blendRangeMax_dataHandle = dataBlock.inputValue( mcbNode.blendRangeMax )
			except:
				sys.stderr.write( "Failed to get MDataHandle blendRangeMax output" )
				raise
				
			try:
				valueIfTrueX_dataHandle = dataBlock.inputValue( mcbNode.valueIfTrueX )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfTrueX output" )
				raise
			try:
				valueIfTrueY_dataHandle = dataBlock.inputValue( mcbNode.valueIfTrueY )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfTrueY output" )
				raise
			try:
				valueIfTrueZ_dataHandle = dataBlock.inputValue( mcbNode.valueIfTrueZ )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfTrueZ output" )
				raise
				
				
			try:
				valueIfFalseX_dataHandle = dataBlock.inputValue( mcbNode.valueIfFalseX )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfFalseX output" )
				raise
			try:
				valueIfFalseY_dataHandle = dataBlock.inputValue( mcbNode.valueIfFalseY )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfFalseY output" )
				raise
			try:
				valueIfFalseZ_dataHandle = dataBlock.inputValue( mcbNode.valueIfFalseZ )
			except:
				sys.stderr.write( "Failed to get MDataHandle valueIfFalseZ output" )
				raise
				
				
			try:
				outValue_dataHandle = dataBlock.outputValue( mcbNode.outValue )
			except:
				sys.stderr.write( "Failed to get MDataHandle outValueX output" )
				raise
			try:
				outValueX_dataHandle = dataBlock.outputValue( mcbNode.outValueX )
			except:
				sys.stderr.write( "Failed to get MDataHandle outValueX output" )
				raise
			try:
				outValueY_dataHandle = dataBlock.outputValue( mcbNode.outValueY )
			except:
				sys.stderr.write( "Failed to get MDataHandle outValueY output" )
				raise
			try:
				outValueZ_dataHandle = dataBlock.outputValue( mcbNode.outValueZ )
			except:
				sys.stderr.write( "Failed to get MDataHandle outValueZ output" )
				raise
				
			try:
				conditionCompound_dataHandle = dataBlock.inputArrayValue( mcbNode.conditionCompound )
			except:
				sys.stderr.write( "Failed to get MDataHandle condition output" )
				raise
				
			#get values from data handles
			inputTerm = inputTerm_dataHandle.asFloat()
			checkTerm = checkTerm_dataHandle.asFloat()
			condition = condition_dataHandle.asShort()
			logic = logic_dataHandle.asShort()
			conditionCompound = conditionCompound_dataHandle
			valueIfTrueX = valueIfTrueX_dataHandle.asFloat()
			valueIfTrueY = valueIfTrueY_dataHandle.asFloat()
			valueIfTrueZ = valueIfTrueZ_dataHandle.asFloat()
			valueIfFalseX = valueIfFalseX_dataHandle.asFloat()
			valueIfFalseY = valueIfFalseY_dataHandle.asFloat()
			valueIfFalseZ = valueIfFalseZ_dataHandle.asFloat()
			outValue = outValue_dataHandle.asFloat()
			outValueX = outValueX_dataHandle.asFloat()
			outValueY = outValueY_dataHandle.asFloat()
			outValueZ = outValueZ_dataHandle.asFloat()
			cRange = cRange_dataHandle.asFloat()
			rangeMin = rangeMin_dataHandle.asFloat()
			rangeMax = rangeMax_dataHandle.asFloat()
			blendRange = blendRange_dataHandle.asFloat()
			blendRangeMin = blendRangeMin_dataHandle.asFloat()
			blendRangeMax = blendRangeMax_dataHandle.asFloat()
				
		else:
			return OpenMaya.kUnknownParameter
			
		#run computation
		
		#split a list of integers, returns a list of lists for the split and a list of the indicies of the splits
		def intListSplit(list, spl):
			spl = str(spl)
			index = []
			output = []
			for i, l in enumerate(list):
				if str(l) == spl:
					index.append(i)
			for i, x in enumerate(index):
				if i == 0:
					output.append(list[0:x])
				else:
					output.append(list[index[i-1]:x])
			try:
				output.append(list[index[len(index)-1]:len(list)])
			except:
				pass
			return output, index
			
		#split a numeric list at specified indices
		def splitAtIndex(inList, index):
			outList = []
			for i, s in enumerate(index):
				if i == 0:
					outList.append(inList[0:s])
				else:
					outList.append(inList[index[i-1]:s])
			try:
				outList.append(inList[index[len(index)-1]:len(inList)])
			except:
				pass
			return outList
			
		#True False Check Function
		def tfCheck(cnd, log, inTerm, chkTerm, rMin, rMax):

			#Less Than
			if cnd == 0:
				if inTerm < chkTerm and log != 2:
					outBool = True
				elif inTerm >= chkTerm and log == 2:
					outBool = True
				else:
					outBool = False
			#Less Than or Equal
			elif cnd == 1:
				if inTerm <= chkTerm and log != 2:
					outBool = True
				elif inTerm > chkTerm and log == 2:
					outBool = True
				else:
					outBool = False
			#Equal
			elif cnd == 2:
				if inTerm == chkTerm and log != 2:
					outBool = True
				elif inTerm != chkTerm and log == 2:
					outBool = True
				else:
					outBool = False
			#Greater Than or Equal
			elif cnd == 3:
				if inTerm >= chkTerm and log != 2:
					outBool = True
				elif inTerm < chkTerm and log == 2:
					outBool = True
				else:
					outBool = False
			#Greater Than
			elif cnd == 4:
				if inTerm > chkTerm and log != 2:
					outBool = True
				elif inTerm <= chkTerm and log == 2:
					outBool = True
				else:
					outBool = False
			#In Range
			elif cnd == 5:
				if inTerm >= rMin and inTerm <= rMax and log != 2:
					outBool = True
				elif inTerm < rMin or inTerm > rMax:
					if log == 2:
						outBool = True
					else:
						outBool = False
				else:
					outBool = False
			else:
				print 'wtf?'
			
			return outBool
			
			
		def blendTerms(brn, brx, inTerm, vitX, vitY, vitZ, vifX, vifY, vifZ):
			if brx >= brn:
				if inTerm >= brn and inTerm <= brx:
					rangePercent = (inTerm - brn) / (brx - brn)
					rangeValueX = ((vitX - vifX) * rangePercent) + vifX
					rangeValueY = ((vitY - vifY) * rangePercent) + vifY
					rangeValueZ = ((vitZ - vifZ) * rangePercent) + vifZ
					outValueX = rangeValueX
					outValueY = rangeValueY
					outValueZ = rangeValueZ
					return outValueX, outValueY, outValueZ
				else:
					return vitX, vitY, vitZ
			else:
				if inTerm >= brx and inTerm <= brn:
					rangePercent = (inTerm - brx) / (brn - brx)
					rangeValueX = ((vifX - vitX) * rangePercent) + vitX
					rangeValueY = ((vifY - vitY) * rangePercent) + vitY
					rangeValueZ = ((vifZ - vitZ) * rangePercent) + vitZ
					outValueX = rangeValueX
					outValueY = rangeValueY
					outValueZ = rangeValueZ
					return outValueX, outValueY, outValueZ
				else:
					return vitX, vitY, vitZ
		
################Compute
		elementCount = conditionCompound.elementCount()
		conditionList = []
		logicList = []
		checkList = []
		rangeMinList = []
		rangeMaxList = []
		
		
		for i in range(elementCount):
			conditionCompound.jumpToElement(i)
			eConditionHandle = conditionCompound.inputValue().child(mcbNode.condition).asShort()
			eLogicHandle = conditionCompound.inputValue().child(mcbNode.logic).asShort()
			eCheckTermHandle = conditionCompound.inputValue().child(mcbNode.checkTerm).asFloat()
			eRangeMinHandle = conditionCompound.inputValue().child(mcbNode.cRange).child(mcbNode.rangeMin).asFloat()
			eRangeMaxHandle = conditionCompound.inputValue().child(mcbNode.cRange).child(mcbNode.rangeMax).asFloat()
			
			conditionList.append(eConditionHandle)
			logicList.append(eLogicHandle)
			checkList.append(eCheckTermHandle)
			rangeMinList.append(eRangeMinHandle)
			rangeMaxList.append(eRangeMaxHandle)
			
		#split into matching condition and logic lists by "OR's" 
		if 1 in logicList:
			splitLogicList = intListSplit(logicList, 1)
			splitConditionList = splitAtIndex(conditionList, splitLogicList[1])
			splitCheckList = splitAtIndex(checkList, splitLogicList[1])
			splitRangeMinList = splitAtIndex(rangeMinList, splitLogicList[1])
			splitRangeMaxList = splitAtIndex(rangeMaxList, splitLogicList[1])

			#Check lists for t/f
			tfList = []
			for i, lis in enumerate(splitLogicList[0]):
				sectionTFList = []
				for x, l in enumerate(lis):
					sectionTFList.append(tfCheck(splitConditionList[i][x], l, inputTerm, splitCheckList[i][x], splitRangeMinList[i][x], splitRangeMaxList[i][x]))
				if False in sectionTFList:
					tfList.append(False)
				else:
					tfList.append(True)
					
			if True in tfList:
				outBool = True
			else:
				outBool = False
		#If no OR's
		else:
			tfList = []
			for i, lis in enumerate(logicList):
				tfList.append(tfCheck(conditionList[i], lis, inputTerm, checkList[i], rangeMinList[i], rangeMaxList[i]))
				
			if False in tfList:
				outBool = False
			else:
				outBool = True
		
		if outBool == True:
			if blendRangeMin != 0 or blendRangeMax != 0:
				val = blendTerms(blendRangeMin, blendRangeMax, inputTerm, valueIfTrueX, valueIfTrueY, valueIfTrueZ, valueIfFalseX, valueIfFalseY, valueIfFalseZ)
				try:
					outValueX = val[0]
					outValueY = val[1]
					outValueZ = val[2]
				except:
					pass
			else:
				outValueX = valueIfTrueX
				outValueY = valueIfTrueY
				outValueZ = valueIfTrueZ
		else:
			outValueX = valueIfFalseX
			outValueY = valueIfFalseY
			outValueZ = valueIfFalseZ

				
		#Set output attribute values
		outValueX_dataHandle.setFloat( outValueX )
		outValueY_dataHandle.setFloat( outValueY )
		outValueZ_dataHandle.setFloat( outValueZ )
				
		dataBlock.setClean(plug)
		
		
		return OpenMaya.MStatus.kSuccess
		
# defines createNode function in maya for this node.
def nodeCreator():
	return OpenMayaMPx.asMPxPtr( mcbNode() )
	
# create and initialize the attributes to the node
def nodeInitializer():

	nAttr = OpenMaya.MFnNumericAttribute()
	cAttr = OpenMaya.MFnCompoundAttribute()
	eAttr = OpenMaya.MFnEnumAttribute()


	# create input attributes
	mcbNode.inputTerm = nAttr.create("inputTerm", "ft", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)

	mcbNode.checkTerm = nAttr.create("checkTerm", "ct", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.rangeMin = nAttr.create("rangeMin", "rn", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.rangeMax = nAttr.create("rangeMax", "rx", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.blendRangeMin = nAttr.create("blendRangeMin", "brn", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.blendRangeMax = nAttr.create("blendRangeMax", "brx", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.condition = eAttr.create("condition", "cnd", 0)
	eAttr.addField("lessThan", 0)
	eAttr.addField("lessThanOrEqual", 1)
	eAttr.addField("equal", 2)
	eAttr.addField("greaterThanOrEqual", 3)
	eAttr.addField("greaterThan", 4)
	eAttr.addField("inRange", 5)
	
	mcbNode.logic = eAttr.create("logic", "lo", 0)
	eAttr.addField("And", 0)
	eAttr.addField("Or", 1)
	eAttr.addField("Not", 2)
	
	mcbNode.valueIfTrueX = nAttr.create("valueIfTrueX", "tx", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.valueIfTrueY = nAttr.create("valueIfTrueY", "ty", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.valueIfTrueZ = nAttr.create("valueIfTrueZ", "tz", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.valueIfFalseX = nAttr.create("valueIfFalseX", "fx", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.valueIfFalseY = nAttr.create("valueIfFalseY", "fy", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	mcbNode.valueIfFalseZ = nAttr.create("valueIfFalseZ", "fz", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(True)
	nAttr.setStorable(True)
	nAttr.setReadable(True)
	nAttr.setKeyable(True)
	
	# create output attributes
	mcbNode.outValueX = nAttr.create("outValueX", "ox", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(False)
	nAttr.setStorable(False)
	nAttr.setReadable(True)

	mcbNode.outValueY = nAttr.create("outValueY", "oy", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(False)
	nAttr.setStorable(False)
	nAttr.setReadable(True)
	
	mcbNode.outValueZ = nAttr.create("outValueZ", "oz", OpenMaya.MFnNumericData.kFloat, 0.0)
	nAttr.setWritable(False)
	nAttr.setStorable(False)
	nAttr.setReadable(True)
	
	#create compound attributes
	mcbNode.blendRange = cAttr.create( "blendRange", "br")
	cAttr.addChild(mcbNode.blendRangeMin)
	cAttr.addChild(mcbNode.blendRangeMax)
	
	mcbNode.cRange = cAttr.create( "range", "ran" )
	cAttr.addChild( mcbNode.rangeMin )
	cAttr.addChild( mcbNode.rangeMax )
	
	mcbNode.valueIfTrue = cAttr.create( "valueIfTrue", "vit" )
	cAttr.addChild( mcbNode.valueIfTrueX )
	cAttr.addChild( mcbNode.valueIfTrueY )
	cAttr.addChild( mcbNode.valueIfTrueZ )
	
	mcbNode.valueIfFalse = cAttr.create( "valueIfFalse", "vif" )
	cAttr.addChild( mcbNode.valueIfFalseX )
	cAttr.addChild( mcbNode.valueIfFalseY )
	cAttr.addChild( mcbNode.valueIfFalseZ )
	
	mcbNode.outValue = cAttr.create( "outValue", "ov" )
	cAttr.addChild( mcbNode.outValueX )
	cAttr.addChild( mcbNode.outValueY )
	cAttr.addChild( mcbNode.outValueZ )
	
	mcbNode.conditionCompound = cAttr.create( "conditionCompound", "cc")
	cAttr.addChild(mcbNode.condition)
	cAttr.addChild(mcbNode.logic)
	cAttr.addChild(mcbNode.checkTerm)
	cAttr.addChild(mcbNode.cRange)
	cAttr.setArray(True)
	cAttr.setUsesArrayDataBuilder(True)
	
	# add attribues
	#
	mcbNode.addAttribute( mcbNode.inputTerm)
	mcbNode.addAttribute( mcbNode.conditionCompound)
	mcbNode.addAttribute( mcbNode.cRange )
	mcbNode.addAttribute( mcbNode.blendRange )
	mcbNode.addAttribute( mcbNode.valueIfTrue )
	mcbNode.addAttribute( mcbNode.valueIfFalse )
	mcbNode.addAttribute( mcbNode.outValue )
	
	# Setup which attributes affect each other
	mcbNode.attributeAffects ( mcbNode.valueIfTrue,  mcbNode.outValue )
	mcbNode.attributeAffects ( mcbNode.valueIfFalse,  mcbNode.outValue )
	mcbNode.attributeAffects ( mcbNode.inputTerm,  mcbNode.outValue )
	mcbNode.attributeAffects ( mcbNode.cRange,  mcbNode.outValue )
	mcbNode.attributeAffects ( mcbNode.blendRange,  mcbNode.outValue )
	mcbNode.attributeAffects ( mcbNode.conditionCompound,  mcbNode.outValue )
	
	
# initialize the script plug-in
def initializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
	try:
		mplugin.registerNode( kcompUtilNodeTypeName, kcompUtilNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kcompUtilNodeClassify)
	except:
		sys.stderr.write( "Failed to register node: %s" % kcompUtilNodeTypeName )
		raise
		
# uninitialize the script plug-in
def uninitializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)
	try:
		mplugin.deregisterNode( kcompUtilNodeId )
	except:
		sys.stderr.write( "Failed to deregister node: %s" % kcompUtilNodeTypeName )
		raise
