extends ScrollContainer

signal item_dropped

# Threshold in pixels from the edge for the list to scroll when dragging a 
# control over this node
const DRAG_SCROLL_THRESHOLD = 10

var seperator: HSeparator


func _ready() -> void:
	connect("mouse_exited", self, "_on_mouse_exited")


func get_list_items() -> Array:
	return $VBoxContainer.get_children()


func add_list_item(item: Control) -> void:
	$VBoxContainer.add_child(item)


func move_list_item(item: Control, idx: int) -> void:
	$VBoxContainer.move_child(item, idx)


func can_drop_data(position: Vector2, idx: int) -> bool:
	"""
	Called when a dragged control is hovered over this node. Creates an
	indicator showing where the control will be placed if it is dropped at its
	current position. Scrolls the node if the control is dragged to its edges
	
	Args:
		position: relative position of the dragged control to this node
		idx: the index of the control within `$VBoxContainer`
	"""
	if $"VBoxContainer".get_child(idx) == seperator:
		idx += 1
	
	if (not $"VBoxContainer".get_child(idx).is_connected(
			"drag_ended", self, "_on_item_drag_ended")
	):
		$"VBoxContainer".get_child(idx).connect(
			"drag_ended", self, "_on_item_drag_ended", [], CONNECT_ONESHOT)
	
	# Place an indicator where the control will be placed if dropped
	var drop_idx = _get_drop_idx(position.y)
	if (not seperator) or (not is_instance_valid(seperator)):
		seperator = HSeparator.new()
		$"VBoxContainer".add_child(seperator)
	$"VBoxContainer".move_child(seperator, drop_idx)
	
	# Scroll when dragging a control near the edge of this node
	if position.y >= get_size().y - DRAG_SCROLL_THRESHOLD:
		set_v_scroll(get_v_scroll() + 10)
	elif position.y <= DRAG_SCROLL_THRESHOLD:
		set_v_scroll(get_v_scroll() - 10)
	
	return true


func drop_data(position: Vector2, idx: int) -> void:
	"""
	Called when a control is dropped on this node
	
	Args:
		position: relative position the control was dropped on this node
		idx: the index of the control within `$VBoxContainer`
	"""
	_remove_seperator()
	
	var drop_idx: int = _get_drop_idx(position.y)
	if drop_idx > idx:
		# Must subtract 1 here because when moving a node all its siblings
		# below get moved up a position before the node is replaced
		drop_idx -= 1
	var node: SectionButton = $"VBoxContainer".get_child(idx)
	$"VBoxContainer".move_child(node, drop_idx)
	emit_signal("item_dropped")


func _get_drop_idx(y_position: float) -> int:
	"""
	Args:
		y_position: y-position relative to this node
	
	Returns:
		The index a node should be inserted if dropped at the given position
	"""
	var drop_idx: int = 0
	var offset: float = $"VBoxContainer".get_position().y
	for child in $"VBoxContainer".get_children():
		if child == seperator:
			continue
		var middle: float = \
				offset + child.get_position().y + child.get_size().y / 2
		if y_position < middle:
			break
		drop_idx += 1
	return drop_idx


func _remove_seperator() -> void:
	if is_instance_valid(seperator):
		if seperator.get_parent():
			seperator.get_parent().remove_child(seperator)
		seperator.queue_free()


func _on_item_drag_ended() -> void:
	_remove_seperator()


func _on_mouse_exited() -> void:
	if not Rect2(Vector2(), get_size()).has_point(get_local_mouse_position()):
		_remove_seperator()
