import bpy, nodeitems_utils, mathutils
import math, random, time, colorsys
from . import (Node, colorize_square, TextureSetGetter, Image, DATA_TextureSet, SOCKET_TextureSet, SOCKET_Map, MapGetter,
			   DATA_GETTER_NODE_Image, DATA_Image, DATA_Map, SOCKET_Image, SC_OT_RandomizeSeedNode)
from .. import my_globals
from ..utils import définirPixel
from typing import Tuple, List


# class MapToBlenderImageNodeDrawOperator(bpy.types.Operator):
# 	bl_idname = 'node.map_drawer_node_draw_operator'
# 	bl_label = 'Draw map in image'
# 	bl_description = 'Draw map in specified image'
# 	source_node_path: bpy.props.StringProperty()
#
# 	def execute(self, context):
# 		source_node: MapToBlenderImageNode = eval(self.source_node_path)
#
# 		# get maps for each channel
# 		if len(source_node.inputs['Map -> RGB'].links) > 0:
# 			rgb_map_node: MapGetter = source_node.inputs['Map -> RGB'].links[0].from_node
# 			rgb_map = rgb_map_node.get_map()  # type: DATA_Map
# 		else: rgb_map = None
# 		try:
# 			red_map_node = source_node.inputs['Map -> Red channel'].links[0].from_node
# 			red_map = red_map_node.get_input_map()  # type: DATA_Map
# 		except: red_map = None
# 		try:
# 			green_map_node = source_node.inputs['Map -> Green channel'].links[0].from_node
# 			green_map = green_map_node.get_input_map()  # type: DATA_Map
# 		except: green_map = None
# 		try:
# 			blue_map_node = source_node.inputs['Map -> Blue channel'].links[0].from_node
# 			blue_map = blue_map_node.get_input_map()  # type: DATA_Map
# 		except: blue_map = None
# 		try:
# 			alpha_map_node = source_node.inputs['Map -> Alpha channel'].links[0].from_node
# 			alpha_map = alpha_map_node.get_input_map()  # type: DATA_Map
# 		except: alpha_map = None
# 		startTime = time.time()  # start counting time here, don't count previous work needed
#
# 		# get or create new image
# 		try:
# 			image = bpy.data.images[source_node.image_name]
# 			image.scale(source_node.image_resolution, source_node.image_resolution)
# 		except:
# 			image = bpy.data.images.new(
# 				source_node.image_name,
# 				source_node.image_resolution,
# 				source_node.image_resolution,
# 				alpha=True,
# 				float_buffer=True)
# 			image.name = source_node.image_name  # force name
#
# 		# store values, and get min and max first
# 		# computed_values_rgb = None
# 		# computed_values_r = None
# 		# computed_values_g = None
# 		# computed_values_b = None
# 		# computed_values_a = None
# 		if rgb_map:
# 			rgb_max_value = -math.inf
# 			rgb_min_value = math.inf
# 			computed_values_rgb = [[0 for __ in range(source_node.image_resolution)] for _ in range(source_node.image_resolution)]
# 		if red_map:
# 			r_max_value = -math.inf
# 			r_min_value = math.inf
# 			computed_values_r = [[0 for __ in range(source_node.image_resolution)] for _ in range(source_node.image_resolution)]
# 		if green_map:
# 			g_max_value = -math.inf
# 			g_min_value = math.inf
# 			computed_values_g = [[0 for __ in range(source_node.image_resolution)] for _ in range(source_node.image_resolution)]
# 		if blue_map:
# 			b_max_value = -math.inf
# 			b_min_value = math.inf
# 			computed_values_b = [[0 for __ in range(source_node.image_resolution)] for _ in range(source_node.image_resolution)]
# 		if alpha_map:
# 			a_max_value = -math.inf
# 			a_min_value = math.inf
# 			computed_values_a = [[0 for __ in range(source_node.image_resolution)] for _ in range(source_node.image_resolution)]
# 		last_progress_display_time = -math.inf
# 		for i in range(source_node.image_resolution):
# 			xPercent = i / source_node.image_resolution
# 			if time.time() >= last_progress_display_time + .2:
# 				source_node.afficher_barre_progression(i / source_node.image_resolution / 2, time.time() - startTime)
# 				last_progress_display_time = time.time()
# 			for j in range(source_node.image_resolution):
# 				yPercent = j / source_node.image_resolution
# 				if rgb_map:
# 					map_value = rgb_map.get_value(xPercent, yPercent)
# 					# print(map_value, rgb_max_value)
# 					if map_value > rgb_max_value: rgb_max_value = map_value
# 					elif map_value < rgb_min_value: rgb_min_value = map_value
# 					computed_values_rgb[i][j] = map_value
# 				if red_map:
# 					map_value = red_map.get_value(xPercent, yPercent)
# 					if map_value > r_max_value: r_max_value = map_value
# 					elif map_value < r_min_value: r_min_value = map_value
# 					computed_values_r[i][j] = map_value
# 				if green_map:
# 					map_value = green_map.get_value(xPercent, yPercent)
# 					if map_value > g_max_value: g_max_value = map_value
# 					elif map_value < g_min_value: g_min_value = map_value
# 					computed_values_g[i][j] = map_value
# 				if blue_map:
# 					map_value = blue_map.get_value(xPercent, yPercent)
# 					if map_value > b_max_value: b_max_value = map_value
# 					elif map_value < b_min_value: b_min_value = map_value
# 					computed_values_b[i][j] = map_value
# 				if alpha_map:
# 					map_value = alpha_map.get_value(xPercent, yPercent)
# 					if map_value > a_max_value: a_max_value = map_value
# 					elif map_value < a_min_value: a_min_value = map_value
# 					computed_values_a[i][j] = map_value
# 		if rgb_map: rgb_écart_valeurs_max = rgb_max_value - rgb_min_value
# 		if red_map: r_écart_valeurs_max = r_max_value - r_min_value
# 		if green_map: g_écart_valeurs_max = g_max_value - g_min_value
# 		if blue_map: b_écart_valeurs_max = b_max_value - b_min_value
# 		if alpha_map: a_écart_valeurs_max = a_max_value - a_min_value
#
# 		# draw
# 		total_pixels = source_node.image_resolution ** 2 * 4
# 		pixels = [0 for _ in range(total_pixels)]
# 		for i in range(source_node.image_resolution):
# 			if time.time() >= last_progress_display_time + .2:
# 				source_node.afficher_barre_progression(0.5 + i / source_node.image_resolution / 2, time.time() - startTime)
# 				last_progress_display_time = time.time()
# 			for j in range(source_node.image_resolution):
# 				if rgb_map:
# 					if source_node.normalize and rgb_écart_valeurs_max != 0:
# 						r = g = b = (computed_values_rgb[i][j] - rgb_min_value) / rgb_écart_valeurs_max
# 					else:
# 						r = g = b = computed_values_rgb[i][j]
# 				else:
# 					r = g = b = 0
# 				a = 1
# 				if red_map:
# 					if source_node.normalize and r_écart_valeurs_max != 0:
# 						r = (computed_values_r[i][j] - r_min_value) / r_écart_valeurs_max
# 					else:
# 						r = computed_values_r[i][j]
# 				if green_map:
# 					if source_node.normalize and g_écart_valeurs_max != 0:
# 						g = (computed_values_g[i][j] - g_min_value) / g_écart_valeurs_max
# 					else:
# 						g = computed_values_g[i][j]
# 				if blue_map:
# 					if source_node.normalize and b_écart_valeurs_max != 0:
# 						b = (computed_values_b[i][j] - b_min_value) / b_écart_valeurs_max
# 					else:
# 						b = computed_values_b[i][j]
# 				if alpha_map:
# 					if source_node.normalize and a_écart_valeurs_max != 0:
# 						a = (computed_values_a[i][j] - a_min_value) / a_écart_valeurs_max
# 					else:
# 						a = computed_values_a[i][j]
# 				définirPixel(pixels, source_node.image_resolution, i, j, (r, g, b, a))
#
# 		image.pixels = pixels
# 		source_node.afficher_barre_progression(1, time.time() - startTime)
# 		image.update()
#
# 		source_node.last_operation_time = time.time() - startTime
# 		return {'FINISHED'}


