extends Panel

onready var _script_list = $ScriptsList
onready var _nav_container = $VBox/BottomPanel/VBox/HBox/Navigation
onready var _nav = {
	container = _nav_container,
	prev = _nav_container.get_node("VBox/HBox/Previous"),
	next = _nav_container.get_node("VBox/HBox/Next"),
	run = _nav_container.get_node("VBox/HBox/Run"),
	current_script = _nav_container.get_node("VBox/CurrentScript"),
	run_single = _nav_container.get_node("VBox/HBox/RunSingleScript")
}

onready var _progress_container = $VBox/BottomPanel/VBox/HBox/Progress
onready var _progress = {
	script = _progress_container.get_node("ScriptProgress"),
	script_xy = _progress_container.get_node("ScriptProgress/xy"),
	test = _progress_container.get_node("TestProgress"),
	test_xy = _progress_container.get_node("TestProgress/xy")
}
onready var _summary = {
	control = $VBox/TitleBar/HBox/Summary,
	failing = $VBox/TitleBar/HBox/Summary/Failing,
	passing = $VBox/TitleBar/HBox/Summary/Passing,
	asserts = $VBox/TitleBar/HBox/Summary/AssertCount,
	fail_count = 0,
	pass_count = 0
}

onready var _extras = $ExtraOptions
onready var _ignore_pauses = $ExtraOptions/IgnorePause
onready var _continue_button = $VBox/BottomPanel/VBox/HBox/Continue/Continue
onready var _text_box = $VBox/TextDisplay/RichTextLabel
onready var _text_box_container = $VBox/TextDisplay
onready var _log_level_slider = $VBox/BottomPanel/VBox/HBox2/LogLevelSlider
onready var _resize_handle = $ResizeHandle
onready var _current_script = $VBox/BottomPanel/VBox/HBox2/CurrentScriptLabel
onready var _title_replacement = $VBox/TitleBar/HBox/TitleReplacement

onready var _titlebar = {
	bar = $VBox/TitleBar, time = $VBox/TitleBar/HBox/Time, label = $VBox/TitleBar/HBox/Title
}

onready var _user_files = $UserFileViewer

var _mouse = {down = false, in_title = false, down_pos = null, in_handle = false}

var _is_running = false
var _start_time = 0.0
var _time = 0.0

const DEFAULT_TITLE = "GUT"
var _pre_maximize_rect = null
var _font_size = 20
var _compact_mode = false

var min_sizes = {
	compact = Vector2(330, 100),
	full = Vector2(740, 300),
}

signal end_pause
signal ignore_pause
signal log_level_changed
signal run_script
signal run_single_script


func _ready():
	if Engine.editor_hint:
		return

	_current_script.text = ""
	_pre_maximize_rect = get_rect()
	_hide_scripts()
	_update_controls()
	_nav.current_script.set_text("No scripts available")
	set_title()
	clear_summary()
	_titlebar.time.set_text("t: 0.0")

	_extras.visible = false
	update()

	set_font_size(_font_size)
	set_font("CourierPrime")

	_user_files.set_position(Vector2(10, 30))


func elapsed_time_as_str():
	return str("%.1f" % (_time / 1000.0), "s")


func _process(_delta):
	if _is_running:
		_time = OS.get_ticks_msec() - _start_time
		_titlebar.time.set_text(str("t: ", elapsed_time_as_str()))


func _draw():  # needs get_size()
	# Draw the lines in the corner to show where you can
	# drag to resize the dialog
	var grab_margin = 3
	var line_space = 3
	var grab_line_color = Color(.4, .4, .4)
	if _resize_handle.visible:
		for i in range(1, 10):
			var x = rect_size - Vector2(i * line_space, grab_margin)
			var y = rect_size - Vector2(grab_margin, i * line_space)
			draw_line(x, y, grab_line_color, 1, true)


func _on_Maximize_draw():
	# draw the maximize square thing.
	var btn = $VBox/TitleBar/HBox/Maximize
	btn.set_text("")
	var w = btn.get_size().x
	var h = btn.get_size().y
	btn.draw_rect(Rect2(0, 2, w, h - 2), Color(0, 0, 0, 1))
	btn.draw_rect(Rect2(2, 6, w - 4, h - 8), Color(1, 1, 1, 1))


func _on_ShowExtras_draw():
	var btn = $VBox/BottomPanel/VBox/HBox/Continue/ShowExtras
	btn.set_text("")
	var start_x = 20
	var start_y = 15
	var pad = 5
	var color = Color(.1, .1, .1, 1)
	var width = 2
	for i in range(3):
		var y = start_y + pad * i
		btn.draw_line(
			Vector2(start_x, y), Vector2(btn.get_size().x - start_x, y), color, width, true
		)


# ####################
# GUI Events
# ####################
func _on_Run_pressed():
	_run_mode()
	emit_signal("run_script", get_selected_index())


func _on_CurrentScript_pressed():
	_toggle_scripts()


func _on_Previous_pressed():
	_select_script(get_selected_index() - 1)


