import bpy, mathutils
import random, math, time, bmesh
from typing import List
from . import Node, SOCKET_Meshes, SC_Curves_Socket, DATA_GETTER_NODE_SC_Meshes, DATA_Mesh, SOCKET_Path, SOCKET_old_SceneObjects
from . import (Path, PathsGetter, SC_Curve, DATA_GETTER_NODE_SC_Curves, CurveBezierPoint, SceneObjectsGetter, delete_datablocks_with_name_prefix,
			   SC_OT_RandomizeSeedNode)
from .. import my_globals, utils


class Get_Curves(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Curves):
	bl_idname = 'sc_node_tcrk153cxuc1ho45g22l'
	bl_label = 'Get curve'
	curve: bpy.props.PointerProperty(name='Curve', type=bpy.types.Curve, update=Node.prop_updated)
	at_least_one_input_socket_required = False

	def sc_init(self, context):
		self.create_output(SC_Curves_Socket, is_new_data_output=False)

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

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'curve', text='')

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

	@Node.get_data_first
	def get_sc_curves(self, *args, **kwargs):
		if not self.curve:
			self.print('No curve specified')
			raise ValueError
		else:
			sc_curve = SC_Curve(self.curve)
			self.print(f'Curve "{self.curve.name}" found')
		utils.definir_blender_mode('OBJECT')
		return [sc_curve]


