extends Node2D

signal element_selected(index: int)
signal element_property_changed(index: int, position: Vector2, rotation: float, scale: Vector2, skew: float)
signal element_action(index: int, action: String, data: Dictionary)
signal asset_drop(asset_name: String, position: Vector2)

@onready var camera = $Camera2D
@onready var root: Sprite2D = $Root
@onready var canvas_layer: CanvasLayer = $CanvasLayer
@onready var free_transform: Sprite2D = $FreeTransform
@onready var xaxis: Line2D = $Xaxis
@onready var yaxis: Line2D = $Yaxis

@onready var canvas_container: Control = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer
@onready var corner: Panel = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer/Corner1
@onready var h_ruler: Panel = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer/HRuler
@onready var v_ruler: Panel = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/VRuler

@onready var tool_bar_panel: Panel = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel
@onready var button_0: Button = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/Panel/HBoxContainer/Button0
@onready var button_1: Button = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/Panel/HBoxContainer/Button1
@onready var button_2: Button = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/Panel/HBoxContainer/Button2
@onready var button_3: Button = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/Panel/HBoxContainer/Button3
@onready var button_4: Button = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/Panel/HBoxContainer/Button4
@onready var asset_listview: Tree = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer2/CanvasContainer/ToolBarPanel/VBoxContainer/AssetListview

@onready var v_scroll = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer2/VScrollBar
@onready var h_scroll = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer3/HScrollBar
@onready var zoom_line_edit: LineEdit = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer3/PanelContainer/HBoxContainer/ZoomLineEdit
@onready var mouse_position_label: LineEdit = $CanvasLayer/CanvasRoot/HBoxContainer/VBoxContainer/HBoxContainer3/PanelContainer/HBoxContainer/MousePositionLabel

var texture_up: Texture2D = preload("res://Resources/UI/Up.png")
var texture_down: Texture2D = preload("res://Resources/UI/Down.png")
var texture_delete: Texture2D = preload("res://Resources/UI/Trash.png")

# 相机控制参数
var min_zoom: int = 5
var max_zoom: int = 1600
var current_zoom: int = 25
var allow_viewport: Rect2

# 拖动相关变量
var is_ready_dragging = false
var is_dragging = false
var camera_start_position = Vector2.ZERO
var mouse_start_screen_position

# element
var sprites = {}
var sprites_names = []
var current_element: SpriteElement

#
var bg_buttons:Array[Button] = []
var editable = false
var mouse_in_canvas = false

# use in all canvas
static var bg_color; 
static var current_bg_button_index = -1;

func _ready():
	current_element = null
	init_gui()
	
	RenderingServer.set_default_clear_color(Color(0.098, 0.098, 0.098))
	set_background(Vector2(2048, 2048), 0)
	zoom_camera(current_zoom)

func _is_visible_in_tree():
	var parent_visible = true
	var p = get_parent()
	while p != null:
		if p is Control and p.visible == false:
			parent_visible = false
			break
		p = p.get_parent()
	return parent_visible

