class_name InputRecorder
extends Node2D

@export var recording_timelimit = 6.0
var _recording_countdown = 0.0

var cur_timeline = 0

var timer_recording = 0.0
var cur_recording = []
var recordings = []  # [0] is time stamp after anchor is set . [1] is an action
var is_recording = false

var timer_replaying = 0.0
var cur_replay_indices = []
var is_replaying = false

const ANCHORED_REMNANT = preload("uid://b4dukwr03x8k6")  # scene (prefab)
var anchored_remnants = []
const ANCHOR_SPRITE = preload("uid://d0klyrhsy5v6b")
var anchor_sprite
var anchor_pos
var anchor_time  # past or present

@export var rewind_hold_button_timer := 1.0
var _rewind_hold_button_timer := 0.0
@export var discard_hold_button_timer := 1.0
var _discard_hold_button_timer := 0.0

@export var player: Player

func _ready() -> void:
	SignalBus.changed_time.connect(time_shift_recording)
	SignalBus.loaded_room.connect(reset_upon_room_change)
	SignalBus.boss_reload_anchor.connect(reset_upon_room_change)
	SignalBus.set_remnant_countdown_max(recording_timelimit)

func _exit_tree() -> void:
	SignalBus.changed_time.disconnect(time_shift_recording)
	SignalBus.loaded_room.disconnect(reset_upon_room_change)

func _process(delta: float) -> void:
	if is_recording:  # should still notice even if game paused. prevents the bug when you switch dir while pausing
		# release move
		if Input.is_action_just_released("move_up"):
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.RELEASE_UP])
		if Input.is_action_just_released("move_down"):
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.RELEASE_DOWN])
		if Input.is_action_just_released("move_left"):
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.RELEASE_LEFT])
		if Input.is_action_just_released("move_right"):
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.RELEASE_RIGHT])
			
	if Globals.paused or SceneChanger.mid_change:
		return
	
	if PlayerVariables.max_number_of_remnants > 1:
		if Input.is_action_just_pressed("switch_remnant_timeline_up"):
			cur_timeline = (cur_timeline+1)%(PlayerVariables.max_number_of_remnants)
			SignalBus.change_remnant_timeline(cur_timeline)
		if Input.is_action_just_pressed("switch_remnant_timeline_down"):
			cur_timeline = (cur_timeline-1 + PlayerVariables.max_number_of_remnants)%(PlayerVariables.max_number_of_remnants)
			SignalBus.change_remnant_timeline(cur_timeline)
	
	if Input.is_action_just_pressed("discard_remnant"):
		_discard_hold_button_timer = discard_hold_button_timer
	elif Input.is_action_pressed("discard_remnant"):
		if _discard_hold_button_timer > 0:
			_discard_hold_button_timer -= delta
			if _discard_hold_button_timer <= 0:
				delete_all_timelines()
				reset_upon_room_change()
	elif Input.is_action_just_released("discard_remnant"):
		if _discard_hold_button_timer > 0:
			delete_at_timeline(cur_timeline)
	
	# record
	# has to be recording held down buttons, if e.g. anchor is set while moving => not is_action_just_pressed . except for attack
	if is_recording:
		timer_recording += delta
		if timer_recording >= recording_timelimit:
			delete()
		else:
			# move
			if Input.is_action_pressed("move_up"):
				cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.UP])
			if Input.is_action_pressed("move_down"):
				cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.DOWN])
			if Input.is_action_pressed("move_left"):
				cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.LEFT])
			if Input.is_action_pressed("move_right"):
				cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.RIGHT])
			# attack
			if Input.is_action_just_pressed("attack"):
				cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.ATTACK])
			# time shift
			# handled through SignalBus
	
	# start recording  # todo: shouldn't this be in the player script?
	if Input.is_action_just_pressed("set_anchored_remnant"):
		SignalBus.set_anchor.emit()
		_start_recording()
		# sfx
		AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.SET_ANCHOR)
	
	# stop recording	
	if Input.is_action_just_pressed("rewind_to_anchor"):
		_rewind_hold_button_timer = rewind_hold_button_timer
	elif Input.is_action_pressed("rewind_to_anchor"):
		if _rewind_hold_button_timer > 0:
			_rewind_hold_button_timer -= delta
			if _rewind_hold_button_timer <= 0:
				rewind(-1)
	elif Input.is_action_just_released("rewind_to_anchor"):
		if _rewind_hold_button_timer > 0:
			rewind(cur_timeline)
		
	# replay recordings
	if is_replaying:
		timer_replaying += delta
		if timer_replaying >= recording_timelimit:
			delete()
		for i in range(PlayerVariables.max_number_of_remnants):
			if not recordings[i].is_empty() and cur_replay_indices[i] < recordings[i].size():
				if timer_replaying >= recordings[i][cur_replay_indices[i]][0]:
					_replay_actions_at_index(i)
			else:  # end of anchored_remnant
				cur_replay_indices[i] = 0
				if anchored_remnants[i]:
					anchored_remnants[i].die()
	if _recording_countdown > 0:
		_recording_countdown -= delta
		SignalBus.change_remnant_countdown_absolute(_recording_countdown)
		if _recording_countdown <= 0:
			SignalBus.anchor_run_out()
			delete()

