import bpy, mathutils, bmesh
import random, math, time, typing
from typing import List
from . import (Node, SceneCity_Object, delete_datablocks_with_name_prefix, SceneCityScene, SceneCitySceneGetter,
			   SOCKET_Light_Scene_Optional, DATA_Mesh, DATA_GETTER_NODE_SC_Meshes, SC_Operator, DATA_GETTER_NODE_SC_Objects, SC_Objects_Socket, SOCKET_bldata,
			   DATA_GETTER_NODE_SC_bldata,
			   DATA_bldata, DATA_Object, SOCKET_Light_Scene,
			   SOCKET_Meshes, SC_Curves_Socket)
from .. import my_globals, utils


class Get_Object(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Objects):
	bl_idname = 'sc_node_8exb13f4t44x2p1o0cu9'
	bl_label = 'Get object'
	object: bpy.props.PointerProperty(name='Object', type=bpy.types.Object, update=Node.prop_updated)
	at_least_one_input_socket_required = False

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

	def are_all_inputs_correct(self):
		return self.object != None

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'object')

	def _get_sc_objects_necessary_data(self, *args, **kwargs):
		return

	@Node.get_data_first
	def get_sc_objects(self, *args, **kwargs):
		if not self.object:
			self.print('No object specified')
			raise ValueError
		else:
			sc_object = DATA_Object(self.object)
			self.print(f'Object "{self.object.name}" found')
			return [sc_object]


class MeshObjectOpSelectionInvert(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Objects):
	bl_idname = 'sc_node_mbj38dsx39vnfxn3l1el'
	bl_label = 'Mesh object op: invert selection'

	def sc_init(self, context):
		self.create_input(SC_Objects_Socket, is_required=True)
		self.create_output(SC_Objects_Socket, is_new_data_output=False)

	def _get_sc_objects_necessary_data(self, *args, **kwargs):
		input: DATA_GETTER_NODE_SC_Objects = self.inputs[0].links[0].from_node
		return input.get_sc_objects()

	@Node.get_data_first
	def get_sc_objects(self, object: DATA_Object, *args, **kwargs):
		utils.definir_blender_mode('OBJECT')
		bpy.ops.object.select_all(action='DESELECT')
		object.bl_object.select_set(True)
		utils.definir_blender_mode('EDIT')
		bpy.ops.mesh.select_all(action='INVERT')
		return object


class CreateObjectNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Objects):
	bl_idname = 'sc_node_zsht359pqmwl9cusqcdi'
	# bl_icon = 'OBJECT_MODE'
	bl_label = 'Create object(s) and assign data'
	obj_name: bpy.props.StringProperty(name='Name', default='New object',
		description='If mutliple datablocks are input, several objects will be created (one per datablock), with the set name as prefix')

	# should_link_to_selected_collection: bpy.props.BoolProperty(name='Link to selected collection', default=False,
	# 	description="(Only effective for new objects) If not, will link to the current scene's root collection")

	def sc_init(self, context):
		self.create_input(SOCKET_bldata, is_required=True)
		# self.create_input(SOCKET_Meshes, is_required=True)
		# self.create_input(SC_Curves_Socket, is_required=True)
		self.create_output(SC_Objects_Socket)

	def sc_draw_buttons(self, context, layout):
		layout.label(text='If the specified object exists, you must assign the same type of data', icon='INFO')
		layout.prop(self, 'obj_name')

	# self.create_operator(layout, self.SC_OT_CreateObject, 'get_sc_objects')

	@Node.get_data_first
	def get_sc_objects(self, sc_bldata_list: List[DATA_bldata], *args, **kwargs):
		result_sc_objects = []
		for data_i, sc_bl_data in enumerate(sc_bldata_list):
			# object name
			if len(sc_bldata_list) > 1: object_name = self.obj_name + f'{data_i}'
			else: object_name = self.obj_name

			# create / update object
			utils.definir_blender_mode('OBJECT')
			final_bl_data = sc_bl_data.bl_data
			if isinstance(final_bl_data, bpy.types.Object):
				final_bl_data = final_bl_data.data
			try:
				bl_object = bpy.data.objects[object_name]
				bl_object.data = final_bl_data  # If trying to assign a new type of data to an existing object -> forbidden by Blender
			except KeyError:
				bl_object = bpy.data.objects.new(object_name, final_bl_data)
				bpy.context.scene.collection.objects.link(bl_object)
			sc_object = DATA_Object(bl_object)
			result_sc_objects.append(sc_object)
		return result_sc_objects