class Mesh_Edges_To_Bezier_Curves(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Curves):
	bl_idname = 'sc_node_97my6l1msh0pbhz5ecbr'
	bl_label = 'Mesh edges to bezier curves'
	splines_handles_type: bpy.props.EnumProperty(
		name='Handle types',
		description='',
		items=[
			('AUTO', 'Auto', 'Best for maximum curve smoothness'),
			('VECTOR', 'Vector', 'The curve will follow the mesh edges exactly'),
		])

	# should_use_angles: bpy.props.BoolProperty(name='Use angles at intersections', default=True,
	# 	description="Where 3 or more edges meet at a vertex, keep the edges that make the largest angles (=closely aligned) connected, "
	# 				"and disconnect those that form sharp angles")
	# sharp_angle_limit_radians: bpy.props.FloatProperty(name='Sharp angle limit',
	# 	description='Edges that form angles below this angle will be disconnected (only works at intersections)', default=math.pi / 2,
	# 	subtype='ANGLE', min=0, max=math.pi)

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

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

	# layout.prop(self, 'should_use_angles')
	# layout.prop(self, 'sharp_angle_limit_radians')

	# def _get_sc_curves_necessary_data(self, *args, **kwargs):
	# 	input: SC_Meshes_Getter_Node = self.inputs[0].links[0].from_node
	# 	return input.get_sc_meshes()

	@Node.get_data_first
	def get_sc_curves(self, meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_curves: List[SC_Curve] = []
		for mesh in meshes:
			# sc_mesh_intersections_removed = utils.removerEdgesIntersections(mesh, self.should_use_angles, self.sharp_angle_limit_radians)
			bl_curve = bpy.data.curves.new(mesh.bl_mesh.name + '(edges to curves)', 'CURVE')

			bl_curve.dimensions = '3D'
			bm = bmesh.new()
			bm.from_mesh(mesh.bl_mesh)
			list_of_connected_edges = utils.get_connected_edges(bm, True)
			bpoints_to_flatten = []
			for connected_edges in list_of_connected_edges:
				must_link_first_and_last = tuple(connected_edges[0]) == tuple(connected_edges[-1])
				if must_link_first_and_last and len(connected_edges) >= 3:
					connected_edges.pop()
				spline = bl_curve.splines.new('BEZIER')
				spline.bezier_points.add(len(connected_edges) - 1)  # -1 car une spline a au moins un point
				bpoints_to_flatten.append(spline.bezier_points[0])
				bpoints_to_flatten.append(spline.bezier_points[-1])
				for i, vert_i_co in enumerate(connected_edges):
					bezier_point_i = spline.bezier_points[i]
					bezier_point_i.co = vert_i_co
					bezier_point_i.handle_left_type = self.splines_handles_type
					bezier_point_i.handle_right_type = self.splines_handles_type

				spline.use_cyclic_u = must_link_first_and_last
			sc_curve = SC_Curve(bl_curve)

			for bpoint in bpoints_to_flatten:
				bpoint.handle_left_type = 'FREE'
				bpoint.handle_right_type = 'FREE'
				bpoint.handle_left = (bpoint.handle_left[0], bpoint.handle_left[1], bpoint.co[2])
				bpoint.handle_right = (bpoint.handle_right[0], bpoint.handle_right[1], bpoint.co[2])

			bm.free()
			result_sc_curves.append(sc_curve)
		return result_sc_curves


class PathGeneratorNode(bpy.types.Node, Node, PathsGetter):
	bl_idname = 'sc_node_6z7nsdwvd4l76nuijlr3'
	bl_label = 'Paths generator'
	at_least_one_input_socket_required = False

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

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

	total_paths: bpy.props.IntProperty(
		name='Total paths',
		description='Number of paths to generate',
		default=10,
		min=0, )

	area_size_xy: bpy.props.IntVectorProperty(
		name='Area size XY',
		description='Area in which the paths will be restricted to',
		default=(50, 50),
		size=2,
		min=1)

	should_loop: bpy.props.BoolProperty(
		name='Loop',
		description='Should the first and last points of the paths be connected',
		default=True)

	total_segments_min_max: bpy.props.IntVectorProperty(
		name='# of segments',
		description="Min/max. Per path",
		default=(20, 50),
		min=0,
		size=2)

	segments_length_min_max: bpy.props.FloatVectorProperty(
		name='Segments length',
		description="Min/max. Per path",
		default=(1, 5),
		min=0,
		precision=3,
		size=2)

	turns_amplitude_degrees: bpy.props.FloatVectorProperty(
		name='Turns amplitude',
		description="When a path makes a turn, by how many degrees can it turn. It will be both ways, positive and negative angles",
		default=(89, 91),
		min=0, max=180,
		size=2)

	start_pos_limit_percent: bpy.props.FloatProperty(
		name='Starting pos sub-area',
		description="Percentage of the full area. A path's starting location can be inside a smaller area, depending on this parameter. "
					"Then it goes randomly inside the whole area",
		default=.75,
		min=0, max=1)

	start_angle_mode: bpy.props.EnumProperty(
		name='Start angle mode',
		description='How will the starting angle be set for each path',
		items=[
			('cardinal directions', 'Along a cardinal direction', '', 1),
			('random', 'Random', '', 2),
		])

	start_angle_cardinal_directions: bpy.props.IntProperty(
		name='Cardinal directions',
		description='When a path starts, its initial angle will be chosen among one of the cardinal directions. Cardinal directions are directions '
					'which are as opposite as possible',
		default=4,
		min=3, )

	start_height: bpy.props.FloatVectorProperty(
		name='Start height',
		description="Random min/max",
		default=(.1, .2),
		min=0,
		precision=2,
		size=2)

	height_variation_max: bpy.props.FloatProperty(
		name='Height variation',
		description="Max variation in height between 2 points of a path",
		default=.1)

	base_height: bpy.props.FloatProperty(
		name='Base height',
		description="Paths points won't go below that height",
		default=0)

	def sc_init(self, context):
		self.width = 350
		self.create_output(SOCKET_Path)
		self.random_seed = random.randint(0, 9e5)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc2(layout)
		layout.prop(self, 'total_paths')
		layout.prop(self, 'area_size_xy')
		row = layout.row()
		row.prop(self, 'random_seed')
		self.create_operator(row, SC_OT_RandomizeSeedNode)
		# self.create_operator(layout, )
		layout.prop(self, 'total_segments_min_max')
		layout.prop(self, 'segments_length_min_max')
		layout.prop(self, 'turns_amplitude_degrees')
		layout.prop(self, 'start_pos_limit_percent')
		layout.prop(self, 'start_angle_mode')
		if self.start_angle_mode == 'cardinal directions':
			layout.prop(self, 'start_angle_cardinal_directions')
		layout.prop(self, 'start_height')
		layout.prop(self, 'height_variation_max')
		layout.prop(self, 'base_height')

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

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

	@Node.get_data_first
	def get_paths(self, *args, **kwargs):
		# startTime = time.time()
		random.seed(self.random_seed)
		paths = []
		left_limit = -self.area_size_xy[0] / 2
		right_limit = self.area_size_xy[0] / 2
		top_limit = self.area_size_xy[1] / 2
		bottom_limit = -self.area_size_xy[1] / 2
		cardinal_angles_step = 2 * math.pi / self.start_angle_cardinal_directions
		cardinal_angles = [cardinal_angles_step * i for i in range(self.start_angle_cardinal_directions)]
		for path_nb in range(self.total_paths):
			current_path = Path()
			current_path.loop = True
			paths.append(current_path)
			current_pos = first_pos = (
				random.uniform(left_limit * self.start_pos_limit_percent, right_limit * self.start_pos_limit_percent),
				random.uniform(bottom_limit * self.start_pos_limit_percent, top_limit * self.start_pos_limit_percent),
				max(self.base_height, random.uniform(self.start_height[0], self.start_height[1])))
			current_path.points.append(current_pos)
			if self.start_angle_mode == 'random':
				current_angle_radians = random.uniform(0, 2 * math.pi)
			else:
				current_angle_radians = random.choice(cardinal_angles)
			# current_angle_radians += random.uniform(self.turns_amplitude[0], self.turns_amplitude[1])
			total_segments = random.randint(self.total_segments_min_max[0], self.total_segments_min_max[1])
			for segment_nb in range(total_segments - 1):
				turn_angle = math.radians(random.uniform(self.turns_amplitude_degrees[0], self.turns_amplitude_degrees[1]))
				turn_angle_is_negative = random.random() >= .5
				if turn_angle_is_negative:
					turn_angle = -turn_angle
				current_angle_radians += turn_angle
				segment_length = random.uniform(self.segments_length_min_max[0], self.segments_length_min_max[1])
				current_pos = (
					current_pos[0] + segment_length * math.cos(current_angle_radians),
					current_pos[1] + segment_length * math.sin(current_angle_radians),
					max(self.base_height, current_pos[2] + random.uniform(-self.height_variation_max / 2, self.height_variation_max / 2)))
				current_path.points.append(current_pos)
				# if last pos is outside area, finish this path
				if current_pos[0] < left_limit or current_pos[0] > right_limit or current_pos[1] < bottom_limit or current_pos[1] > top_limit:
					break
			# put last point to be at the same x or y as the last point. And in-between height
			middle_height = (current_pos[2] + first_pos[2]) / 2
			if abs(current_pos[0] - first_pos[0]) < abs(current_pos[1] - first_pos[1]):
				current_path.points.append((first_pos[0], current_pos[1], middle_height))
			else:
				current_path.points.append((current_pos[0], first_pos[1], middle_height))
		# self.last_operation_time = time.time() - startTime
		return paths


class PathsToCurvesNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Curves):
	bl_idname = 'sc_node_5ze6mj424209utuafm54'
	bl_label = 'Paths to curves'

	turn_size: bpy.props.FloatProperty(
		name='Turn size',
		description="When a path changes direction, a larger turn size will result in a smoother change of direction than a small one, for the output curve",
		default=1,
		min=0.01)

	def sc_init(self, context):
		self.width = 150
		# self.inputs.new('PathsSocket', 'Paths')
		self.create_input(SOCKET_Path, is_required=True)
		# self.outputs.new('SC_Curves_Socket', 'Curves')
		self.create_output(SC_Curves_Socket)

	def sc_draw_buttons(self, context, layout):
		# if len(self.inputs['Paths'].links) <= 0:
		# 	layout.label(text="Source paths needed", icon="ERROR")
		#
		# self.ui_display_doc2(layout)
		layout.prop(self, 'turn_size')

	# def get_input_sc_bl_data(self, *args, **kwarhs):
	# 	return self.get_sc_curves()

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

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

	@Node.get_data_first
	def get_sc_curves(self, *args, **kwargs):
		curves = []

		# get input paths
		paths_node: PathsGetter = self.inputs[0].links[0].from_node
		paths = paths_node.get_paths()

		# create 1 curve per path
		for path in paths:
			current_curve = SC_Curve(None)
			current_curve.loop = True
			curves.append(current_curve)

			for point_nb, current_path_point in enumerate(path.points):
				# find next and previous points
				# previous_point = None
				previous_point = path.points[-1]  # suppose loop
				if point_nb == 0 and path.loop:
					previous_point = path.points[-1]
				elif point_nb > 0:
					previous_point = path.points[point_nb - 1]
				# if previous_point:
				vect_2_previous_point = mathutils.Vector((
					previous_point[0] - current_path_point[0],
					previous_point[1] - current_path_point[1],
					previous_point[2] - current_path_point[2]))
				# next_point = None
				next_point = path.points[0]  # suppose loop
				if point_nb == len(path.points) - 1 and path.loop:
					next_point = path.points[0]
				elif point_nb < len(path.points) - 1:
					next_point = path.points[point_nb + 1]
				vect_2_next_point = mathutils.Vector((
					next_point[0] - current_path_point[0],
					next_point[1] - current_path_point[1],
					next_point[2] - current_path_point[2]))

				# previous curve point
				# pos
				previous_curve_point = CurveBezierPoint()
				current_path_point_pos_vect = mathutils.Vector((current_path_point[0], current_path_point[1], current_path_point[2]))
				previous_curve_point_pos_vect = current_path_point_pos_vect + vect_2_previous_point.normalized() * self.turn_size
				previous_segment_length = mathutils.Vector((
					previous_point[0] - current_path_point[0],
					previous_point[1] - current_path_point[1],
					previous_point[2] - current_path_point[2])).length
				# attention si location dépasse parce que segment précédent trop court, ramener, et laisser un petit espace pour le left handle
				if previous_segment_length < (self.turn_size + .1) * 2:
					previous_curve_point_pos_vect = current_path_point_pos_vect + vect_2_previous_point.normalized() * (previous_segment_length / 2 - .1)
				previous_curve_point.pos = (previous_curve_point_pos_vect[0], previous_curve_point_pos_vect[1], previous_curve_point_pos_vect[2])
				# left handle
				previous_curve_point.left_handle_type = 'FREE'
				previous_curve_point_left_handle_pos_vect = previous_curve_point_pos_vect + vect_2_previous_point.normalized() * .1
				previous_curve_point.left_handle_pos = (
					previous_curve_point_left_handle_pos_vect[0],
					previous_curve_point_left_handle_pos_vect[1],
					previous_curve_point_left_handle_pos_vect[2])
				# right handle
				previous_curve_point.right_handle_type = 'FREE'
				previous_curve_point.right_handle_pos = (current_path_point[0], current_path_point[1], current_path_point[2])

				# next curve point
				# pos
				next_curve_point = CurveBezierPoint()
				next_curve_point_pos_vect = current_path_point_pos_vect + vect_2_next_point.normalized() * self.turn_size
				next_segment_length = mathutils.Vector((
					next_point[0] - current_path_point[0],
					next_point[1] - current_path_point[1],
					next_point[2] - current_path_point[2])).length
				# attention si location dépasse parce que segment suivant trop court, ramener, et laisser un petit espace pour le right handle
				if next_segment_length < (self.turn_size + .1) * 2:
					next_curve_point_pos_vect = current_path_point_pos_vect + vect_2_next_point.normalized() * (next_segment_length / 2 - .1)
				next_curve_point.pos = (next_curve_point_pos_vect[0], next_curve_point_pos_vect[1], next_curve_point_pos_vect[2])
				# left handle
				next_curve_point.left_handle_type = 'FREE'
				next_curve_point.left_handle_pos = (current_path_point[0], current_path_point[1], current_path_point[2])
				# right handle
				next_curve_point.right_handle_type = 'FREE'
				next_curve_point_right_handle_pos_vect = next_curve_point_pos_vect + vect_2_next_point.normalized() * .1
				next_curve_point.right_handle_pos = (
					next_curve_point_right_handle_pos_vect[0],
					next_curve_point_right_handle_pos_vect[1],
					next_curve_point_right_handle_pos_vect[2])
				# next_curve_point = CurveBezierPoint()
				# current_curve_point.pos =
				# left handle est tjrs vers le point d'avant
				current_curve.bezier_points.append(previous_curve_point)
				current_curve.bezier_points.append(next_curve_point)
		return curves


