# Copyright (c) 2009 The Foundry Visionmongers Ltd.  All Rights Reserved.
# Edit by Max van Leeuwen - maxvanleeuwen.com - 1.2

# PREFERENCES FOR DEFAULT SETTINGS:


# sticky note default label size
defaultStickyLabelSize = '50'

# sticky note default color
defaultStickyColor = 3435954431

# change colors of the backdrops that are within the newly created one
defaultUpdateBackdrops = True

# color for backdrop (stacked backdrops will get darker)
startingColor = 1179010815

# backdrop colors to never change (photoshop breakout layers)
ignoreColors = [2829621248, 1751668736]

# backdrop boundaries scaling
bdSize = 3


############################################






import nuke, random
import colorsys




# make every stacked backdrop slightly darker
def darker(levels):

	newColor = startingColor

	for i in range( int( abs(levels) ) ):
		RGB = [(0xFF & newColor >>  i) / 255.0 for i in [24,16,8]]
		HSV = colorsys.rgb_to_hsv(RGB[0],RGB[1],RGB[2])

		newHSV = [HSV[0], HSV[1], HSV[2] * .85]
		newRGB = colorsys.hsv_to_rgb(newHSV[0],newHSV[1],newHSV[2])

		newColor = int('%02x%02x%02x%02x' % (round(newRGB[0]*255),round(newRGB[1]*255),round(newRGB[2]*255),255),16)

	return newColor




def nodeIsInside(node, backdropNode):

	"""Returns true if node geometry is inside backdropNode otherwise returns false"""

	topLeftNode = [node.xpos(), node.ypos()]
	topLeftBackDrop = [backdropNode.xpos(), backdropNode.ypos()]
	bottomRightNode = [node.xpos() + node.screenWidth(), node.ypos() + node.screenHeight()]
	bottomRightBackdrop = [backdropNode.xpos() + backdropNode.screenWidth(), backdropNode.ypos() + backdropNode.screenHeight()]

	topLeft = ( topLeftNode[0] >= topLeftBackDrop[0] ) and ( topLeftNode[1] >= topLeftBackDrop[1] )
	bottomRight = ( bottomRightNode[0] <= bottomRightBackdrop[0] ) and ( bottomRightNode[1] <= bottomRightBackdrop[1] )

	return topLeft and bottomRight




# function can be called with custom default variables
def GrayAutoBackdrop():


	global defaultStickyLabelSize
	global defaultStickyColor
	global defaultUpdateBackdrops



	bdUpdateBooleantext = 'update included backdrops?'


	'''
	Automatically puts a backdrop behind the selected nodes.

	The backdrop will be just big enough to fit all the select nodes in, with room
	at the top for some text in a large font.
	'''


	# check if nodes are selected
	selNodes = nuke.selectedNodes()
	if selNodes:

		# panel
		p = nuke.Panel('backdrop')
		p.addSingleLineInput('label', '')
		p.addBooleanCheckBox(bdUpdateBooleantext, defaultUpdateBackdrops)

		panelObj = p.show()


		if panelObj:

			# calculate bounds for the backdrop node.
			bdX = min([node.xpos() for node in selNodes])
			bdY = min([node.ypos() for node in selNodes])
			bdW = max([node.xpos() + node.screenWidth() for node in selNodes]) - bdX
			bdH = max([node.ypos() + node.screenHeight() for node in selNodes]) - bdY


			# bigger boundaries if node area is bigger
			boundsMult = 10 + (bdW + bdH) * (bdSize/100)
			zOrder = 0

			defaultUpdateBackdrops = p.value(bdUpdateBooleantext)
			noBackdropNodes = False
			
			if defaultUpdateBackdrops:

				selectedBackdropNodes = nuke.selectedNodes("BackdropNode")

				# if there are backdropNodes selected, put the new one at the lowest level and all others at their own level + 1
				if len(selectedBackdropNodes):

					# placeholders
					lowest = 99999
					offs = 0

					for sBD in selectedBackdropNodes:

						currVal = sBD['z_order'].value()
						lowest = min(lowest, currVal)

					# all backdrops must be => 0
					if lowest < 0:
						offs = abs(lowest)
						lowest = 0

					for sBD in selectedBackdropNodes:

						currVal = sBD['z_order'].value()

						newZ = currVal + 1 + offs
						sBD['z_order'].setValue(newZ)

						if not sBD['tile_color'].value() in ignoreColors:
							sBD['tile_color'].setValue(darker(newZ))

					zOrder = lowest


				else:

					noBackdropNodes = True



			if (not defaultUpdateBackdrops) or noBackdropNodes:

				# else (no backdrop in selection) find the nearest backdrop if exists and set the new one in front of it
				nonSelectedBackdropNodes = nuke.allNodes("BackdropNode")

				for nonBackdrop in selNodes:

					for backdrop in nonSelectedBackdropNodes:

						if nodeIsInside( nonBackdrop, backdrop ):

							zOrder = max(zOrder, backdrop.knob("z_order").value() + 1 )



			# Expand the bounds to leave a border, relative to zoom size. Elements are offsets for left, top, right and bottom edges respectively. Extra top height if larger label.
			left, top, right, bottom = (-10 * boundsMult, -80 - 10 * boundsMult, 10 * boundsMult, 10 * boundsMult)
			bdX += left
			bdY += top
			bdW += (right - left)
			bdH += (bottom - top)


			labelstr = p.value('label')

			n = nuke.nodes.BackdropNode(xpos = bdX,
										bdwidth = bdW,
										ypos = bdY,
										bdheight = bdH,
										note_font_size = 100,
										tile_color = darker(zOrder if defaultUpdateBackdrops else 0),
										z_order = zOrder if defaultUpdateBackdrops else 0,
										label = labelstr)

			# revert to previous selection + this backdrop
			n['selected'].setValue(True)
			for node in selNodes:
				node['selected'].setValue(True)

			return n

		else:
			pass



	# if no nodes are selected, make sticky note
	else:

		# panel
		p = nuke.Panel('stickynote')
		p.addSingleLineInput('label', '')
		p.addSingleLineInput('label size', defaultStickyLabelSize)
		p.addButton('cancel')
		p.addButton('done')
		p.addButton('color')

		# show panel
		panelObj = p.show()


		# if user did not cancel panel or leave label empty
		if panelObj:

			# get color form different panel, because the RGBChip in nuke's panel is broken
			if(panelObj == 2):
				colObj = nuke.getColor(defaultStickyColor)
				if colObj:
					defaultStickyColor = colObj

			# try to get and set label size
			try:
				l = int(p.value('label size'))
				defaultStickyLabelSize = l
			except:
				pass


			# make stickynote, show panel if the label was empty (the user might want to type multiline text)
			n = nuke.createNode("StickyNote", inpanel=(p.value('label') == ''))
			n['note_font_size'].setValue(defaultStickyLabelSize)
			n['label'].setValue(p.value('label'))
			n['tile_color'].setValue(defaultStickyColor)

			n.setXpos(n.xpos() - round(n.screenWidth()/2))
			n.setYpos(n.ypos() + round(n.screenHeight()/2))




# autostart (if not imported)
if __name__ == "__main__":
	GrayAutoBackdrop()