# class SC_OT_CreateObject(bpy.types.Operator, SC_Operator):
# 	bl_idname = 'sc_op.8xs3obpmbq1hb9yhpnbr'
# 	bl_description = "Create the object if it doesn't exist, or update it otherwise. Will add it to the current scene's root collection if newly created"
# 	bl_label = 'Create / update object'


class ObjectsGetterNode(bpy.types.Node, Node, SceneCitySceneGetter):
	bl_idname = 'sc_node_jwctdyts73476clpvu8k'
	bl_label = 'Objects getter'
	at_least_one_input_socket_required = False

	blender_object_name: bpy.props.StringProperty(
		name='Object name',
		description='Name of the Blender object to convert to SceneCity objects. Its transform is not important. All its children will be considered too. '
					'Its data type can be anything datablock (mesh, light...). It can also be a group instance',
		default='', update=Node.prop_updated)

	def are_all_inputs_correct(self):
		return self.blender_object_name != None

	def sc_init(self, context):
		self.width = 250
		# self.outputs.new('SceneCityObjectsSocket', 'Objects')
		self.create_output(SOCKET_Light_Scene)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc2(layout)
		split = layout.split(factor=.95)
		split2 = split.split(factor=.4)
		split2.label(text='Object name')
		split2.prop(self, 'blender_object_name', text='', icon='OBJECT_DATA')
		if self.blender_object_name in bpy.data.objects:
			# split.label(text='', icon='FILE_TICK')
			split.label(text='', icon_value=my_globals.icônes['tick'].icon_id)
		# self.use_custom_color = False
		# pass
		else:
			split.label(text='', icon='CANCEL')

	# split.label(text='', icon_value=my_globals.icônes['error'].icon_id)
	# self.use_custom_color = True
	# self.color = (1,0,0)

	def convert_bl_ob_2_sc_ob(self, bl_object):
		sc_object = SceneCity_Object()
		sc_object.name = bl_object.name
		sc_object.data = bl_object.data
		sc_object.source_bl_object = bl_object
		if not sc_object.data and bl_object.instance_type == 'COLLECTION':
			sc_object.data = bl_object.instance_collection
		# sc_object.location = (bl_object.location[0], bl_object.location[1], bl_object.location[2])
		# sc_object.scale = (bl_object.scale[0], bl_object.scale[1], bl_object.scale[2])
		# sc_object.rotation_euler_radians = (bl_object.rotation_euler[0], bl_object.rotation_euler[1], bl_object.rotation_euler[2])
		sc_object.matrix_local = bl_object.matrix_local
		sc_object.matrix_world = bl_object.matrix_world
		for bl_child in bl_object.children:
			sc_child = self.convert_bl_ob_2_sc_ob(bl_child)
			sc_child.parent = sc_object
			sc_object.children.add(sc_child)
		return sc_object

	# def get_scenecity_objects(self, **kwargs):
	# 	blender_object = bpy.data.objects[self.blender_object_name]
	# 	return self.convert_bl_ob_2_sc_ob(blender_object)

	def get_scenecity_scene(self, **kwargs):
		blender_object = bpy.data.objects[self.blender_object_name]
		sc_scene = SceneCityScene()
		sc_scene.add_sc_object(self.convert_bl_ob_2_sc_ob(blender_object))
		return sc_scene


