extends SyllablesMinigame

const HOLE_SCENE: PackedScene = preload("res://sources/minigames/crabs/hole/hole.tscn")
const BACKGROUND_2: CompressedTexture2D = preload("res://assets/minigames/crabs/graphic/background_2.png")

var difficulty_settings: Array[DifficultySettings] = [
	DifficultySettings.new(0.75, [2, 1]),
	DifficultySettings.new(0.66, [3, 2]),
	DifficultySettings.new(0.33, [4, 3]),
	DifficultySettings.new(0.25, [4, 3, 2]),
	DifficultySettings.new(0.25, [4, 3, 4])
]
var holes: Array[Hole] = []
var stimulus_spawned: bool = false

@onready var crab_zone: Control = $GameRoot/CrabZone
@onready var background: TextureRect = %Background


# Find and set the parameters of the minigame, like the number of lives or the victory conditions.
func _setup_minigame() -> void:
	super._setup_minigame()
	
	if randf() < 0.5:
		background.texture = BACKGROUND_2

	var current_difficulty_settings: DifficultySettings = _get_difficulty_settings()

	# Define the rectangular zone where holes will be placed
	var zone_top_left: Vector2 = crab_zone.position
	var zone_bottom_right: Vector2 = zone_top_left + crab_zone.size

	# Loop through each row defined by the difficulty settings
	for row_index: int in range(current_difficulty_settings.rows.size()):
		# Compute vertical placement ratio (between 0 and 1, skipping the edges)
		var vertical_ratio: float = float(row_index + 1) / float(current_difficulty_settings.rows.size() + 1)
		var hole_y: float = lerp(zone_top_left.y, zone_bottom_right.y, vertical_ratio)

		# Get the number of holes to place in this row
		var holes_in_row: int = current_difficulty_settings.rows[row_index]

		# Loop through each hole in the current row
		for column_index: int in range(holes_in_row):
			# Compute horizontal placement ratio (between 0 and 1, skipping the edges)
			var horizontal_ratio: float = float(column_index + 1) / float(holes_in_row + 1)
			var hole_x: float = lerp(zone_bottom_right.x, zone_top_left.x, horizontal_ratio)

			# Instantiate and position the hole
			var hole: Hole = HOLE_SCENE.instantiate()
			game_root.add_child(hole)
			hole.position = Vector2(hole_x, hole_y)

			if not Engine.is_editor_hint():
				hole.crab_out.connect(_on_hole_crab_out.bind(hole))
				hole.stimulus_hit.connect(_on_stimulus_pressed.bind(hole))
				hole.crab_despawned.connect(_on_hole_crab_despawned)
				stimulus_heard.connect(hole.on_stimulus_heard)

			holes.append(hole)
 

# Launch the minigame
func _start() -> void:
	super()
	fireworks.set_colors([Color("#7fc8ff"), Color("#f5a8c8"), Color("#bca4ff")])
	_spawn_crabs()


func _get_difficulty_settings() -> DifficultySettings:
	return difficulty_settings[difficulty]


func _highlight() -> void:
	for hole: Hole in holes:
		if hole.crab and hole.crab_visible and _is_stimulus_right(hole.crab.stimulus):
			hole.highlight()


func _on_stimulus_pressed(stimulus: Dictionary, node: Node) -> bool:
	if not super(stimulus, node):
		return false
	
	var hole: Hole = node as Hole
	if not hole:
		return false
	
	var is_right: bool = _is_stimulus_right(stimulus)
	if is_right:
		hole.right()
		current_progression += 1
		stimulus_spawned = false
	else:
		hole.wrong()
		current_lives -= 1
		
		# Spawn another crab
		_on_hole_crab_despawned(false)
		
		# Play the pressed crab syllable
		if stimulus:
			await audio_player.play_syllable(stimulus)
		
		await get_tree().create_timer(1).timeout
		_play_current_stimulus_phoneme()
	
	return true


func _on_current_progression_changed() -> void:
	# Play the new stimulus
	super()
	# Restarts the spawning of crabs
	_spawn_crabs()

# ------------ Crabs ------------

# Spawn a set amount of crabs in random holes
func _spawn_crabs() -> void:
	for index: int in range(int(3.0 * holes.size() / 4.0)):
		_on_hole_crab_despawned(false)
		await get_tree().create_timer(0.1).timeout


func _on_hole_crab_out(hole: Hole) -> void:
	if is_highlighting and _is_stimulus_right(hole.crab.stimulus):
		hole.highlight()


func _on_hole_crab_despawned(is_stimulus: bool) -> void:
	if is_stimulus:
		stimulus_spawned = false
	
	if await _await_for_future_or_stimulus_found(get_tree().create_timer(randf_range(0.1, 2.0)).timeout):
		return
	_on_hole_timer_timeout()


func _on_hole_timer_timeout() -> void:
	var holes_range: Array = range(holes.size())
	holes_range.shuffle()
	
	var hole_found: bool = false
	while not hole_found:
		for index: int in holes_range:
			if not holes[index].crab:
				# Define if the crab is a stimulus or a distraction
				# Only one crab with the correct stimulus can be showned at a time
				var is_stimulus: bool = not stimulus_spawned and randf() < _get_difficulty_settings().stimuli_ratio
				if is_stimulus:
					stimulus_spawned = true
					holes[index].spawn_crab(_get_current_stimulus(), true)
				else:
					var current_distractors: Array = distractions[current_progression % distractions.size()]
					holes[index].spawn_crab(current_distractors.pick_random() as Dictionary, false)
				hole_found = true
				break
		
		if not hole_found:
			if await _await_for_future_or_stimulus_found(get_tree().create_timer(randf_range(0.1, 0.5)).timeout):
				hole_found = true


func _on_stimulus_found() -> void:
	# Despawn all the crabs
	for hole: Hole in holes:
		hole.stop.emit()


class DifficultySettings:
	var stimuli_ratio: float = 0.75
	var rows: Array[int] = [2, 1]
	
	
	func _init(p_stimuli_ratio: float, p_rows: Array[int]) -> void:
		stimuli_ratio = p_stimuli_ratio
		rows = p_rows