class NewTextureSetNode(bpy.types.Node, Node, TextureSetGetter):
	bl_idname = 'sc_node_54kap20qek58q9dxzy0a'
	bl_label = 'New texture set'
	at_least_one_input_socket_required = False

	# last_operation_time: bpy.props.FloatProperty(
	# 	name='',
	# 	description='',
	# 	default=0)

	resolution: bpy.props.IntVectorProperty(
		name='Resolution',
		description='On X and Y',
		default=(128, 128),
		size=2,
		min=2)

	start_color_albedo: bpy.props.FloatVectorProperty(
		name='Albedo',
		description='Initial color of the albedo texture channel',
		default=(0, 0, 0, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	start_color_emit: bpy.props.FloatVectorProperty(
		name='Emit',
		description='Initial color of the emissive texture channel',
		default=(0, 0, 0, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	def sc_init(self, context):
		self.create_output(SOCKET_TextureSet)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'resolution')
		layout.prop(self, 'start_color_albedo')
		layout.prop(self, 'start_color_emit')

	def _get_texture_set_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_texture_set(self, *args, **kwargs):
		startTime = time.time()
		# last_progress_display_time = time.time()
		# total_pixels = self.resolution[0] * self.resolution[1]
		# current_pixel_nb = 0

		image_albedo = Image(resolution=(self.resolution[0], self.resolution[1]))
		image_emit = Image(resolution=(self.resolution[0], self.resolution[1]))
		texture_set = DATA_TextureSet()
		texture_set.textures['albedo'] = image_albedo
		texture_set.textures['emit'] = image_emit
		start_colors_by_name = {'albedo': self.start_color_albedo, 'emit': self.start_color_emit}

		for textureName, image in texture_set.textures.items():
			start_color = start_colors_by_name[textureName]
			for pixel_column in image.pixels:
				for pixel in pixel_column:
					pixel.r = start_color[0]
					pixel.g = start_color[1]
					pixel.b = start_color[2]
					pixel.a = start_color[3]

		# afficher progress bar
		# current_pixel_nb += 1
		# if time.time() > last_progress_display_time + .25:
		# 	last_progress_display_time = time.time()
		# 	self.afficher_barre_progression(current_pixel_nb / total_pixels, time.time() - startTime)

		self.last_operation_time = time.time() - startTime
		# self.afficher_barre_progression(1, self.last_operation_time)
		return texture_set


class RandomRectanglesDrawerNode(bpy.types.Node, Node, TextureSetGetter):
	bl_idname = 'sc_node_0b12uumr39cc6rd9tdvu'
	bl_label = 'Random rectangles drawer'

	# last_operation_time: bpy.props.FloatProperty(
	# 	name='',
	# 	description='',
	# 	default=0)

	random_seed: bpy.props.IntProperty(
		name='Seed',
		description='Random seed', )

	total_passes: bpy.props.IntProperty(
		name='Passes',
		description='How many times to draw a pass of random rectangles',
		default=2, min=0)

	rectangles_size_min_max: bpy.props.IntVectorProperty(
		name='Size min / max',
		description="In pixels, on one axis. The other axis' size will be calculated from the min and max aspect ratio",
		default=(100, 200), size=2, min=1)

	rectangles_size_variation_proba: bpy.props.FloatProperty(
		name='Size variation proba',
		description='How much size variation will occur among the rectangles of the same pass',
		default=20, min=0, max=100, subtype='PERCENTAGE')

	rectangles_size_ratio_min_max: bpy.props.FloatVectorProperty(
		name='Size ratio min / max',
		description='In pixels, on X and Y. You can use a ratio of 0 safely, the minimum size will always be 1 pixel to make the rectangles at least visible',
		default=(0, .05), size=2, min=0)

	rectangles_color_min: bpy.props.FloatVectorProperty(
		name='Color min',
		description='The random color of each rectangle pass will be chosent between this color and the max color, in HSV color space',
		default=(0, 0, 0, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	rectangles_color_max: bpy.props.FloatVectorProperty(
		name='Color max',
		description='The random color of each rectangle pass will be chosent between this color and the min color, in HSV color space',
		default=(0.1, 0.1, 0.1, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	target_texture_channel: bpy.props.StringProperty(
		name='Texture channel',
		description='Texture channel to draw into (albedo, emit,...)',
		default='albedo')

	def sc_init(self, context):
		self.width = 370
		# self.inputs.new('TextureSetSocket', 'Texture set')
		self.create_input(SOCKET_TextureSet, is_required=True)
		# self.outputs.new('TextureSetSocket', 'Texture set')
		self.create_output(SOCKET_TextureSet)
		self.random_seed = random.randint(0, 9e5)

	def sc_draw_buttons(self, context, layout):
		# if len(self.inputs['Texture set'].links) <= 0:
		# 	layout.label(text="Source texture set is needed", icon="ERROR")
		#
		# self.ui_display_doc_and_last_job_done2(layout)

		layout.prop(self, 'target_texture_channel')
		row = layout.row()
		row.prop(self, 'random_seed')
		self.create_operator(row, SC_OT_RandomizeSeedNode)

		# layout.label(text='Rectangles', icon='OOPS')
		layout.prop(self, 'total_passes')
		layout.prop(self, 'rectangles_size_min_max')
		layout.prop(self, 'rectangles_size_variation_proba')
		layout.prop(self, 'rectangles_size_ratio_min_max')

		row = layout.row()
		row.label(text='Color min / max')
		row.prop(self, 'rectangles_color_min', text='')
		row.prop(self, 'rectangles_color_max', text='')

	def _get_texture_set_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_texture_set(self, *args, **kwargs):
		texture_set_source_node = self.inputs[0].links[0].from_node  # type: TextureSetGetter
		texture_set = texture_set_source_node.get_texture_set()  # type: DATA_TextureSet

		startTime = time.time()
		random.seed(self.random_seed)

		target_image = texture_set.textures[self.target_texture_channel.lower()]
		self.add_ribbons(target_image)
		self.last_operation_time = time.time() - startTime
		return texture_set

	def add_ribbons(self, img_data):
		for a in range(self.total_passes):
			colorMinRGB = mathutils.Color((self.rectangles_color_min[0], self.rectangles_color_min[1], self.rectangles_color_min[2]))
			colorMaxRGB = mathutils.Color((self.rectangles_color_max[0], self.rectangles_color_max[1], self.rectangles_color_max[2]))
			# extract and compute random HSV
			colorHSVA = (
				random.uniform(colorMinRGB.h, colorMaxRGB.h),
				random.uniform(colorMinRGB.s, colorMaxRGB.s),
				random.uniform(colorMinRGB.v, colorMaxRGB.v),
				random.uniform(self.rectangles_color_min[3], self.rectangles_color_max[3])
			)
			# convert HSV back to RGB
			colorRGB = colorsys.hsv_to_rgb(colorHSVA[0], colorHSVA[1], colorHSVA[2])
			color = (colorRGB[0], colorRGB[1], colorRGB[2], colorHSVA[3])

			# decide where (x,y) to start
			pos_top_left = [random.randint(0, img_data.resolution[0] - 1), random.randint(0, img_data.resolution[1] - 1)]
			# decide vertical or horizontal
			barres_sont_horizontales = random.random() >= 0.5
			if barres_sont_horizontales:
				pos_top_left[1] = img_data.resolution[1] - 1
			else:
				pos_top_left[0] = 0
			# decide (x,y) size
			s = random.randint(self.rectangles_size_min_max[0], self.rectangles_size_min_max[1])
			if barres_sont_horizontales:
				size_barre_pixels = [s, max(1, int(s * random.uniform(self.rectangles_size_ratio_min_max[0], self.rectangles_size_ratio_min_max[1])))]
			else:
				size_barre_pixels = [max(1, int(s * random.uniform(self.rectangles_size_ratio_min_max[0], self.rectangles_size_ratio_min_max[1]))), s]
			prev_size_barre_pixels = size_barre_pixels
			# draw

			while 0 <= pos_top_left[0] < img_data.resolution[0] and 0 <= pos_top_left[1] < img_data.resolution[1]:
				# change size?
				should_change_size = random.random() < self.rectangles_size_variation_proba / 100
				if should_change_size:
					prev_size_barre_pixels = size_barre_pixels
					if barres_sont_horizontales:
						size_barre_pixels = [s,
											 max(1, int(s * random.uniform(self.rectangles_size_ratio_min_max[0], self.rectangles_size_ratio_min_max[1])))]
					else:
						size_barre_pixels = [max(1, int(s * random.uniform(self.rectangles_size_ratio_min_max[0], self.rectangles_size_ratio_min_max[1]))),
											 s]

				# draw
				bottom_right = [pos_top_left[0] + size_barre_pixels[0], pos_top_left[1] - size_barre_pixels[1]]
				colorize_square(img_data, pos_top_left, bottom_right, color)

				# move both corners
				if barres_sont_horizontales:
					increment = prev_size_barre_pixels[1] + size_barre_pixels[1]
					bottom_right[1] -= increment
					pos_top_left[1] -= increment
				else:
					increment = prev_size_barre_pixels[0] + size_barre_pixels[0]
					bottom_right[0] += increment
					pos_top_left[0] += increment


class SimpleWindowsDrawerNode(bpy.types.Node, Node, TextureSetGetter):
	bl_idname = 'sc_node_seu8ldunyjveppp53fzw'
	bl_label = 'Simple windows drawer'

	# last_operation_time: bpy.props.FloatProperty(
	# 	name='',
	# 	description='',
	# 	default=0)

	windows_size_x_min_max: bpy.props.IntVectorProperty(
		name='X size min / max',
		description='In pixels, on X and Y',
		default=(1, 4), size=2, min=1)

	windows_size_y_min_max: bpy.props.IntVectorProperty(
		name='Y size min / max',
		description='In pixels, on X and Y',
		default=(1, 1), size=2, min=1)

	windows_size_variation_proba: bpy.props.FloatProperty(
		name='Size variation proba',
		description='All the windows in each row always have the same size, but different rows can have different window sizes. '
					'This controls the amount of rows that have different windows sizes',
		default=20, min=0, max=100, subtype='PERCENTAGE')

	windows_skip_proba: bpy.props.FloatVectorProperty(
		name='Skip probas min/max',
		description='Min / max. Each row of windows has a probability of skipping windows. This controls the min and max probabilities',
		default=(0, 10), size=2, min=0, max=100)

	windows_color_min: bpy.props.FloatVectorProperty(
		name='Color min',
		description='Each unlit windows will have a color between this one and the max color, in HSV color space',
		default=(0, 0, 0, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	windows_color_max: bpy.props.FloatVectorProperty(
		name='Color max',
		description='Each unlit windows will have a color between this one and the min color, in HSV color space',
		default=(0.1, 0.1, 0.1, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	windows_lit_color_min: bpy.props.FloatVectorProperty(
		name='Emit color min',
		description='Each lit windows will have a color between this one and the max color, in HSV color space',
		default=(1, 1, .4, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	windows_lit_color_max: bpy.props.FloatVectorProperty(
		name='Emit color max',
		description='Each lit windows will have a color between this one and the min color, in HSV color space',
		default=(1, 1, .6, 1),
		size=4,
		min=0, max=1, subtype='COLOR')

	windows_lit_skip_proba: bpy.props.FloatVectorProperty(
		name='Skip probas min/max',
		description='Min / max. Each row of windows has a probability of skipping lit windows. This controls the min and max probabilities',
		default=(70, 100), size=2, min=0, max=100)

	walls_size_x_min_max: bpy.props.IntVectorProperty(
		name='X size min / max',
		description='In pixels',
		default=(1, 1), size=2, min=1)

	walls_size_y_min_max: bpy.props.IntVectorProperty(
		name='Y size min / max',
		description='In pixels',
		default=(1, 1), size=2, min=1)

	walls_size_variation_proba: bpy.props.FloatProperty(
		name='Size variation proba',
		description='All the walls (space between each window, and space with previous row of windows) in each row of windows always have the same size, '
					'but different rows can have different walls sizes. This controls the amount of rows that have different walls sizes',
		default=20, min=0, max=100, subtype='PERCENTAGE')

	random_seed: bpy.props.IntProperty(
		name='Seed',
		description='Random seed', )

	target_texture_channel_unlit: bpy.props.StringProperty(
		name='Texture channel',
		description='Texture channel onto which to draw the unlit windows (albedo, emit,...)',
		default='albedo')

	target_texture_channel_lit: bpy.props.StringProperty(
		name='Texture channel',
		description='Texture channel onto which to draw the lit windows (albedo, emit,...)',
		default='emit')

	def sc_init(self, context):
		self.width = 380
		self.create_input(SOCKET_TextureSet, is_required=True)
		self.create_output(SOCKET_TextureSet)
		self.random_seed = random.randint(0, 9e5)

	def sc_draw_buttons(self, context, layout):
		# if len(self.inputs['Texture set'].links) <= 0:
		# 	layout.label(text="Source texture set is needed", icon="ERROR")
		#
		# self.ui_display_doc_and_last_job_done2(layout)

		row = layout.row()
		row.prop(self, 'random_seed')
		self.create_operator(row, SC_OT_RandomizeSeedNode)
		layout.label(text='Windows')
		layout.prop(self, 'target_texture_channel_unlit')
		layout.prop(self, 'windows_size_x_min_max')
		layout.prop(self, 'windows_size_y_min_max')
		layout.prop(self, 'windows_size_variation_proba')
		layout.prop(self, 'windows_skip_proba')
		split = layout.split(factor=.5)
		split.label(text='Colors min / max')
		split.prop(self, 'windows_color_min', text='')
		split.prop(self, 'windows_color_max', text='')

		layout.label(text='Lit windows')
		layout.prop(self, 'target_texture_channel_lit')
		split = layout.split(factor=.5)
		split.label(text='Colors min / max')
		split.prop(self, 'windows_lit_color_min', text='')
		split.prop(self, 'windows_lit_color_max', text='')
		layout.prop(self, 'windows_lit_skip_proba')

		layout.label(text='Walls')
		layout.prop(self, 'walls_size_x_min_max')
		layout.prop(self, 'walls_size_y_min_max')
		layout.prop(self, 'walls_size_variation_proba')

	def _get_texture_set_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_texture_set(self, *args, **kwargs):
		texture_set_source_node = self.inputs[0].links[0].from_node  # type: TextureSetGetter
		texture_set = texture_set_source_node.get_texture_set()  # type: DATA_TextureSet

		startTime = time.time()
		random.seed(self.random_seed)
		starting = True

		# from bottom to top
		currentPos = [1, 1]
		image_unlit = texture_set.textures[self.target_texture_channel_unlit]
		image_lit = texture_set.textures[self.target_texture_channel_lit]
		while currentPos[1] < image_unlit.resolution[1]:
			# draw single floor

			# set random parameters
			if starting or random.random() <= (self.windows_size_variation_proba / 100):
				currentWinSize = (random.randint(self.windows_size_x_min_max[0], self.windows_size_x_min_max[1]),
								  random.randint(self.windows_size_y_min_max[0], self.windows_size_y_min_max[1]))
			if starting or random.random() <= (self.walls_size_variation_proba / 100):
				currentWallSize = (random.randint(self.walls_size_x_min_max[0], self.walls_size_x_min_max[1]),
								   random.randint(self.walls_size_y_min_max[0], self.walls_size_y_min_max[1]))
			currentSkipWinProba = random.uniform(self.windows_skip_proba[0] / 100, self.windows_skip_proba[1] / 100)
			winLightSkipProba = random.uniform(self.windows_lit_skip_proba[0] / 100, self.windows_lit_skip_proba[1] / 100)
			starting = False

			# left to right: draw each window
			while currentPos[0] < image_unlit.resolution[0]:
				# if don't skip current window
				if random.random() > currentSkipWinProba:
					winUnlitColorHSVA = [0, 0, 0, 1]
					winUnlitColorMinRGB = mathutils.Color((self.windows_color_min[0], self.windows_color_min[1], self.windows_color_min[2]))
					winUnlitColorMaxRGB = mathutils.Color((self.windows_color_max[0], self.windows_color_max[1], self.windows_color_max[2]))
					# extract and compute random HSV
					winUnlitColorHSVA[0] = random.uniform(winUnlitColorMinRGB.h, winUnlitColorMaxRGB.h)
					winUnlitColorHSVA[1] = random.uniform(winUnlitColorMinRGB.s, winUnlitColorMaxRGB.s)
					winUnlitColorHSVA[2] = random.uniform(winUnlitColorMinRGB.v, winUnlitColorMaxRGB.v)
					winUnlitColorHSVA[3] = random.uniform(self.windows_color_min[3], self.windows_color_max[3])
					# convert HSV back to RGB
					colorRGB = colorsys.hsv_to_rgb(winUnlitColorHSVA[0], winUnlitColorHSVA[1], winUnlitColorHSVA[2])
					winUnlitColorRGBA = (colorRGB[0], colorRGB[1], colorRGB[2], winUnlitColorHSVA[3])

					# decide if the window is lit or not
					winLitColorHSVA = [0, 0, 0, 1]
					if random.random() > winLightSkipProba:
						winLitColorMinRGB = mathutils.Color((self.windows_lit_color_min[0], self.windows_lit_color_min[1], self.windows_lit_color_min[2]))
						winLitColorMaxRGB = mathutils.Color((self.windows_lit_color_max[0], self.windows_lit_color_max[1], self.windows_lit_color_max[2]))
						# extract and compute random HSV
						winLitColorHSVA[0] = random.uniform(winLitColorMinRGB.h, winLitColorMaxRGB.h)
						winLitColorHSVA[1] = random.uniform(winLitColorMinRGB.s, winLitColorMaxRGB.s)
						winLitColorHSVA[2] = random.uniform(winLitColorMinRGB.v, winLitColorMaxRGB.v)
						winLitColorHSVA[3] = random.uniform(self.windows_color_min[3], self.windows_color_max[3])
					# convert HSV back to RGB
					colorRGB = colorsys.hsv_to_rgb(winLitColorHSVA[0], winLitColorHSVA[1], winLitColorHSVA[2])
					winLitColorRGBA = (colorRGB[0], colorRGB[1], colorRGB[2], winLitColorHSVA[3])

					# draw unlit
					if not colorize_square(image_unlit,
							(currentPos[0], currentPos[1] + currentWinSize[1]),
							(currentPos[0] + currentWinSize[0], currentPos[1]),
							winUnlitColorRGBA):
						break
					# draw lit
					colorize_square(image_lit,
						(currentPos[0], currentPos[1] + currentWinSize[1]),
						(currentPos[0] + currentWinSize[0], currentPos[1]),
						winLitColorRGBA)
				# move to the right
				currentPos[0] += currentWinSize[0] + currentWallSize[0]

			# reached picture right hand side, move upwards and back to left
			currentPos[0] = 1
			currentPos[1] += currentWinSize[1] + currentWallSize[1]
		self.last_operation_time = time.time() - startTime
		return texture_set


class BlenderImagesToTextureSetNode(bpy.types.Node, Node, TextureSetGetter):
	bl_idname = 'sc_node_6al9ea8zfk04dvfyc6rd'
	bl_label = 'Blender images to texture set'
	at_least_one_input_socket_required = False

	# last_operation_time: bpy.props.FloatProperty(
	# 	name='',
	# 	description='',
	# 	default=0)

	blender_image_name_albedo: bpy.props.StringProperty(
		name='Albedo image',
		description='Name of the Blender image for the albedo channel',
		default='')

	blender_image_name_emit: bpy.props.StringProperty(
		name='Emit image',
		description='Name of the Blender image for the emit channel',
		default='')

	def sc_init(self, context):
		# self.width = 300
		# self.outputs.new('TextureSetSocket', 'Texture set')
		self.create_output(SOCKET_TextureSet)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc_and_last_job_done2(layout)

		split = layout.split(factor=.95)
		split.prop(self, 'blender_image_name_albedo', icon='IMAGE_DATA')
		if self.blender_image_name_albedo in bpy.data.images:
			split.label(text='', icon='FILE_TICK')
		else:
			split.label(text='', icon='CANCEL')

		split = layout.split(factor=.95)
		split.prop(self, 'blender_image_name_emit', icon='IMAGE_DATA')
		if self.blender_image_name_emit in bpy.data.images:
			split.label(text='', icon='FILE_TICK')
		else:
			split.label(text='', icon='CANCEL')

	def get_data(self, *args, **kwargs):
		return self.get_texture_set()

	def _get_texture_set_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_texture_set(self, *args, **kwargs):
		startTime = time.time()

		blender_image_albedo = bpy.data.images[self.blender_image_name_albedo]
		blender_image_emit = bpy.data.images[self.blender_image_name_emit]
		blender_image_albedo_pixels = blender_image_albedo.pixels[:]
		blender_image_emit_pixels = blender_image_emit.pixels[:]

		texture_set = DATA_TextureSet()
		texture_set.textures['albedo'] = Image(resolution=(blender_image_albedo.size[0], blender_image_albedo.size[1]))
		texture_set.textures['emit'] = Image(resolution=(blender_image_albedo.size[0], blender_image_albedo.size[1]))

		for image, pixels in zip([texture_set.textures['albedo'], texture_set.textures['emit']], [blender_image_albedo_pixels, blender_image_emit_pixels]):
			for i in range(image.resolution[0]):
				for j in range(image.resolution[1]):
					indicePixel = j * image.resolution[0] * 4 + i * 4
					pixel = image.pixels[i][j]
					pixel.r = pixels[indicePixel + 0]
					pixel.g = pixels[indicePixel + 1]
					pixel.b = pixels[indicePixel + 2]
					pixel.a = pixels[indicePixel + 3]
		self.last_operation_time = time.time() - startTime
		return texture_set
