extends VBoxContainer

const COLOR_NORMAL = Color("3c3c46")
const COLOR_HOVERED = Color("50505a")
const COLOR_PRESSED = Color("32323c")

export(PackedScene) var ChannelWindow_scene: PackedScene = load("res://scenes/custom_nodes/channel_window/channel_window.tscn")
export(PackedScene) var ChannelCard_scene: PackedScene = load("res://scenes/custom_nodes/channel_card/channel_card.tscn")
export(PackedScene) var ButtonContainer_scene: PackedScene = load("res://scenes/custom_nodes/button_container/button_container.tscn")

var _initialized: bool = false
var _following_changed: bool = false
# Right margin offset that should be applied so that the scroll bar hugs the
# edge of the window
var _channel_list_scroll_container_offset: int = 0
var _async_helper := AsyncHelper.new(self)
onready var _search_bar: LineEdit = find_node("SearchBar")
onready var _channel_list_node: VBoxContainer = find_node("ChannelList")
onready var _channel_list_margin_container: MarginContainer = \
		find_node("ChannelListMarginContainer")
onready var _channel_list_scroll_container: ScrollContainer = \
		find_node("ChannelListScrollContainer")


func _ready() -> void:
	connect("visibility_changed", self, "_on_visibility_changed")
	_search_bar.connect("text_entered", self, "_on_SearchBar_text_entered")
	_channel_list_scroll_container.get_v_scrollbar().connect(
			"visibility_changed", self,
			"_on_ChannelListScrollContainer_v_scrollbar_visibility_changed")
	LBRYActions.connect(
			"followed_channel", self, "_on_LBRYActions_channel_followed")
	LBRYActions.connect(
			"unfollowed_channel", self, "_on_LBRYActions_channel_unfollowed")
	
	# Wait until the lbrynet daemon is ready
	var wait_timer := Timer.new()
	wait_timer.set_one_shot(true)
	add_child(wait_timer)
	var tracker: Dictionary = _async_helper.connect_wrapped(
			SDK.is_ready(), "completed")
	yield(_async_helper.wait_until_finished([tracker]), "completed")
	var sdk_ready: bool = tracker["result"]
	while sdk_ready == false:
		# Don't use `get_tree().create_timer() because the node may have been
		# removed from the tree
		wait_timer.start(1)
		yield(wait_timer, "timeout")
		
		tracker = _async_helper.connect_wrapped(SDK.is_ready(), "completed")
		yield(_async_helper.wait_until_finished([tracker]), "completed")
		sdk_ready = tracker["result"]
	remove_child(wait_timer)
	wait_timer.queue_free()
	
	_setup()


func _setup() -> void:
	var tracker: Dictionary = _async_helper.connect_wrapped(
			LBRYActions.get_followed_channels(), "completed")
	yield(_async_helper.wait_until_finished([tracker]), "completed")
	var followed_channels: Array = tracker["result"]
	_display_followed_channels(followed_channels)
	_make_scroll_bar_hug_window_edge()


func _display_followed_channels(channel_urls: Array) -> void:
	if len(channel_urls) > 0:
		for channel_url in channel_urls:
			var button_container: ButtonContainer = \
					ButtonContainer_scene.instance()
			var channel_card: ChannelCard = ChannelCard_scene.instance()
			channel_card.init(channel_url)
			
			button_container.connect(
					"pressed", self, "_open_channel_window", [channel_url])
			button_container.set_normal_color(COLOR_NORMAL)
			button_container.set_hovered_color(COLOR_HOVERED)
			button_container.set_pressed_color(COLOR_PRESSED)
			button_container.add_child(channel_card)
			_channel_list_node.add_child(button_container)
	else:
		var label := Label.new()
		label.set_text("Not following any channels")
		_channel_list_node.add_child(label)


func _apply_search(search_text: String) -> void:
	"""
	Hides channels that don't match the search term, unless the search term is
	empty in which case all channels are shown
	"""
	for child in _channel_list_node.get_children():
		if child.get_child_count() > 0 and child.get_child(0) is ChannelCard:
			var channel_card: ChannelCard = child.get_child(0)
			var name_search_term: String = \
					channel_card.get_channel_name().to_lower().trim_prefix("@")
			var title_search_term: String = \
					channel_card.get_channel_title().to_lower()
			
			if search_text == "":
				child.set_visible(true)
				child.set_disabled(false)
			elif (
					not search_text.to_lower() in name_search_term
					and not search_text.to_lower() in title_search_term
			):
				child.set_visible(false)
				child.set_disabled(true)
			else:
				child.set_visible(true)
				child.set_disabled(false)


func _clear() -> void:
	for child in _channel_list_node.get_children():
		child.queue_free()


func _make_scroll_bar_hug_window_edge() -> void:
	var scroll_container_right_edge: float = (
			_channel_list_scroll_container.get_global_position().x
			+ _channel_list_scroll_container.get_size().x
	)
	var diff := int(OS.get_window_size().x - scroll_container_right_edge)
	_channel_list_scroll_container_offset = -diff
	if _channel_list_scroll_container.get_v_scrollbar().is_visible_in_tree():
		_channel_list_margin_container.add_constant_override(
				"margin_right", _channel_list_scroll_container_offset)


func _open_channel_window(channel_url: String) -> void:
	var channel_window: ChannelWindow = ChannelWindow_scene.instance()
	channel_window.init(channel_url)
	get_parent().set_window(channel_window)


func _on_visibility_changed() -> void:
	if is_visible_in_tree() and _following_changed:
		_following_changed = false
		_clear()
		_setup()


func _on_SearchBar_text_entered(new_text: String) -> void:
	_apply_search(new_text)


func _on_ChannelListScrollContainer_v_scrollbar_visibility_changed() -> void:
	""" Ensure the margin is maintained when the scroll bar is not visible """
	var right_margin: int = (
			_channel_list_scroll_container_offset
			if _channel_list_scroll_container \
					.get_v_scrollbar() \
					.is_visible_in_tree()
			else 0
	)
	_channel_list_margin_container.add_constant_override(
			"margin_right", right_margin)


func _on_LBRYActions_channel_followed(_channel_url: String) -> void:
	_following_changed = true


func _on_LBRYActions_channel_unfollowed(_channel_url: String) -> void:
	_following_changed = true