func _on_Next_pressed():
	_select_script(get_selected_index() + 1)


func _on_LogLevelSlider_value_changed(_value):
	emit_signal("log_level_changed", _log_level_slider.value)


func _on_Continue_pressed():
	_continue_button.disabled = true
	emit_signal("end_pause")


func _on_IgnorePause_pressed():
	var checked = _ignore_pauses.is_pressed()
	emit_signal("ignore_pause", checked)
	if checked:
		emit_signal("end_pause")
		_continue_button.disabled = true


func _on_RunSingleScript_pressed():
	_run_mode()
	emit_signal("run_single_script", get_selected_index())


func _on_ScriptsList_item_selected(index):
	var tmr = $ScriptsList/DoubleClickTimer
	if !tmr.is_stopped():
		_run_mode()
		emit_signal("run_single_script", get_selected_index())
		tmr.stop()
	else:
		tmr.start()

	_select_script(index)


func _on_TitleBar_mouse_entered():
	_mouse.in_title = true


func _on_TitleBar_mouse_exited():
	_mouse.in_title = false


func _input(event):
	if event is InputEventMouseButton:
		if event.button_index == 1:
			_mouse.down = event.pressed
			if _mouse.down:
				_mouse.down_pos = event.position

	if _mouse.in_title:
		if event is InputEventMouseMotion and _mouse.down:
			set_position(get_position() + (event.position - _mouse.down_pos))
			_mouse.down_pos = event.position
			_pre_maximize_rect = get_rect()

	if _mouse.in_handle:
		if event is InputEventMouseMotion and _mouse.down:
			var new_size = rect_size + event.position - _mouse.down_pos
			var new_mouse_down_pos = event.position
			rect_size = new_size
			_mouse.down_pos = new_mouse_down_pos
			_pre_maximize_rect = get_rect()


func _on_ResizeHandle_mouse_entered():
	_mouse.in_handle = true


func _on_ResizeHandle_mouse_exited():
	_mouse.in_handle = false


func _on_RichTextLabel_gui_input(ev):
	pass
	# leaving this b/c it is wired up and might have to send
	# more signals through


func _on_Copy_pressed():
	OS.clipboard = _text_box.text


func _on_ShowExtras_toggled(button_pressed):
	_extras.visible = button_pressed


func _on_Maximize_pressed():
	if get_rect() == _pre_maximize_rect:
		compact_mode(false)
		maximize()
	else:
		compact_mode(false)
		rect_size = _pre_maximize_rect.size
		rect_position = _pre_maximize_rect.position


func _on_Minimize_pressed():
	compact_mode(!_compact_mode)


func _on_Minimize_draw():
	# draw the maximize square thing.
	var btn = $VBox/TitleBar/HBox/Minimize
	btn.set_text("")
	var w = btn.get_size().x
	var h = btn.get_size().y
	btn.draw_rect(Rect2(0, h - 3, w, 3), Color(0, 0, 0, 1))


func _on_UserFiles_pressed():
	_user_files.show_open()


# ####################
# Private
# ####################
func _run_mode(is_running = true):
	if is_running:
		_start_time = OS.get_ticks_msec()
		_time = 0.0
		clear_summary()
	_is_running = is_running

	_hide_scripts()
	_nav.prev.disabled = is_running
	_nav.next.disabled = is_running
	_nav.run.disabled = is_running
	_nav.current_script.disabled = is_running
	_nav.run_single.disabled = is_running


func _select_script(index):
	var text = _script_list.get_item_text(index)
	var max_len = 50
	if text.length() > max_len:
		text = "..." + text.right(text.length() - (max_len - 5))
	_nav.current_script.set_text(text)
	_script_list.select(index)
	_update_controls()


func _toggle_scripts():
	if _script_list.visible:
		_hide_scripts()
	else:
		_show_scripts()


func _show_scripts():
	_script_list.show()


func _hide_scripts():
	_script_list.hide()


func _update_controls():
	var is_empty = _script_list.get_selected_items().size() == 0
	if is_empty:
		_nav.next.disabled = true
		_nav.prev.disabled = true
	else:
		var index = get_selected_index()
		_nav.prev.disabled = index <= 0
		_nav.next.disabled = index >= _script_list.get_item_count() - 1

	_nav.run.disabled = is_empty
	_nav.current_script.disabled = is_empty
	_nav.run_single.disabled = is_empty


func _update_summary():
	if !_summary:
		return

	var total = _summary.fail_count + _summary.pass_count
	_summary.control.visible = !total == 0
	_summary.asserts.text = str("Failures ", _summary.fail_count, "/", total)


# ####################
# Public
# ####################
func run_mode(is_running = true):
	_run_mode(is_running)


func set_scripts(scripts):
	_script_list.clear()
	for i in range(scripts.size()):
		_script_list.add_item(scripts[i])
	_select_script(0)
	_update_controls()


func select_script(index):
	_select_script(index)


func get_selected_index():
	return _script_list.get_selected_items()[0]


func get_log_level():
	return _log_level_slider.value