func _input(event):
	if not _is_visible_in_tree():
		return
		
	if event is InputEventMouseButton:
		# 滚轮放大
		if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
			_zoom_camera_by_mouse_wheel(true)
			
		# 滚轮缩小
		elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
			_zoom_camera_by_mouse_wheel(false)
		
		# 双击确认变换
		elif event.button_index == MOUSE_BUTTON_LEFT and event.double_click:
			if asset_listview.get_column_at_position(asset_listview.get_local_mouse_position()) == -1:
				cancle_free_transform()	
			else:
				get_viewport().set_input_as_handled()
		
		# 右键拖拽画布
		elif event.button_index == MOUSE_BUTTON_RIGHT:
			if event.pressed:
				# 开始拖动
				is_dragging = true
				camera_start_position = camera.position
				mouse_start_screen_position = get_viewport().get_mouse_position()
				get_viewport().set_input_as_handled()
			else:
				# 结束拖动
				is_dragging = false
		
		# 空格+左键拖拽画布
		elif event.button_index == MOUSE_BUTTON_LEFT and Input.is_key_pressed(KEY_SPACE):
			# 检查空格键是否同时被按下
			if event.pressed:
				# 开始拖动
				is_dragging = true
				camera_start_position = camera.position
				mouse_start_screen_position = get_viewport().get_mouse_position()
				get_viewport().set_input_as_handled()
			else:
				# 结束拖动
				is_dragging = false
		
	# 鼠标拖动
	elif event is InputEventMouseMotion:
		var pos = get_global_mouse_position()
		mouse_position_label.text = "%d, %d" %[int(pos.x), int(pos.y)]
		
		if is_dragging:
			var current_screen_mouse = get_viewport().get_mouse_position()
			var screen_offset = mouse_start_screen_position - current_screen_mouse
			var world_offset = screen_offset / camera.zoom
			var new_position = camera_start_position + world_offset
			new_position.x = clamp(new_position.x, allow_viewport.position.x, allow_viewport.end.x)
			new_position.y = clamp(new_position.y, allow_viewport.position.y, allow_viewport.end.y)
			camera.position = new_position
			_update_scrollbars()
			get_viewport().set_input_as_handled()
			
	elif event is InputEventKey:
		if event.pressed and not event.echo:
			if event.ctrl_pressed and Input.is_key_pressed(KEY_KP_0):
				zoom_fit()
				
			elif event.ctrl_pressed and Input.is_key_pressed(KEY_KP_1):
				zoom_100()
				get_viewport().set_input_as_handled()
				
			elif Input.is_key_pressed(KEY_ENTER) or Input.is_key_pressed(KEY_KP_ENTER):
				cancle_free_transform()	
		
func _process(_delta: float) -> void:
	if not _is_visible_in_tree():
		return
		
	_update_ruler()
	var need_update_cursor = false
	if free_transform.is_dragging == true:
		need_update_cursor = true
	elif mouse_in_canvas:
		var subviewport_container = get_parent().get_parent()
		if subviewport_container is SubViewportContainer:
			var pos = subviewport_container.get_local_mouse_position()
			var rect = tool_bar_panel.get_rect()
			rect = Rect2(corner.size.x + rect.position.x, corner.size.y, rect.size.x, rect.size.y)
			if rect.has_point(pos):
				need_update_cursor = false
			else:
				rect = subviewport_container.get_rect()
				rect = Rect2(corner.size.x, corner.size.y, rect.size.x - corner.size.x * 2, rect.size.y - corner.size.y * 2)
				if rect.has_point(pos):
					need_update_cursor = true
	
	free_transform.update_free_transform(need_update_cursor)
	
func init_gui():
	canvas_container.set_drag_forwarding(Callable(), _on_canvas_can_drop_data, _on_canvas_drop_data)
	free_transform.value_change.connect(_on_free_transform_value_change)
	
	# asset_listview
	asset_listview.columns = 5
	asset_listview.hide_root = true
	asset_listview.set_column_title(0, "ID")
	asset_listview.set_column_title(1, "Name")
	asset_listview.set_column_custom_minimum_width(0, 40)
	asset_listview.set_column_custom_minimum_width(2, 16)
	asset_listview.set_column_custom_minimum_width(3, 16)
	asset_listview.set_column_custom_minimum_width(4, 16)
	asset_listview.set_column_expand(0, false)
	asset_listview.set_column_expand(1, true)
	asset_listview.set_column_expand(2, false)
	asset_listview.set_column_expand(3, false)
	asset_listview.set_column_expand(4, false)
	asset_listview.item_selected.connect(_on_asset_listview_item_selected)
	asset_listview.nothing_selected.connect(_on_asset_listview_nothing_selected)
	
	# bg button
	bg_buttons.append(button_0)
	bg_buttons.append(button_1)
	bg_buttons.append(button_2)
	bg_buttons.append(button_3)
	bg_buttons.append(button_4)
	var button_group = ButtonGroup.new()
	for i in range(bg_buttons.size()):
		var btn = bg_buttons[i]
		btn.button_group = button_group
		btn.pressed.connect(_on_bg_button_pressed.bind(i))
	button_0.button_pressed = true
	
	# 连接滚动条信号
	v_scroll.value_changed.connect(_on_v_scroll_value_changed)
	h_scroll.value_changed.connect(_on_h_scroll_value_changed)
	zoom_line_edit.text_submitted.connect(_on_zoom_line_edit_text_submitted)