class SC_OT_ObjectsRepeatedAlongCurvesNodeCreateObjects(bpy.types.Operator):
	bl_idname = 'node.objects_repeated_along_curves_node_create_objects'
	bl_description = ''
	bl_label = 'Create 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: BlenderObjectsRepeatedAlongCurvesNode
		curves_source_node = source_node.inputs[0].links[0].from_node  # type:SceneObjectsGetter
		blender_curve_objects = curves_source_node.get_scene_objects()
		model_data = bpy.data.meshes[source_node.model_mesh_name]
		for blender_curve_object in blender_curve_objects:
			blender_curve = blender_curve_object.data
			if type(blender_curve) != bpy.types.Curve:
				continue
			# créer objet à répéter
			new_object_to_repeat = bpy.data.objects.new(source_node.objects_name_prefix, model_data)
			new_object_to_repeat.scale = (source_node.default_objects_scale, source_node.default_objects_scale, source_node.default_objects_scale)
			# context.scene.objects.link(new_object_to_repeat)
			context.collection.objects.link(new_object_to_repeat)
			# ajouter les modifiers
			array_modifier = new_object_to_repeat.modifiers.new('Along curve array', 'ARRAY')
			# array_modifier.fit_type = 'FIT_LENGTH'
			array_modifier.fit_type = 'FIT_CURVE'
			array_modifier.curve = blender_curve_object
			# array_modifier.fit_length = get_blender_curve_length(blender_curve)
			# array_modifier.fit_length = 150
			curve_modifier = new_object_to_repeat.modifiers.new('Along curve array', 'CURVE')
			curve_modifier.object = blender_curve_object
			# new_object_to_repeat.parent = blender_curve_object
			new_object_to_repeat.show_wire = True

		return {'FINISHED'}