class ObjectsInstancerNode(bpy.types.Node, Node):
	bl_idname = 'sc_node_mweep2jn1g9royfmc86z'
	bl_label = 'Objects instancer'

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

	scale: bpy.props.FloatProperty(
		name='Scale',
		description='Initial scale of the parent object of all instances',
		default=.1)

	blender_objects_name_prefix: bpy.props.StringProperty(
		name='Objects name prefix',
		description='Name prefix of the Blender objects that will be created',
		default='Put a prefix')

	instancing_mode: bpy.props.EnumProperty(
		name='Instancing mode',
		description="Choose depending on your needs and number of objects. "
					"Individual objects are much slower to create, and will make Blender slower even with moderate numbers of objects in the scene, "
					"but will allow each instance to be manipulated individually. "
					"Dupli verts is very fast to create, and Blender will have a much easier time handling very large numbers of instances, "
					"however you cannot manipulate any individual instance",
		items=[('individual_objects', 'Individual objects', '', 1),
			   ('dupli_verts', 'Dupli verts', '', 2), ])

	should_hide_in_viewport: bpy.props.BoolProperty(
		name='Hide objects in viewport',
		description="For very large scenes, the viewport won't be able to draw all objects, so you might want to hide them. Will not affect their visibility "
					"in the final renders",
		default=False)

	def sc_init(self, context):
		self.width = 300
		# self.inputs.new('SceneCityObjectsSocket', 'Objects')
		self.create_input(SOCKET_Light_Scene, is_required=True)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc_and_last_job_done2(layout)
		if not self.blender_objects_name_prefix:
			layout.label(text="Name prefix needed", icon="ERROR")
		# if len(self.inputs['Objects'].links) <= 0:
		# 	layout.label(text="Input objects needed", icon="ERROR")
		layout.prop(self, 'blender_objects_name_prefix', icon='OBJECT_DATA')
		layout.prop(self, 'scale')
		layout.prop(self, 'instancing_mode')
		# layout.prop(self, 'should_hide_in_viewport')
		op = layout.operator(SC_OT_ObjectsInstancerNodeCreate.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()
		op = layout.operator(SC_OT_ObjectsInstancerNodeDelete.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()


class SC_OT_ObjectsInstancerNodeCreate(bpy.types.Operator):
	bl_idname = 'node.objects_instancer_node_create'
	bl_description = 'The created objects will live in the active collection in the outliner, so select one first'
	bl_label = 'Create objects'
	source_node_path: bpy.props.StringProperty()

	@classmethod
	def create_and_add_to_scene_bl_ob_from_sc_ob(cls, sc_ob, name_prefix):
		name_bl_ob = name_prefix + ' - ' + sc_ob.data.name
		# data est un groupe
		if type(sc_ob.data) == bpy.types.Collection:
			new_bl_object = bpy.data.objects.new(name=name_bl_ob, object_data=None)
			new_bl_object.instance_type = 'COLLECTION'
			new_bl_object.instance_collection = sc_ob.data
		else:
			new_bl_object = bpy.data.objects.new(name=name_bl_ob, object_data=sc_ob.data)
		new_bl_object.matrix_local = sc_ob.matrix_local
		sc_ob.copy_modifiers_to_other_bl_object(new_bl_object)
		bpy.context.collection.objects.link(new_bl_object)
		for sc_child in sc_ob.children:
			bl_child = cls.create_and_add_to_scene_bl_ob_from_sc_ob(sc_child, name_prefix)
			bl_child.parent = new_bl_object
		return new_bl_object

	def execute(self, context):
		my_globals.todel_derniere_operation_par_operator_non_standard = True

		source_node: ObjectsInstancerNode = eval(self.source_node_path)
		assert source_node.blender_objects_name_prefix
		# objects_source = source_node.inputs['Objects'].links[0].from_node  # type: SceneCityObjectsGetter
		# objects = objects_source.get_scenecity_objects()
		objects_source = source_node.inputs[0].links[0].from_node  # type: SceneCitySceneGetter
		sc_scene = objects_source.get_scenecity_scene()  # type: SceneCityScene

		startTime = time.time()
		parent = bpy.data.objects.new(name=source_node.blender_objects_name_prefix, object_data=None)
		parent.scale = (source_node.scale, source_node.scale, source_node.scale)
		parent.empty_display_size = 5 / source_node.scale
		bpy.context.collection.objects.link(parent)

		if source_node.instancing_mode == 'dupli_verts':
			for datablock, sc_objects in sc_scene.get_objects_by_data().items():
				# objet placeur
				mesh_placeur = bpy.data.meshes.new(name=source_node.blender_objects_name_prefix + ' - dupli positions for: ' + datablock.name)
				obj_placeur = bpy.data.objects.new(name=mesh_placeur.name, object_data=mesh_placeur)
				bpy.context.collection.objects.link(obj_placeur)
				obj_placeur.location = (0, 0, 0)
				# obj_placeur.parent = parent
				obj_placeur.instance_type = 'VERTS'
				obj_placeur.use_instance_vertices_rotation = True
				obj_placeur.parent = parent
				# obj_placeur.hide_set(source_node.should_hide_in_viewport)

				# objet à placer
				nom_nouvelle_instance = source_node.blender_objects_name_prefix + ' - duplicated datablock: ' + datablock.name
				if type(datablock) == bpy.types.Collection:
					obj_à_placer = bpy.data.objects.new(name=nom_nouvelle_instance, object_data=None)
					obj_à_placer.instance_type = 'COLLECTION'
					obj_à_placer.instance_collection = datablock
				else:
					obj_à_placer = bpy.data.objects.new(name=nom_nouvelle_instance, object_data=datablock)
				bpy.context.collection.objects.link(obj_à_placer)
				obj_à_placer.parent = obj_placeur
				# obj_à_placer.hide_set(source_node.should_hide_in_viewport)

				# placer les verts
				verts = []
				no_instance_actuelle = 0
				for sc_object in sc_objects:
					verts.append(sc_object.get_matrix_world().translation)
					no_instance_actuelle += 1

				# if time.time() - temps_dernier_affichage >= 1:
				# 	temps_dernier_affichage = time.time()
				# 	percentage_done = no_instance_actuelle / total_instances_à_créer * 100
				# 	print('SceneCity | ' + str(no_instance_actuelle) + ' instances created and placed so far...(' + str(round(percentage_done, 2)) + '%)')
				mesh_placeur.from_pydata(vertices=verts, edges=[], faces=[])
				mesh_placeur.update()

				# orienter les verts
				for instance_nb, sc_object in enumerate(sc_objects):
					# normal_x = -math.sin(sc_object.rotation_euler_radians[2])
					# normal_y = math.cos(sc_object.rotation_euler_radians[2])
					# mesh_placeur.vertices[instance_nb].normal = normal_x, normal_y, 0
					world_matrix = sc_object.get_matrix_world()
					# rot_matrix = world_matrix.to_quaternion().to_matrix()
					rot_matrix = world_matrix.to_euler().to_matrix()
					# print(world_matrix)
					# print(rot_matrix)
					forward = (rot_matrix @ mathutils.Vector((0, 1, 0))).normalized()
					# print(forward)
					mesh_placeur.vertices[instance_nb].normal = forward

		elif source_node.instancing_mode == 'individual_objects':
			for sc_object in sc_scene._objects_hierarchy:
				bl_ob = self.create_and_add_to_scene_bl_ob_from_sc_ob(sc_object, source_node.blender_objects_name_prefix)
				bl_ob.parent = parent

		source_node.last_operation_time = time.time() - startTime
		return {'FINISHED'}


# def create_object(self, sc_object_parent, bl_object_parent):
# 	nom_nouvelle_instance = source_node.blender_objects_name_prefix + ' - instance ' + str(instance_nb) + ': ' + datablock.name
#
# 	# instance est un groupe
# 	if type(datablock) == bpy.types.Group:
# 		nouvel_objet_instancié = bpy.data.objects.new(name=nom_nouvelle_instance, object_data=None)
# 		nouvel_objet_instancié.dupli_type = 'GROUP'
# 		nouvel_objet_instancié.dupli_group = datablock
# 	else:
# 		nouvel_objet_instancié = bpy.data.objects.new(name=nom_nouvelle_instance, object_data=datablock)
# 	bpy.context.scene.objects.link(nouvel_objet_instancié)
#
# 	# instance transformations
# 	nouvel_objet_instancié.parent = parent
# 	nouvel_objet_instancié.location = instance.location
# 	nouvel_objet_instancié.rotation_euler_radians = instance.rotation_euler_radians


class SC_OT_ObjectsInstancerNodeDelete(bpy.types.Operator):
	bl_idname = 'node.objects_instancer_node_delete'
	bl_description = 'It will delete the object with the specified name, and all its children'
	bl_label = 'Delete objects'
	source_node_path: bpy.props.StringProperty()

	def execute(self, context):
		my_globals.todel_derniere_operation_par_operator_non_standard = True

		source_node = eval(self.source_node_path)  # type: ObjectsInstancerNode
		delete_datablocks_with_name_prefix('objects', source_node.blender_objects_name_prefix)
		delete_datablocks_with_name_prefix('meshes', source_node.blender_objects_name_prefix)
		return {'FINISHED'}


class ObjectsAddNode(bpy.types.Node, Node, SceneCitySceneGetter):
	bl_idname = 'sc_node_ivnjovu5ig46drimgrkt'
	bl_label = 'Objects add'

	def sc_init(self, context):
		self.width = 250
		# self.outputs.new('SceneCityObjectsSocket', 'Objects')
		self.create_output(SOCKET_Light_Scene)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc2(layout)
		op = layout.operator(SC_OT_BuildingsCollectionNodeAddInput.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()

	def get_scenecity_scene(self, **kwargs):
		objects = []
		scene = SceneCityScene()
		for input in self.inputs:
			try:
				source_node: SceneCitySceneGetter = input.links[0].from_node
			except:
				continue
			# objects.extend(source_node.get_scenecity_objects())
			scene.add(source_node.get_scenecity_scene())
		return scene


class SC_OT_BuildingsCollectionNodeAddInput(bpy.types.Operator):
	bl_idname = 'node.object_add_node_add_input'
	bl_description = ''
	bl_label = 'Add objects'
	source_node_path: bpy.props.StringProperty()

	def execute(self, context):
		source_node: ObjectsAddNode = eval(self.source_node_path)
		random_string = time.time()
		# source_node.inputs.new(OptionalObjectSocket.__name__, str(random_string))
		source_node.create_input(SOCKET_Light_Scene_Optional, is_required=True, label=str(random_string))
		return {'FINISHED'}


# class SCMeshToBLMeshAndBLObjectNode(bpy.types.Node, Node):
class SCMeshToBLMeshAndBLObjectNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Objects):
	bl_idname = 'sc_node_2lrckpp4sv0sjtvc0eu1'
	bl_label = 'Mesh data to Blender mesh and object'

	object_name: bpy.props.StringProperty(
		name='Object name',
		description="That object will be created if it doesn't exist",
		default='City')

	mesh_name: bpy.props.StringProperty(
		name='Mesh name',
		description="That mesh will be created if it doesn't exist",
		default='City')

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)

	# self.create_input(SOCKET_Objects, is_required=True)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'object_name', icon='OBJECT_DATA')
		layout.prop(self, 'mesh_name', icon='MESH_DATA')
		# self.create_operator(layout, SC_OT_SCMeshToBlenderMeshAndObject_Create, 'get_mesh')
		op = layout.operator(SC_OT_SCMeshToBlenderMeshAndObject_Create.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()

	# op.node_method_name_to_call = "get_mesh"

	# def _get_mesh_necessary_data(self):
	# 	mesh_source_node: typing.Union[SC_Meshes_Getter_Node, Node] = self.inputs['DATA_Mesh'].links[0].from_node
	# 	mesh_data: DATA_Mesh = mesh_source_node.get_sc_meshes()
	# 	return mesh_data

	def _get_sc_objects_necessary_data(self, *args, **kwargs):
		mesh_source_node: typing.Union[DATA_GETTER_NODE_SC_Meshes, Node] = self.inputs[0].links[0].from_node
		mesh_data: DATA_Mesh = mesh_source_node.get_sc_meshes()
		return mesh_data[0]

	# @Node.get_data_first
	# def get_sc_objects(self, *args, **kwargs):

	@Node.get_data_first
	# def get_mesh(self, *args, **kwargs):
	def get_sc_objects(self, mesh_data: DATA_Mesh, *args, **kwargs):
		# get/create object -------------------------------------------------------------------------------------------------------
		mesh_todel = None
		try:
			blender_object: bpy.types.Object = bpy.data.objects[self.object_name]
		except:
			if not mesh_data.bl_mesh:
				mesh_todel = mesh_data.bl_mesh = bpy.data.meshes.new("todel")
			blender_object = bpy.data.objects.new(self.object_name, mesh_data.bl_mesh)
			bpy.context.scene.collection.objects.link(blender_object)
		# blender_mesh = mesh_data.bl_mesh
		blender_object.data = mesh_data.bl_mesh

		# if False:
		# if not mesh_data.bmesh:
		if mesh_data.verts:
			node_name_for_display = self.label if self.label else self.name
			print(
				node_name_for_display + ": CREATING MESH (some steps are internal to Blender and can't display a progress indicator and will lengthen total time)")

			# get/create mesh -------------------------------------------------------------------------------------------------------
			print('\t' + "cleaning mesh...", end='', flush=True)
			try:
				blender_mesh: bpy.types.Mesh = bpy.data.meshes[self.mesh_name]
				# bpy.ops.object.mode_set(mode='OBJECT')
				utils.definir_blender_mode('OBJECT')
				bm = bmesh.new()  # on écrase le mesh existant en lui assignant un bmesh vide
				bm.to_mesh(blender_mesh)
				bm.free()
			except:
				blender_mesh = bpy.data.meshes.new(self.mesh_name)
			print("done")

			# get/create object -------------------------------------------------------------------------------------------------------
			try:
				blender_object: bpy.types.Object = bpy.data.objects[self.object_name]
			except:
				blender_object = bpy.data.objects.new(self.object_name, blender_mesh)
				bpy.context.scene.collection.objects.link(blender_object)
			blender_object.data = blender_mesh

			# set verts and faces -------------------------------------------------------------------------------------------------------
			print('\t' + "creating geometry...", end='', flush=True)
			blender_mesh.from_pydata(mesh_data.verts, mesh_data.edges, mesh_data.faces)
			blender_mesh.update()
			print("done")

			# set material indices -------------------------------------------------------------------------------------------------------
			print('\t' + "setting material indices...", end='', flush=True)
			max_material_index = -1
			for polygon_nb, polygon in enumerate(blender_mesh.polygons):
				polygon.material_index = mesh_data.faces_material_indices[polygon_nb]
				if polygon.material_index > max_material_index:
					max_material_index = polygon.material_index
			bpy.context.view_layer.objects.active = blender_object
			missing_slots = (max_material_index + 1) - len(blender_object.material_slots)
			for materiel_index in range(missing_slots):
				bpy.ops.object.material_slot_add()
			print("done")

			# vertex groups -------------------------------------------------------------------------------------------------------
			# for vertex_group_name, vertex_group_indices in sc_mesh.verts_groups.items():
			# 	blender_mesh.

			# vertex colors -------------------------------------------------------------------------------------------------------
			# for name, colors in sc_mesh.colorLayers.items():
			# 	blender_mesh.vertex_colors.new(name=name)
			# 	for vert_index, vert_color in enumerate(colors):
			# 		blender_mesh.vertex_colors[name].data[vert_index].color = vert_color

			# set UV layers -------------------------------------------------------------------------------------------------------
			# print(node_name_for_display + ": creating UV layers...", end='', flush=True)
			print('\t' + "creating UV layers...")
			uv_layers_startTime = time.time()
			last_progress_display_time = time.time()

			for uv_layer_name, uvs_per_face in mesh_data.uvs_per_face_layers.items():
				# blender_mesh.uv_textures.new(name=uv_layer_name)
				blender_mesh.uv_layers.new(name=uv_layer_name)
				for polygon_nb, polygon in enumerate(blender_mesh.polygons):
					for vert_nb, loop_index in enumerate(polygon.loop_indices):
						polygon_vertex_uv = uvs_per_face[polygon_nb][vert_nb]
						blender_mesh.uv_layers[uv_layer_name].data[loop_index].uv = polygon_vertex_uv

					if time.time() > last_progress_display_time + .25:
						last_progress_display_time = time.time()
						self.afficher_barre_progression(
							polygon_nb / len(blender_mesh.polygons),
							time.time() - uv_layers_startTime,
							prefix="\t", no_message=True)

			self.afficher_barre_progression(1, time.time() - uv_layers_startTime, prefix="\t", no_message=True)

		# bmesh
		# prepare final mesh to put bmesh into
		# else:
		# 	try:
		# 		blender_mesh2: bpy.types.Mesh = bpy.data.meshes[self.mesh_name]
		# 		utils.definir_blender_mode('OBJECT')
		# 		bm = bmesh.new()  # on écrase le mesh existant en lui assignant un bmesh vide
		# 		bm.to_mesh(blender_mesh2)
		# 		bm.free()
		# 	except:
		# 		blender_mesh2 = bpy.data.meshes.new(self.mesh_name)
		# 	mesh_data.bmesh.to_mesh(blender_mesh2)
		# 	# prepare object for bmesh mesh
		# 	try:
		# 		blender_object2 = bpy.data.objects[self.object_name]
		# 	except:
		# 		blender_object2 = bpy.data.objects.new(self.object_name, blender_mesh2)
		# 		# bpy.context.scene.collection.objects.link(blender_object)
		# 		bpy.context.collection.objects.link(blender_object2)
		# 	blender_object2.data = blender_mesh2
		# print("All done")
		# print('ok')
		# sc_object = DATA_Object(blender_object)
		# return [sc_object]
		# blender_object.data = blender_mesh
		blender_object.hide_set(False)
		if mesh_todel:
			bpy.data.meshes.remove(mesh_todel)
		return None


# class SC_OT_SCMeshToBlenderMeshAndObject_Create(bpy.types.Operator, SC_Operator):
class SC_OT_SCMeshToBlenderMeshAndObject_Create(bpy.types.Operator):
	bl_idname = 'sc.sc_mesh_to_blender_mesh_and_object_create'
	bl_description = ''
	bl_label = 'Create / update Blender mesh + object'
	# node_method_name_to_call = "get_mesh"
	source_node_path: bpy.props.StringProperty()

	# def get_data_for_execution(self, source_node: SCMeshToBLMeshAndBLObjectNode):
	# 	mesh_source_node: typing.Union[SC_Meshes_Getter_Node, Node] = source_node.inputs['DATA_Mesh'].links[0].from_node
	# 	sc_mesh: DATA_Mesh = mesh_source_node.get_sc_meshes()
	# 	return sc_mesh

	# def SC_execute(self, source_node: SCMeshToBLMeshAndBLObjectNode):
	def execute(self, context):
		# source_node: typing.Union[DATA_GETTER_NODE_SC_Meshes, Node] = eval(self.source_node_path)
		source_node: DATA_GETTER_NODE_SC_Objects = eval(self.source_node_path)
		# sc_mesh: DATA_Mesh = source_node.get_sc_meshes()
		# source_node.get_mesh()
		source_node.get_sc_objects()

		# if not sc_mesh.bmesh:
		# 	node_name_for_display = source_node.label if source_node.label else source_node.name
		# 	print(
		# 		node_name_for_display + ": CREATING MESH (some steps are internal to Blender and can't display a progress indicator and will lengthen total time)")
		#
		# 	# get/create mesh -------------------------------------------------------------------------------------------------------
		# 	print('\t' + "cleaning mesh...", end='', flush=True)
		# 	try:
		# 		blender_mesh: bpy.types.Mesh = bpy.data.meshes[source_node.mesh_name]
		# 		# bpy.ops.object.mode_set(mode='OBJECT')
		# 		utils.definir_blender_mode('OBJECT')
		# 		bm = bmesh.new()  # on écrase le mesh existant en lui assignant un bmesh vide
		# 		bm.to_mesh(blender_mesh)
		# 		bm.free()
		# 	except:
		# 		blender_mesh = bpy.data.meshes.new(source_node.mesh_name)
		# 	print("done")
		#
		# 	# get/create object -------------------------------------------------------------------------------------------------------
		# 	try: blender_object: bpy.types.Object = bpy.data.objects[source_node.object_name]
		# 	except:
		# 		blender_object = bpy.data.objects.new(source_node.object_name, blender_mesh)
		# 		# bpy.context.scene.collection.objects.link(blender_object)
		# 		bpy.context.collection.objects.link(blender_object)
		# 	blender_object.data = blender_mesh
		#
		# 	# set verts and faces -------------------------------------------------------------------------------------------------------
		# 	print('\t' + "creating geometry...", end='', flush=True)
		# 	blender_mesh.from_pydata(sc_mesh.verts, sc_mesh.edges, sc_mesh.faces)
		# 	blender_mesh.update()
		# 	print("done")
		#
		# 	# set material indices -------------------------------------------------------------------------------------------------------
		# 	print('\t' + "setting material indices...", end='', flush=True)
		# 	max_material_index = -1
		# 	for polygon_nb, polygon in enumerate(blender_mesh.polygons):
		# 		polygon.material_index = sc_mesh.faces_material_indices[polygon_nb]
		# 		if polygon.material_index > max_material_index:
		# 			max_material_index = polygon.material_index
		# 	bpy.context.view_layer.objects.active = blender_object
		# 	missing_slots = (max_material_index + 1) - len(blender_object.material_slots)
		# 	for materiel_index in range(missing_slots):
		# 		bpy.ops.object.material_slot_add()
		# 	print("done")
		#
		# 	# vertex groups -------------------------------------------------------------------------------------------------------
		# 	# for vertex_group_name, vertex_group_indices in sc_mesh.verts_groups.items():
		# 	# 	blender_mesh.
		#
		# 	# vertex colors -------------------------------------------------------------------------------------------------------
		# 	# for name, colors in sc_mesh.colorLayers.items():
		# 	# 	blender_mesh.vertex_colors.new(name=name)
		# 	# 	for vert_index, vert_color in enumerate(colors):
		# 	# 		blender_mesh.vertex_colors[name].data[vert_index].color = vert_color
		#
		# 	# set UV layers -------------------------------------------------------------------------------------------------------
		# 	# print(node_name_for_display + ": creating UV layers...", end='', flush=True)
		# 	print('\t' + "creating UV layers...")
		# 	uv_layers_startTime = time.time()
		# 	last_progress_display_time = time.time()
		#
		# 	for uv_layer_name, uvs_per_face in sc_mesh.uvs_per_face_layers.items():
		# 		# blender_mesh.uv_textures.new(name=uv_layer_name)
		# 		blender_mesh.uv_layers.new(name=uv_layer_name)
		# 		for polygon_nb, polygon in enumerate(blender_mesh.polygons):
		# 			for vert_nb, loop_index in enumerate(polygon.loop_indices):
		# 				polygon_vertex_uv = uvs_per_face[polygon_nb][vert_nb]
		# 				blender_mesh.uv_layers[uv_layer_name].data[loop_index].uv = polygon_vertex_uv
		#
		# 			if time.time() > last_progress_display_time + .25:
		# 				last_progress_display_time = time.time()
		# 				source_node.afficher_barre_progression(
		# 					polygon_nb / len(blender_mesh.polygons),
		# 					time.time() - uv_layers_startTime,
		# 					prefix="\t", no_message=True)
		#
		# 	source_node.afficher_barre_progression(1, time.time() - uv_layers_startTime, prefix="\t", no_message=True)
		#
		# # bmesh
		# # prepare final mesh to put bmesh into
		# else:
		# 	try:
		# 		blender_mesh2: bpy.types.Mesh = bpy.data.meshes[source_node.mesh_name]
		# 		utils.definir_blender_mode('OBJECT')
		# 		bm = bmesh.new()  # on écrase le mesh existant en lui assignant un bmesh vide
		# 		bm.to_mesh(blender_mesh2)
		# 		bm.free()
		# 	except:
		# 		blender_mesh2 = bpy.data.meshes.new(source_node.mesh_name)
		# 	sc_mesh.bmesh.to_mesh(blender_mesh2)
		# 	# prepare object for bmesh mesh
		# 	try:
		# 		blender_object2 = bpy.data.objects[source_node.object_name]
		# 	except:
		# 		blender_object2 = bpy.data.objects.new(source_node.object_name, blender_mesh2)
		# 		# bpy.context.scene.collection.objects.link(blender_object)
		# 		bpy.context.collection.objects.link(blender_object2)
		# 	blender_object2.data = blender_mesh2
		#
		# # print("All done")
		return {'FINISHED'}