func _on_bg_button_pressed(index):
	current_bg_button_index = index
	bg_buttons[index].button_pressed = true
	set_background(root.texture.get_size(), index)

func _on_v_scroll_value_changed(value):
	camera.position.y = value
	_update_scrollbars()

func _on_h_scroll_value_changed(value):
	camera.position.x = value
	_update_scrollbars()

func _on_canvas_can_drop_data(at_position, data):
	return data is Dictionary and data.get("type") == "asset_panel_drag"
	
func _on_canvas_drop_data(at_position, data):
	asset_drop.emit(data["data"]["asset_name"], get_global_mouse_position())

func _on_asset_listview_item_selected():
	if asset_listview.get_selected() == null:
		current_element = null
		free_transform.set_target(null, -1)
		return
		
	var selected_item = asset_listview.get_selected()
	var index = selected_item.get_index()
	var column = asset_listview.get_column_at_position(asset_listview.get_local_mouse_position())
	match column:
		2: element_action.emit(index, "UP", {})
		3: element_action.emit(index, "DOWN", {})
		4: element_action.emit(index, "REMOVE", {})
		_: 
			current_element = sprites[int(selected_item.get_text(0))]
			if selected_item.get_text(1) != "__blank__":
				free_transform.set_target(current_element, index, editable)
			else:
				free_transform.set_target(current_element, index, false)
			element_selected.emit(index)
	
func _on_asset_listview_nothing_selected():
	cancle_free_transform()
	
func _on_zoom_line_edit_text_submitted(new_text: String):
	var new_zoom = new_text.replace(" ", "").replace("%", "")
	if new_zoom.is_valid_int():
		zoom_camera(int(new_zoom))
	elif new_zoom.is_valid_float():
		zoom_camera(int(float(new_zoom)))
	else:
		zoom_camera(current_zoom)
	zoom_line_edit.release_focus()
	
func _on_free_transform_value_change(_index: int, _position: Vector2, _rotation: float, _scale: Vector2, _skew: float):
	var deg = fposmod(rad_to_deg(_rotation), 360)
	element_property_changed.emit(_index, _position, deg, _scale, _skew)

func _zoom_camera_by_mouse_wheel(zoom_up: bool):
	var zoom_step = 0;
	if current_zoom < 100: zoom_step = 5
	elif current_zoom < 300: zoom_step = 20
	elif current_zoom < 500: zoom_step = 25
	elif current_zoom < 800: zoom_step = 50
	elif current_zoom <= 1600: zoom_step = 100
		
	if zoom_up: zoom_camera(current_zoom + zoom_step)
	else: zoom_camera(current_zoom - zoom_step)

func _update_scrollbars():
	var bar_size = 0.4 * (allow_viewport.end.x - allow_viewport.position.x)
	h_scroll.min_value = allow_viewport.position.x
	h_scroll.max_value = allow_viewport.end.x + bar_size
	h_scroll.page = bar_size
	h_scroll.set_value_no_signal(camera.position.x)
	
	bar_size = 0.4 * (allow_viewport.end.y - allow_viewport.position.y)
	v_scroll.min_value = allow_viewport.position.y
	v_scroll.max_value = allow_viewport.end.y + bar_size
	v_scroll.page = bar_size
	v_scroll.set_value_no_signal(camera.position.y)
	_update_ruler()
	
