extends Sprite2D

signal value_change(index: int, position: Vector2, rotation: float, scale: Vector2, skew: float)

@onready var target: Sprite2D
@export var zoom = 1.0
var target_index = -1

var is_dragging = false
var cursor_sprite_frames: SpriteFrames
var local_direction = 0
var cursor_direction = 0
var action = ""
var transform_start_local_direction = 0
var transform_start_cursor_direction = 0
var transform_start_global_position: Vector2
var transform_start_position: Vector2
var transform_start_rotation: float
var transform_start_scale: Vector2
var _corners = [
	Vector2(1.0, 0.5), Vector2(1.0, 0.0), Vector2(0.5, 0.0), Vector2(0.0, 0.0),
	Vector2(0.0, 0.5), Vector2(0.0, 1.0), Vector2(0.5, 1.0), Vector2(1.0, 1.0)
]
var editable = true

func _ready() -> void:
	set_target(null, -1)
	cursor_sprite_frames = load("res://Resources/cursor_sprite_frames.tres")

func set_target(_sprite: Sprite2D, _index: int, _editable: bool = false):
	target = _sprite
	target_index = _index
	editable = _editable
	if target == null:
		visible = false
		return
	
	visible = true
	var image = Image.create(target.texture.get_width() + 512, target.texture.get_height() + 512, false, Image.FORMAT_RGBA8)
	texture = ImageTexture.create_from_image(image)
	
	position = target.position
	rotation = target.rotation
	scale = target.scale
	skew = target.skew
	
func grow_rect(rect: Rect2, amount: float, _scale: Vector2) -> Rect2:
	var amount_x = clampf(amount / abs(_scale.x), 1.0, 9999.9) if _scale.x != 0.0 else 1.0
	var amount_y = clampf(amount / abs(_scale.y), 1.0, 9999.9) if _scale.y != 0.0 else 1.0
	return rect.grow_individual(amount_x, amount_y, amount_x, amount_y)

func _process(_delta: float) -> void:
	if target == null:
		return
	update_shader_params()
			
func _input(event):
	if target == null or editable == false:
		return
		
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT:
			if event.pressed and action != "":
				transform_start_global_position = get_global_mouse_position()
				transform_start_local_direction = local_direction
				transform_start_cursor_direction = cursor_direction
				transform_start_position = target.position
				transform_start_rotation = target.rotation
				transform_start_scale = target.scale
				is_dragging = true
				get_viewport().set_input_as_handled()
			else:
				is_dragging = false
	
	elif event is InputEventKey:
		if event.pressed and not event.echo:
			if Input.is_key_pressed(KEY_ENTER) or Input.is_key_pressed(KEY_KP_ENTER):
				is_dragging = false
				set_target(null, -1)
				Input.set_custom_mouse_cursor(null)
				return
			
			var _offset = Vector2.ZERO;
			if Input.is_key_pressed(KEY_LEFT): _offset.x = -1
			if Input.is_key_pressed(KEY_RIGHT): _offset.x = 1
			if Input.is_key_pressed(KEY_UP): _offset.y = -1
			if Input.is_key_pressed(KEY_DOWN): _offset.y = 1
			if _offset == Vector2.ZERO: return
			if event.ctrl_pressed:
				_offset *= 10;
				
			target.position += _offset;
			position = target.position
			value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
			get_viewport().set_input_as_handled()
	
	elif event is InputEventMouseMotion and is_dragging:
		if action == "Move":
			var _offset = get_global_mouse_position() - transform_start_global_position;
			
			if event.ctrl_pressed:
				if abs(_offset.x) < abs(_offset.y): 
					_offset.x = 0
				else: 
					_offset.y = 0
					
			target.position = transform_start_position + _offset;
			position = target.position
			value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
			get_viewport().set_input_as_handled()
			
		elif action == "Rotation":
			var vector_OA = (transform_start_global_position - transform_start_position).normalized()
			var vector_OB = (get_global_mouse_position() - transform_start_position).normalized()
			var angle_rad = -vector_OA.angle_to(vector_OB)
			if angle_rad < 0:  angle_rad += 2 * PI
			
			if event.ctrl_pressed:
				angle_rad = deg_to_rad(round(rad_to_deg(angle_rad) / 15.0) * 15.0)
			
			target.rotation = transform_start_rotation - angle_rad
			rotation = target.rotation
			value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
			get_viewport().set_input_as_handled()
			
		elif action == "Scale":
			# 将起始点和当前点转换到Sprite的局部坐标系
			var start_local = target.to_local(transform_start_global_position)
			var current_local = target.to_local(get_global_mouse_position())

			# 在局部坐标系中计算缩放
			var scale_delta = Vector2.ONE
			var start_vec = start_local - target.offset  # 从中心点到起始点的向量
			var current_vec = current_local - target.offset  # 从中心点到当前点的向量

			# 避免除零错误
			if start_vec.x != 0: scale_delta.x = current_vec.x / start_vec.x
			if start_vec.y != 0: scale_delta.y = current_vec.y / start_vec.y

			# 根据拖拽方向锁定轴
			if transform_start_local_direction == 0 or transform_start_local_direction == 4:  # 左右
				scale_delta.y = 1.0
			elif transform_start_local_direction == 2 or transform_start_local_direction == 6:  # 上下
				scale_delta.x = 1.0

			# 等比缩放
			if event.ctrl_pressed:
				var uniform_scale = (scale_delta.x + scale_delta.y) / 2.0
				scale_delta = Vector2(uniform_scale, uniform_scale)

			# 应用缩放
			target.scale = transform_start_scale * scale_delta
			scale = target.scale
			value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
			get_viewport().set_input_as_handled()