func set_log_level(value):
	var new_value = value
	if new_value == null:
		new_value = 0
	# !! For some reason, _log_level_slider was null, but this wasn't, so
	# here's another hardcoded node path.
	$VBox/BottomPanel/VBox/HBox2/LogLevelSlider.value = new_value


func set_ignore_pause(should):
	_ignore_pauses.pressed = should


func get_ignore_pause():
	return _ignore_pauses.pressed


func get_text_box():
	# due to some timing issue, this cannot return _text_box but can return
	# this.
	return $VBox/TextDisplay/RichTextLabel


func end_run():
	_run_mode(false)
	_update_controls()


func set_progress_script_max(value):
	var max_val = max(value, 1)
	_progress.script.set_max(max_val)
	_progress.script_xy.set_text(str("0/", max_val))


func set_progress_script_value(value):
	_progress.script.set_value(value)
	var txt = str(value, "/", _progress.test.get_max())
	_progress.script_xy.set_text(txt)


func set_progress_test_max(value):
	var max_val = max(value, 1)
	_progress.test.set_max(max_val)
	_progress.test_xy.set_text(str("0/", max_val))


func set_progress_test_value(value):
	_progress.test.set_value(value)
	var txt = str(value, "/", _progress.test.get_max())
	_progress.test_xy.set_text(txt)


func clear_progress():
	_progress.test.set_value(0)
	_progress.script.set_value(0)


func pause():
	_continue_button.disabled = false


func set_title(title = null):
	if title == null:
		_titlebar.label.set_text(DEFAULT_TITLE)
	else:
		_titlebar.label.set_text(title)


func add_passing(amount = 1):
	if !_summary:
		return
	_summary.pass_count += amount
	_update_summary()


func add_failing(amount = 1):
	if !_summary:
		return
	_summary.fail_count += amount
	_update_summary()


func clear_summary():
	_summary.fail_count = 0
	_summary.pass_count = 0
	_update_summary()


func maximize():
	if is_inside_tree():
		var vp_size_offset = get_tree().root.get_viewport().get_visible_rect().size
		rect_size = vp_size_offset / get_scale()
		set_position(Vector2(0, 0))


func clear_text():
	_text_box.bbcode_text = ""


func scroll_to_bottom():
	pass
	#_text_box.cursor_set_line(_gui.get_text_box().get_line_count())


func _set_font_size_for_rtl(rtl, new_size):
	if rtl.get("custom_fonts/normal_font") != null:
		rtl.get("custom_fonts/bold_italics_font").size = new_size
		rtl.get("custom_fonts/bold_font").size = new_size
		rtl.get("custom_fonts/italics_font").size = new_size
		rtl.get("custom_fonts/normal_font").size = new_size


func _set_fonts_for_rtl(rtl, base_font_name):
	pass


func set_font_size(new_size):
	_font_size = new_size
	_set_font_size_for_rtl(_text_box, new_size)
	_set_font_size_for_rtl(_user_files.get_rich_text_label(), new_size)


func _set_font(rtl, font_name, custom_name):
	if font_name == null:
		rtl.set("custom_fonts/" + custom_name, null)
	else:
		var dyn_font = preload("res://addons/godot_xterm/themes/fonts/regular.tres").duplicate()
		rtl.set("custom_fonts/" + custom_name, dyn_font)


func _set_all_fonts_in_ftl(ftl, base_name):
	if base_name == "Default":
		_set_font(ftl, null, "normal_font")
		_set_font(ftl, null, "bold_font")
		_set_font(ftl, null, "italics_font")
		_set_font(ftl, null, "bold_italics_font")
	else:
		_set_font(ftl, base_name + "-Regular", "normal_font")
		_set_font(ftl, base_name + "-Bold", "bold_font")
		_set_font(ftl, base_name + "-Italic", "italics_font")
		_set_font(ftl, base_name + "-BoldItalic", "bold_italics_font")
	set_font_size(_font_size)


func set_font(base_name):
	_set_all_fonts_in_ftl(_text_box, base_name)
	_set_all_fonts_in_ftl(_user_files.get_rich_text_label(), base_name)


func set_default_font_color(color):
	_text_box.set("custom_colors/default_color", color)


func set_background_color(color):
	_text_box_container.color = color


func get_waiting_label():
	return $VBox/TextDisplay/WaitingLabel


func compact_mode(should):
	if _compact_mode == should:
		return

	_compact_mode = should
	_text_box_container.visible = !should
	_nav.container.visible = !should
	_log_level_slider.visible = !should
	$VBox/BottomPanel/VBox/HBox/Continue/ShowExtras.visible = !should
	_titlebar.label.visible = !should
	_resize_handle.visible = !should
	_current_script.visible = !should
	_title_replacement.visible = should

	if should:
		rect_min_size = min_sizes.compact
		rect_size = rect_min_size
	else:
		rect_min_size = min_sizes.full
		rect_size = min_sizes.full

	goto_bottom_right_corner()


func set_script_path(text):
	_current_script.text = text


func goto_bottom_right_corner():
	rect_position = get_tree().root.get_viewport().get_visible_rect().size - rect_size