class SC_OT_ObjectsRepeatedAlongCurvesNodeDeleteObjects(bpy.types.Operator):
	bl_idname = 'node.objects_repeated_along_curves_node_delete_objects'
	bl_description = "Objects are retrieved by name prefix. Mutli-user objects won't be deleted"
	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: BlenderObjectsRepeatedAlongCurvesNode
		delete_datablocks_with_name_prefix('objects', source_node.objects_name_prefix)
		return {'FINISHED'}


class BlenderObjectsRepeatedAlongCurvesNode(bpy.types.Node, Node, SceneObjectsGetter):
	bl_idname = 'sc_node_g3bjhixwvmdlvup3wc75'
	bl_label = 'Blender objects repeated along curves'

	objects_name_prefix: bpy.props.StringProperty(
		name='New objects prefix',
		description="",
		default='Curve follower')

	parent_object_name: bpy.props.StringProperty(
		name='Parent name',
		description="(Optional) New objects will have their parent set to this object",
		default='')

	model_mesh_name: bpy.props.StringProperty(
		name='Mesh to repeat',
		description="The new created objects will be instances of this model object",
		default='')

	default_objects_scale: bpy.props.FloatProperty(
		name='Default objects scale',
		description="",
		default=1)

	def sc_init(self, context):
		self.width = 350
		self.create_input(SOCKET_old_SceneObjects, is_required=True)
		# self.create_output(SOCKET_old_SceneObjects)

	def sc_draw_buttons(self, context, layout):
		# if len(self.inputs['SC_Curve objects'].links) <= 0:
		# 	layout.label(text="Source curve objects needed", icon="ERROR")
		#
		# self.ui_display_doc2(layout)
		layout.prop(self, 'objects_name_prefix', icon='OBJECT_DATA')
		# layout.prop(self, 'parent_object_name', icon='OBJECT_DATA')
		split = layout.split(factor=.95)
		split.prop(self, 'model_mesh_name', icon='MESH_DATA')
		if self.model_mesh_name in bpy.data.meshes:
			split.label(text='', icon_value=my_globals.icônes['tick'].icon_id)
		else:
			split.label(text='', icon='CANCEL')
		# layout.prop(self, 'model_mesh_name', icon='MESH_DATA')
		# layout.prop(self, 'default_objects_scale')
		op = layout.operator(SC_OT_ObjectsRepeatedAlongCurvesNodeCreateObjects.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()
		op = layout.operator(SC_OT_ObjectsRepeatedAlongCurvesNodeDeleteObjects.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()


	def get_scene_objects(self, **kwargs):
		return []


class CurveObjectsNodeCreateObjectsOp(bpy.types.Operator):
	bl_idname = 'node.curve_objects_node_create_objects'
	bl_description = ''
	bl_label = 'Create curves and objects'
	source_node_path: bpy.props.StringProperty()

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

		source_node: CurvesToBlenderObjectsNode = eval(self.source_node_path)
		source_node.create_curves_and_objects()
		# curves_source_node: DATA_GETTER_NODE_SC_Curves = source_node.inputs[0].links[0].from_node
		# curves = curves_source_node.get_sc_curves()
		# for current_curve in curves:
		# 	blender_curve_datablock = bpy.data.curves.new(source_node.curves_name_prefix, 'CURVE')
		# 	blender_curve_datablock.twist_mode = 'Z_UP'
		# 	blender_curve_datablock.dimensions = '3D'
		# 	spline = blender_curve_datablock.splines.new('BEZIER')
		# 	spline.use_cyclic_u = True
		# 	spline.bezier_points.add(len(current_curve.bezier_points) - 1)  # -1 parce que il y a déjà un point par défaut
		# 	for nb, current_bezier_point in enumerate(current_curve.bezier_points):
		# 		current_blender_bezier_point = spline.bezier_points[nb]
		# 		current_blender_bezier_point.radius = source_node.curves_points_radius
		# 		current_blender_bezier_point.co = current_bezier_point.pos
		# 		current_blender_bezier_point.handle_left = current_bezier_point.left_handle_pos
		# 		current_blender_bezier_point.handle_left_type = current_bezier_point.left_handle_type
		# 		current_blender_bezier_point.handle_right = current_bezier_point.right_handle_pos
		# 		current_blender_bezier_point.handle_right_type = current_bezier_point.right_handle_type
		# 	blender_object = bpy.data.objects.new(source_node.objects_name_prefix, blender_curve_datablock)
		# 	blender_object.scale = (source_node.default_objects_scale, source_node.default_objects_scale, source_node.default_objects_scale)
		# 	context.collection.objects.link(blender_object)
		# 	# il semble qu'il faille définir la résolution après avoir ajouté tous les points, sinon elle n'est pas prise en compte
		# 	blender_curve_datablock.resolution_u = source_node.curves_resolution_u
		return {'FINISHED'}


class CurveObjectsNodeDeleteObjectsOp(bpy.types.Operator):
	bl_idname = 'node.curve_objects_node_delete_objects'
	bl_description = "Objects are retrieved by name prefix. Mutli-user curve sc_bldata_list and objects won't be deleted"
	bl_label = 'Delete curves and 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: CurvesToBlenderObjectsNode
		delete_datablocks_with_name_prefix('objects', source_node.objects_name_prefix)
		delete_datablocks_with_name_prefix('curves', source_node.objects_name_prefix)
		return {'FINISHED'}


class CurvesToBlenderObjectsNode(bpy.types.Node, Node, SceneObjectsGetter):
	bl_idname = 'sc_node_4028bwgsec44ntetvhr3'
	bl_label = 'Curves to Blender objects'

	objects_name_prefix: bpy.props.StringProperty(
		name='New objects prefix',
		description="Name prefix of newly created objects. When deleting, objects with this prefix in their name will be deleted",
		default='City curve ')

	curves_name_prefix: bpy.props.StringProperty(
		name='New curves prefix',
		description="Name prefix of newly created Blender curves sc_bldata_list. When deleting, curves with this prefix in their name will be deleted",
		default='City curve ')

	parent_object_name: bpy.props.StringProperty(
		name='Parent name',
		description="(Optional) New objects will have their parent set to this object",
		default='')

	default_objects_scale: bpy.props.FloatProperty(
		name='Default objects scale',
		description="",
		default=1)

	curves_resolution_u: bpy.props.IntProperty(
		name='Curves resolution U',
		description="A higher resolution means smoother curves",
		default=1024,
		max=1024)

	curves_points_radius: bpy.props.FloatProperty(
		name='Curves points radius',
		description="Each point along a curve has a radius, affecting different things, such as the size of the objects following the curve, at that point.",
		default=1)

	# let user choose what to do with unused existing objects and curves when creating/updating:
	# 	delete / hide in viewport or for render / do nothing

	def sc_init(self, context):
		self.create_input(SC_Curves_Socket, is_required=True)
		self.create_output(SOCKET_old_SceneObjects)

	def sc_draw_buttons(self, context, layout):
		# if len(self.inputs['Curves'].links) <= 0:
		# 	layout.label(text="Source curves needed", icon="ERROR")
		#
		# self.ui_display_doc2(layout)
		# split = layout.split(0.5)
		# split.label(text=CurvesToBlenderObjectsNode.objects_name_prefix.name)
		layout.prop(self, 'objects_name_prefix', icon='OBJECT_DATA')
		# layout.prop(self, 'parent_object_name', icon='OBJECT_DATA')
		layout.prop(self, 'curves_name_prefix', icon='CURVE_DATA')
		# layout.prop(self, 'default_objects_scale')
		row = layout.row()
		# row.prop(self, 'curves_resolution_u')
		# row.prop(self, 'curves_points_radius')
		# op = layout.operator('node.curve_objects_node_create_objects')
		op = layout.operator(CurveObjectsNodeCreateObjectsOp.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()
		# op = layout.operator('node.curve_objects_node_delete_objects')
		op = layout.operator(CurveObjectsNodeDeleteObjectsOp.bl_idname)
		op.source_node_path = 'bpy.data.node_groups["' + self.id_data.name + '"].' + self.path_from_id()

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

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

	@Node.get_data_first
	def get_scene_objects(self, *args, **kwargs):
		self.create_curves_and_objects()
		blender_objects = []
		for object in bpy.data.objects:
			if object.name.startswith(self.objects_name_prefix):
				blender_objects.append(object)
		return blender_objects

	def create_curves_and_objects(self):
		curves_source_node: DATA_GETTER_NODE_SC_Curves = self.inputs[0].links[0].from_node
		curves = curves_source_node.get_sc_curves()
		for current_curve in curves:
			blender_curve_datablock = bpy.data.curves.new(self.curves_name_prefix, 'CURVE')
			blender_curve_datablock.twist_mode = 'Z_UP'
			blender_curve_datablock.dimensions = '3D'
			spline = blender_curve_datablock.splines.new('BEZIER')
			spline.use_cyclic_u = True
			spline.bezier_points.add(len(current_curve.bezier_points) - 1)  # -1 parce que il y a déjà un point par défaut
			for nb, current_bezier_point in enumerate(current_curve.bezier_points):
				current_blender_bezier_point = spline.bezier_points[nb]
				current_blender_bezier_point.radius = self.curves_points_radius
				current_blender_bezier_point.co = current_bezier_point.pos
				current_blender_bezier_point.handle_left = current_bezier_point.left_handle_pos
				current_blender_bezier_point.handle_left_type = current_bezier_point.left_handle_type
				current_blender_bezier_point.handle_right = current_bezier_point.right_handle_pos
				current_blender_bezier_point.handle_right_type = current_bezier_point.right_handle_type
			blender_object = bpy.data.objects.new(self.objects_name_prefix, blender_curve_datablock)
			blender_object.scale = (self.default_objects_scale, self.default_objects_scale, self.default_objects_scale)
			bpy.context.collection.objects.link(blender_object)
			# il semble qu'il faille définir la résolution après avoir ajouté tous les points, sinon elle n'est pas prise en compte
			blender_curve_datablock.resolution_u = self.curves_resolution_u
