extends Node

# Data fields
@onready var state_history_data := %"State History Data" as TextEdit
@onready var input_history_data := %"Input History Data" as TextEdit
@onready var network_tick_data := %"Network Tick Data" as LineEdit
@onready var rollback_tick_data := %"Rollback Tick Data" as LineEdit
@onready var has_input_data := %"Has Input Data" as Label
@onready var input_age_data := %"Input Age Data" as Label
@onready var simset_list := %"Simset List" as ItemList
@onready var skipset_list := %"Skipset List" as ItemList
@onready var transmit_state_data := %"Transmit State Data" as Label

# Tool buttons
@onready var nt_before_loop_button := %"NT Before Loop Button" as Button
@onready var nt_before_tick_button := %"NT Before Tick Button" as Button
@onready var nt_on_tick_button := %"NT On Tick Button" as Button
@onready var nr_before_loop_button := %"NR Before Loop Button" as Button
@onready var nr_prepare_button := %"NR Prepare Button" as Button
@onready var nr_process_button := %"NR Process Button" as Button
@onready var nr_record_button := %"NR Record Button" as Button
@onready var nr_after_loop_button := %"NR After Loop" as Button
@onready var nt_after_tick_button := %"NT After Tick Button" as Button
@onready var nt_after_tick_loop_button := %"NT After Tick Loop Button" as Button

@onready var run_nr_tick_button := %"Run NR Tick Button" as Button
@onready var advance_button := %"Advance Button" as Button

@onready var rollback_synchronizer := self.get_tree().root\
	.find_children("*", "RollbackSynchronizer", true, false)\
	.pop_front() as RollbackSynchronizer

func _ready():
	if not rollback_synchronizer:
		OS.alert("No RollbackSynchronizer found! Add one to the scene and run again!")
		get_tree().quit()

	# Render initial data
	_render_data()

	# Set defaults
	input_history_data.text = """{
		0: { "Input:movement": Vector2(1, 0) },
		1: { "Input:movement": Vector2(1, 1) },
		2: { "Input:movement": Vector2(1, 1) },
		3: { "Input:movement": Vector2(0, 1) },
	}"""

	# Start NetworkTime but make sure it doesn't tick on its own
	NetworkTime.start()
	NetworkTime.set_process(false)

	# Connect signals
	rollback_synchronizer._on_transmit_state.connect(func(state, tick):
		transmit_state_data.text = "@%d: %s" % [tick, var_to_str(state)]
	)

	nt_before_loop_button.pressed.connect(func():
		NetworkTime.before_tick_loop.emit()
	)

	nt_before_tick_button.pressed.connect(func():
		NetworkTime.before_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
	)

	nt_on_tick_button.pressed.connect(func():
		NetworkTime.on_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
	)

	nr_before_loop_button.pressed.connect(func():
		NetworkRollback.before_loop.emit()
	)

	nr_prepare_button.pressed.connect(func():
		NetworkRollback.on_prepare_tick.emit(NetworkRollback.tick)
		NetworkRollback.after_prepare_tick.emit(NetworkRollback.tick)
	)

	nr_process_button.pressed.connect(func():
		NetworkRollback.on_process_tick.emit(NetworkRollback.tick)
		NetworkRollback._tick += 1
	)

	nr_record_button.pressed.connect(func():
		NetworkRollback.on_record_tick.emit(NetworkRollback.tick)
	)

	nr_after_loop_button.pressed.connect(func():
		NetworkRollback.after_loop.emit()
	)

	nt_after_tick_button.pressed.connect(func():
		NetworkTime.after_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
	)

	nt_after_tick_loop_button.pressed.connect(func():
		NetworkTime.after_tick_loop.emit()
		NetworkTime._tick += 1
	)

	run_nr_tick_button.pressed.connect(func():
		NetworkRollback.before_loop.emit()

		NetworkRollback.on_prepare_tick.emit(NetworkRollback.tick)
		NetworkRollback.after_prepare_tick.emit(NetworkRollback.tick)
		NetworkRollback.on_process_tick.emit(NetworkRollback.tick)
		NetworkRollback._tick += 1
		NetworkRollback.on_record_tick.emit(NetworkRollback.tick)

		NetworkRollback.after_loop.emit()
	)

	advance_button.pressed.connect(func():
		NetworkRollback._tick += 1
	)

	# Update UI after any button press
	var buttons = get_parent().find_children("*", "Button")
	for button in buttons:
		(button as Button).button_down.connect(_read_data)
		(button as Button).button_up.connect(_render_data)

func _render_data():
	network_tick_data.text = str(NetworkTime.tick)
	rollback_tick_data.text = str(NetworkRollback.tick)

	state_history_data.text = _serialize_history(rollback_synchronizer._states)
	input_history_data.text = _serialize_history(rollback_synchronizer._inputs)

	if rollback_synchronizer.has_input():
		has_input_data.text = "true"
		input_age_data.text = str(rollback_synchronizer.get_input_age())
	else:
		has_input_data.text = "false"
		input_age_data.text = "?"

	simset_list.clear()
	for sim_entry in rollback_synchronizer._simset.values():
		simset_list.add_item(str(sim_entry), null, false)

	skipset_list.clear()
	for skip_entry in rollback_synchronizer._skipset.values():
		skipset_list.add_item(str(skip_entry), null, false)

func _read_data():
	if network_tick_data.text.is_valid_int():
		NetworkTime._tick = network_tick_data.text.to_int()

	if rollback_tick_data.text.is_valid_int():
		NetworkRollback._tick = rollback_tick_data.text.to_int()

	rollback_synchronizer._states = _parse_history(state_history_data.text)
	rollback_synchronizer._inputs = _parse_history(input_history_data.text)

func _serialize_history(history: _PropertyHistoryBuffer) -> String:
	var result = PackedStringArray()

	for tick in history.ticks():
		var snapshot := history.get_snapshot(tick)
		result.append("\t%d: %s" % [tick, var_to_str(snapshot.as_dictionary()).replace("\n", "")])

	return "{\n%s\n}" % [",\n".join(result)]

func _parse_history(history_string: String) -> _PropertyHistoryBuffer:
	var result_data = str_to_var(history_string)
	if not result_data is Dictionary:
		return _PropertyHistoryBuffer.new()

	var result := _PropertyHistoryBuffer.new()
	for tick in result_data.keys():
		var snapshot := _PropertySnapshot.from_dictionary(result_data[tick])
		result.set_snapshot(tick, snapshot)

	return result