func _update_ruler():
	var viewport_size = get_viewport().get_visible_rect().size
	
	var corner_cover_size = corner.size / camera.zoom
	var visible_rect = viewport_size / camera.zoom
	
	var viewport_rect = Rect2(camera.position - visible_rect / 2 + corner_cover_size, visible_rect - 2.0 * corner_cover_size)
	var ruler_range = Vector4(viewport_rect.position.x, viewport_rect.position.y, viewport_rect.end.x, viewport_rect.end.y)
	
	var root_rect = root.get_rect()
	var highlight_range = Vector4(root_rect.position.x, root_rect.position.y, root_rect.end.x, root_rect.end.y)
	
	var mousr_pos = get_local_mouse_position()
	
	h_ruler.material.set_shader_parameter("ruler_size", h_ruler.size)
	h_ruler.material.set_shader_parameter("ruler_range", ruler_range)
	h_ruler.material.set_shader_parameter("highlight_range", highlight_range)
	h_ruler.material.set_shader_parameter("mouse_pos", mousr_pos)
	
	v_ruler.material.set_shader_parameter("ruler_size", v_ruler.size)
	v_ruler.material.set_shader_parameter("ruler_range", ruler_range)
	v_ruler.material.set_shader_parameter("highlight_range", highlight_range)
	v_ruler.material.set_shader_parameter("mouse_pos", mousr_pos)

func zoom_camera(new_zoom: int):
	current_zoom = clamp(new_zoom, min_zoom, max_zoom)
	zoom_line_edit.text = "%d%%" % (current_zoom)
	var zoom = current_zoom / 100.0
	camera.zoom = Vector2(zoom, zoom)
	free_transform.zoom = zoom
		
	var visible_size = get_viewport().get_visible_rect().size / camera.zoom
	var sprite_size = root.texture.get_size()
	var corner_cover_size = corner.size / camera.zoom
	
	xaxis.width = 2.0 / zoom
	yaxis.width = 2.0 / zoom
	
	allow_viewport.position.x = min(-sprite_size.x, sprite_size.x - visible_size.x) / 2 + corner_cover_size.x
	allow_viewport.end.x = max(sprite_size.x, -sprite_size.x + visible_size.x) / 2  - corner_cover_size.x
	allow_viewport.position.y = min(-sprite_size.y, sprite_size.y - visible_size.y) / 2  + corner_cover_size.y
	allow_viewport.end.y = max(sprite_size.y, -sprite_size.y + visible_size.y) / 2  - corner_cover_size.y
	_update_scrollbars()
	set_background(root.texture.get_size())
	
func zoom_fit():
	var visible_size = get_viewport().get_visible_rect().size
	var sprite_size = root.texture.get_size()
	var zoom = 0.8 * min(visible_size.x, visible_size.y) / max(sprite_size.x, sprite_size.y)
	print(visible_size, sprite_size, zoom)
	zoom_camera(zoom * 100)
	camera.position = Vector2.ZERO
	
func zoom_100():
	zoom_camera(100)
	camera.position = Vector2.ZERO
	
func clear_all_sprites() -> void:
	sprites.clear()
	sprites_names.clear()
	for child in root.get_children():
		if child is Sprite2D:
			child.queue_free()
	
	asset_listview.clear()
	asset_listview.create_item() # root
	free_transform.set_target(null, -1)
	current_element = null