func time_shift_recording():
	if cur_recording.is_empty():
		return
	match Globals.current_time:
		Utils.TIME.PAST: 
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.TIME_SHIFT_TO_PAST])
		Utils.TIME.PRESENT:
			cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.TIME_SHIFT_TO_PRESENT])

func _start_recording():
	delete()
	_recording_countdown = recording_timelimit
	anchor_time = Globals.current_time
	is_recording = true
	# TODO later with multiple lines independent, but for now:
	anchor_pos = player.player_base.global_position
	cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.NOTHING, 0])  # s.t. a remnant still spawns, even if no input is done
	
	# sprite
	if anchor_sprite:  # remove already existing one
		remove_child(anchor_sprite)
	anchor_sprite = ANCHOR_SPRITE.instantiate()
	add_child(anchor_sprite)
	anchor_sprite.z_index = 0
	anchor_sprite.global_position = anchor_pos

func rewind(to_timeline : int):
	if cur_recording.is_empty():
		return
	if anchor_time != Globals.current_time:  # not just pos, but also time
		player.player_base.time_shift.trigger()
		await SignalBus.changed_time  
	player.player_base.global_position = anchor_pos
	SignalBus.reload_anchor.emit()
	start_replaying_recordings(to_timeline)

func start_replaying_recordings(to_timeline : int):
	if cur_recording.is_empty():
		return
	if cur_recording[-1][0] != timer_recording:
		cur_recording.append([timer_recording, Utils.RECORDED_ACTIONS.NOTHING, 0])  # s.t. a remnant still spawns, even if no input is done
	
	if to_timeline in range(PlayerVariables.max_number_of_remnants):
		recordings[to_timeline] = cur_recording
		cur_recording = []
	timer_recording = 0
	_recording_countdown = recording_timelimit
	cur_replay_indices = []
	for i in range(PlayerVariables.max_number_of_remnants):
		cur_replay_indices.append(0)
	timer_replaying = 0
	#is_recording = false
	is_replaying = true
	spawn_remnants()
	
func spawn_remnants():
	for anchored_remnant in anchored_remnants:
		if anchored_remnant:  # delete previous one
			anchored_remnant.die()
	anchored_remnants = []
	for i in range(PlayerVariables.max_number_of_remnants):
		if not recordings[i].is_empty():
			var anchored_remnant = ANCHORED_REMNANT.instantiate() as AnchoredRemnant
			add_child(anchored_remnant)
			anchored_remnant.global_position = anchor_pos
			anchored_remnant.player = player
			anchored_remnant.player_base.set_map_loader(player.map_loader)
			anchored_remnants.append(anchored_remnant)
		else:
			anchored_remnants.append(null)
	AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.RELOAD_ANCHOR)
	
func _replay_actions_at_index(recording_index):
	if not anchored_remnants[recording_index]:  # e.g. if the remnant is dead
		return
	if cur_replay_indices[recording_index] >= recordings[recording_index].size():
		return
	#print(recordings[index][1], " ", recordings[index][0], " ", Time.get_unix_time_from_system())
	anchored_remnants[recording_index].translate_replay_inputs(recordings[recording_index][cur_replay_indices[recording_index]][1])
	cur_replay_indices[recording_index] += 1
	# if multiple actions were at same time, play them too
	if cur_replay_indices[recording_index] < recordings[recording_index].size():
		if timer_replaying >= recordings[recording_index][cur_replay_indices[recording_index]][0]:
			_replay_actions_at_index(recording_index)

func delete_all_timelines():
	for i in range(PlayerVariables.max_number_of_remnants):
		delete_at_timeline(i)

func delete_at_timeline(timeline_index : int):
	if not anchored_remnants or not cur_replay_indices or not recordings:
		return
	if anchored_remnants[timeline_index]:
		anchored_remnants[timeline_index].die()
	cur_replay_indices[timeline_index] = 0
	recordings[timeline_index].clear()
	for recording in recordings:
		if not recording.is_empty():
			return

func reset_upon_room_change():
	_recording_countdown = 0
	SignalBus.anchor_run_out()
	SignalBus.change_remnant_countdown_absolute(-1)
	delete()

func delete():
	for anchored_remnant in anchored_remnants:
		if anchored_remnant: 
			anchored_remnant.die()
	is_recording = false
	is_replaying = false
	timer_replaying = 0
	timer_recording = 0
	recordings.clear()
	anchored_remnants.clear()
	cur_replay_indices.clear()
	for i in range(PlayerVariables.max_number_of_remnants):
		recordings.append([])
		anchored_remnants.append(null)
		cur_replay_indices.append(0)
	cur_recording = []
	if anchor_sprite:
		anchor_sprite.queue_free()
		# if one existed => sfx for deleting it
		AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.DISCARD_ANCHOR)