#func _draw() -> void:
	#var _scale = target.scale * zoom
	#var r0 = target.get_rect()
	#var r1 = grow_rect(r0, -15, _scale) # move
	#var r2 = grow_rect(r0, 30, _scale) # scale
	#var r3 = grow_rect(r0, 80, _scale) # rotation
	#draw_rect(r0, Color.WHITE, false)
	#draw_rect(r1, Color.WHITE, false)
	#draw_rect(r2, Color.WHITE, false)
	#draw_rect(r3, Color.WHITE, false)
	#
	#var texture_size = target.texture.get_size()
	#var s = Vector2(1.0, texture_size.y / texture_size.x) if texture_size.x < texture_size.y else Vector2(texture_size.x / texture_size.y, 1.0)
	#var t = Transform2D(0.0, s, 0.0, Vector2.ZERO)
	#var scale_local_mouse_pos = t.affine_inverse() * get_local_mouse_position()
	#
	#var PI16 = PI / 16;
	#for i in range(32):
		#var p = (Vector2.RIGHT * 3000.0).rotated(i * PI16)
		#var point = t * p
		#draw_line(Vector2.ZERO, point, Color.WHITE, 2.0)
	
func update_free_transform(update_cursor: bool=true):
	if update_cursor == false or editable == false or target == null:
		action = ""
		local_direction = -1
		Input.set_custom_mouse_cursor(null)
		return
	
	var local_mouse_position = get_local_mouse_position()
	var PI8 = PI / 8;
	var texture_size = target.texture.get_size()
	var scale_factor = Vector2(1.0, texture_size.y / texture_size.x) if texture_size.x < texture_size.y else Vector2(texture_size.x / texture_size.y, 1.0)
	var scale_local_mouse_pos = local_mouse_position / scale_factor
	var angle = fposmod(PI / 16 - scale_local_mouse_pos.angle(), TAU) 
	
	local_direction = -1
	for i in range(9):
		if (i * 2) * PI8 <= angle and angle <  (1 + i * 2) * PI8:
			local_direction = i % 8
			break
	#print(rad_to_deg(angle), " ", local_direction, " ")
	
	var target_rect = target.get_rect()
	if local_direction != -1:
		var rect = Rect2(-target_rect.size / 2, target_rect.size)
		var trans = target.transform
		trans.origin = Vector2.ZERO
		cursor_direction = find_point_mapping(rect, trans, local_direction)

	if is_dragging == false:
		action = ""
		var _scale = target.scale * zoom
		var local_mouse_pos = local_mouse_position
		
		if grow_rect(target_rect, -15, _scale).has_point(local_mouse_pos):
			action = "Move"
		elif local_direction != -1:
			if grow_rect(target_rect, 30, _scale).has_point(local_mouse_pos):
				action = "Scale"
			elif grow_rect(target_rect, 80, _scale).has_point(local_mouse_pos):
				action = "Rotation"

	if action == "": 
		Input.set_custom_mouse_cursor(null)
	elif action == "Move":
		var tex = cursor_sprite_frames.get_frame_texture(action, 0)
		Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
	elif action == "Rotation":
		var curson_name = "%s_%d" % [action, cursor_direction]
		var tex = cursor_sprite_frames.get_frame_texture(curson_name, 0)
		Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
	elif action == "Scale":
		if is_dragging and transform_start_local_direction in [0, 2, 4, 6]:
			return
				
		var curson_name = "%s_%d" % [action, cursor_direction]
		var tex = cursor_sprite_frames.get_frame_texture(curson_name, 0)
		Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
	else:
		Input.set_custom_mouse_cursor(null)
		action = ""
		
	update_shader_params()
	

func update_shader_params():
	var rect = target.get_rect()
	var points_array = [
		rect.position,                           # 左上角
		rect.position + Vector2(rect.size.x, 0), # 右上角
		rect.position + rect.size,               # 右下角
		rect.position + Vector2(0, rect.size.y)  # 左下角
	]
	for i in range(4):
		points_array[i] = target.transform * points_array[i]
	
	material.set_shader_parameter("points", points_array)
	material.set_shader_parameter("editable", editable)
	

func find_point_mapping(rect: Rect2, trans: Transform2D, direction: int) -> int:
	var points = []
	for corner in _corners:
		points.append(rect.position + rect.size * corner)
	
	var min_index = -1
	var min_angle = TAU
	for i in points.size():
		var angle = abs((trans * points[i]).angle())
		if angle < min_angle:
			min_angle = angle
			min_index = i

	if(trans.get_scale().y < 0): 
		return (min_index - direction + 8) % 8
	else: 
		return (direction - min_index + 8) % 8
	