func load_elements(elements_data, selected_index = -1):
	clear_all_sprites()
	
	var selected_item = null
	asset_listview.set_block_signals(true)
	for i in range(elements_data.size()):
		var element = elements_data[i]
		var sprite = SpriteElement.new()
		var asset_id = int(element["AssetId"])
		sprite.texture = AvatarDollDataMgr.get_asset(asset_id)
		var asset_name = AvatarDollDataMgr.get_asset_name(asset_id)
		root.add_child(sprite)
		
		sprite.position = Vector2(element["PositionX"], element["PositionY"])
		sprite.rotation = deg_to_rad(element["Rotation"])
		sprite.scale = Vector2(element["ScaleX"], element["ScaleY"])
		sprite.set_shader_param({})
		
		var item = asset_listview.create_item()
		item.set_cell_mode(2, TreeItem.CELL_MODE_ICON)
		item.set_cell_mode(3, TreeItem.CELL_MODE_ICON)
		item.set_cell_mode(4, TreeItem.CELL_MODE_ICON)
		item.set_text(0, str(i))
		item.set_text(1, asset_name)
		if editable:
			item.set_icon(2, texture_up)
			item.set_icon(3, texture_down)
			item.set_icon(4, texture_delete)
		
		if i == selected_index:
			selected_item = item
		
		sprites[i] = sprite
		sprites_names.append(asset_name)
		
	if selected_item:
		asset_listview.set_selected(selected_item, 0)
		current_element = sprites[int(selected_item.get_text(0))]
		if selected_item.get_text(1) != "__blank__":
			free_transform.set_target(current_element, selected_index, editable)
		else:
			free_transform.set_target(current_element, selected_index, false)
		
	asset_listview.set_block_signals(false)
	
func update_element(index, element):
	var sprite = sprites[index]
	sprite.position = Vector2(element["PositionX"], element["PositionY"])
	sprite.rotation = deg_to_rad(element["Rotation"])
	sprite.scale = Vector2(element["ScaleX"], element["ScaleY"])
	
	free_transform.position = sprite.position
	free_transform.rotation = sprite.rotation
	free_transform.scale = sprite.scale

func load_icon(icon_asset_id):
	clear_all_sprites()
	var asset_id = int(icon_asset_id)
	var asset_name = AvatarDollDataMgr.get_asset_name(asset_id)
	
	var item = asset_listview.create_item()
	item.set_cell_mode(2, TreeItem.CELL_MODE_ICON)
	item.set_cell_mode(3, TreeItem.CELL_MODE_ICON)
	item.set_cell_mode(4, TreeItem.CELL_MODE_ICON)
	item.set_text(0, "0")
	item.set_text(1, asset_name)
	if editable:
		item.set_icon(2, texture_up)
		item.set_icon(3, texture_down)
		item.set_icon(4, texture_delete)
	
	var sprite = SpriteElement.new()
	sprite.texture = AvatarDollDataMgr.get_asset(asset_id)
	sprites[0] = sprite
	root.add_child(sprite)

func update_palette(element_index, palettes):
	if element_index >= 0 and element_index < sprites.size():
		var sprite = sprites[element_index]
		sprite.set_shader_param(palettes)

func sync_background():
	if current_bg_button_index != -1:
		bg_buttons[current_bg_button_index].button_pressed = true

func set_background(size: Vector2, background_type: int = -1):
	if root.texture == null or root.texture.get_size() != size:
		var image = Image.create(int(size.x), int(size.y), false, Image.FORMAT_RGBA8)
		image.fill(Color(0, 0, 0, 0))
		root.texture = ImageTexture.create_from_image(image)
	
	if background_type == 0: bg_color = Color.TRANSPARENT
	elif background_type == 1: bg_color = Color.WHITE
	elif background_type == 2: bg_color = Color(0.5, 0.5, 0.5, 1.0)
	elif background_type == 3: bg_color = Color(0.25, 0.25, 0.25, 1.0)
	elif background_type == 4: bg_color = Color.BLACK
	
	root.material.set_shader_parameter("bg_color", bg_color)
	root.material.set_shader_parameter("zoom", current_zoom / 100.0)

func set_editable(_editable: bool):
	editable = _editable
	if editable == false:
		free_transform.set_target(null, -1)

func set_selected(index):
	asset_listview.deselect_all()
	if index >= 0:
		current_element = sprites[index]
		asset_listview.set_selected(asset_listview.get_root().get_child(index), 0)

func cancle_free_transform():
	asset_listview.deselect_all()
	free_transform.set_target(null, -1)
	current_element = null
	element_selected.emit(-1)

func update_mouse_in_canvas(b: bool):
	mouse_in_canvas = b
