extends Node2D
# ----------------------------------------
# ASTEROIDES v2 GODOT 4.5
# ----------------------------------------
# Añadidos respecto al original:
# - Fondo de estrellas en parallax simple (dibujo inmediato).
# - Música de fondo (utilizando el archivo correspondiente de la carpeta assets).
# - Disparos con botón izquierdo del ratón (y táctil) desde la nave.
# - Los disparos destruyen asteroides (suena un SFX por cada destrucción).
# - Al hacer clic, la nave también se mueve hacia ese lado.
# - SFX al disparar, al destruir asteroides, y al morir (game over).
# - Dunciones pequeñas e independientes, llamadas desde
#   _ready, _process, _draw e _input para observar el progreso al implementar cada función.
#
# INPUT MAP necesario (Proyecto → Configuración del Proyecto → Mapa de Entrada):
#   "ui_left" (← / A), "ui_right" (→ / D), "ui_accept" (Enter/Espacio)
#   (El disparo se hace con botón izquierdo del ratón; no hace falta mapearlo.)
#
# RECURSOS que debes aportar:
#   jugador.png, asteroide.png, disparo.png, boton_cerrar.png, estrella.png, fondo.png, game_over.png
#   musica_fondo.mp3, sfx_destruccion.mp3, sfx_muerte.mp3, sfx_disparo

# -------------------------
# CONSTANTES DE CONFIGURACIÓN
# -------------------------
const TAM_JUGADOR = 128
const TAM_ASTEROIDE = 64
const TAM_ESTRELLAS = 8
const NUM_ESTRELLAS = 128
const TAM_DISPARO = 16
const FACTOR_HITBOX = 0.75
const TAM_BOTON_CERRAR = 32
const TAM_TEXTO = 32
const INC_VELOCIDAD = 8
const PAUSA_GAME_OVER = 1

# Imágenes (pon los archivos en la carpeta 'assets')
const TEX_FONDO: Texture2D = preload("res://assets/fondo.png")
const TEX_ESTRELLA: Texture2D = preload("res://assets/estrella.png")
const TEX_GAME_OVER: Texture2D = preload("res://assets/game_over.png")
const TEX_JUGADOR: Texture2D = preload("res://assets/jugador.png")
const TEX_ASTEROIDE: Texture2D = preload("res://assets/asteroide.png")
const TEX_DISPARO: Texture2D = preload("res://assets/disparo.png")
const TEX_BOTON_CERRAR: Texture2D = preload("res://assets/boton_cerrar.png")

# Audios (pon los archivos en la carpeta 'assets')
const MUSICA_FONDO: AudioStream = preload("res://assets/musica_fondo.mp3")
const SFX_DESTRUCCION: AudioStream = preload("res://assets/sfx_destruccion.mp3")
const SFX_MUERTE: AudioStream = preload("res://assets/sfx_muerte.mp3")
const SFX_DISPARO: AudioStream = preload("res://assets/sfx_disparo.mp3")

# Velocidades y tiempos
var vel_jugador = 500
var vel_asteroides = 250
var vel_disparo = 750
var vel_estrellas = 16
var vel_fondo = 8  # Muy lento: 8 px/s
var intervalo_asteroides = 0.75

# -------------------------
# ESTADO DEL JUEGO
# -------------------------
var boton_cerrar: Rect2
var jugador: Rect2
var asteroides: Array[Rect2] = []
var disparos: Array[Rect2] = []
var estrellas = []

var pos_fondo = 0
var tiempo_proximo_asteroide = 0.0
var muerto = false
var pausa = false
var pantalla: Vector2
var tiempo_total = 0.0

# UI
var etiqueta_tiempo: Label

# Controles
var tocando_izquierda = false
var tocando_derecha = false

# Audio players
var musica_fondo: AudioStreamPlayer
var sfx_destruccion: AudioStreamPlayer
var sfx_muerte: AudioStreamPlayer
var sfx_disparo: AudioStreamPlayer

# -------------------------
# CICLO DE VIDA PRINCIPAL
# -------------------------
func _ready():
	# Ejecutar al iniciar la escena
	randomize()
	_inicializar_pantalla()
	_inicializar_jugador()
	_inicializar_estrellas()
	_crear_ui_tiempo()
	_crear_boton_cerrar()
	_inicializar_audio()
	_reproducir(musica_fondo)

func _process(delta: float):
	# Ejecutar la lógica del juego en cada frame
	if muerto: return
	_mover_jugador(delta)
	_mover_estrellas(delta)
	_mover_fondo(delta)
	_crear_asteroides(delta)
	_mover_asteroides(delta)
	_mover_disparos(delta)
	_colisiones_disparos_asteroides()
	_comprobar_colision_jugador()
	_actualizar_tiempo(delta)
	_actualizar_dificultad(delta)
	queue_redraw()

func _draw():
	# Dibujar en pantalla los elementos del juego
	_mostrar_fondo_jugando()
	_dibujar_estrellas()
	_dibujar_asteroides()
	_dibujar_disparos()
	_dibujar_jugador()
	_mostrar_boton_cerrar()
	if muerto: _mostrar_game_over()

func _input(event: InputEvent):
	# Comprobar la entrada de teclado, ratón o táctil
	if muerto and pausa: return
	_comprobar_pantalla_tactil_y_raton(event)
	_comprobar_teclado(event)

# Estructura inicial con todas las funciones vacías.
# Más adelante iremos completándolas paso a paso.

# -------------------------
# INICIALIZACIÓN BÁSICA
# -------------------------
func _inicializar_pantalla():
	# Guardar el tamaño actual de la pantalla (ancho y alto)
	pantalla = get_viewport_rect().size

func _inicializar_jugador():
	# Colocar al jugador centrado abajo de la pantalla con un pequeño margen
	jugador = Rect2(
		pantalla.x * 0.5 - TAM_JUGADOR * 0.5,  # Coordenada X: centrado
		pantalla.y - TAM_JUGADOR * 1.25,       # Coordenada Y: cerca del borde inferior
		TAM_JUGADOR,                           # Ancho del rectángulo
		TAM_JUGADOR                            # Alto del rectángulo
	)

# -------------------------
# ESTRELLAS DE FONDO
# -------------------------
func _inicializar_estrellas():
	# Rellenar la lista con estrellas aleatorias (posición en pantalla y profundidad para parallax)
	estrellas.clear()
	for _i in range(NUM_ESTRELLAS):
		var x = randf_range(0.0, pantalla.x)
		var y = randf_range(0.0, pantalla.y)
		var profundidad = randf()  # 0 = cerca, 1 = lejos
		estrellas.append({ "pos": Vector2(x, y), "profundidad": profundidad })

func _mover_estrellas(delta):
	# Desplazar estrellas según profundidad y reciclar al salir por abajo
	for e in estrellas:
		var velocidad = lerp(float(vel_estrellas), vel_estrellas * 0.1, e.profundidad)
		e.pos.y += velocidad * delta
		if e.pos.y > pantalla.y:
			e.pos.y = -TAM_ESTRELLAS
			e.pos.x = randf_range(0.0, pantalla.x)

func _dibujar_estrellas():
	# Dibujar cada estrella escalando tamaño y modulando brillo según su profundidad
	for e in estrellas:
		var z = e.profundidad
		var tam = lerp(TAM_ESTRELLAS as float, 1.0, z)
		var a = lerp(0.75, 0.25, z)
		draw_texture_rect(TEX_ESTRELLA, Rect2(e.pos, Vector2(tam, tam)), false, Color(1, 1, 1, a))

# -------------------------
# AUDIO
# -------------------------
func _crear_audio_player(stream: AudioStream, bus: String, volumen := 0.0):
	# Instanciar y configurar un AudioStreamPlayer con stream, bus y volumen inicial, y devolverlo
	var player = AudioStreamPlayer.new()
	player.stream = stream
	player.bus = bus
	player.volume_db = volumen
	add_child(player)
	return player

func _inicializar_audio():
	# Construir y registrar players de música y SFX con buses/volúmenes apropiados
	musica_fondo    = _crear_audio_player(MUSICA_FONDO, "Music", -5.0)
	sfx_destruccion = _crear_audio_player(SFX_DESTRUCCION, "SFX")
	sfx_muerte      = _crear_audio_player(SFX_MUERTE, "SFX")
	sfx_disparo     = _crear_audio_player(SFX_DISPARO, "SFX", -10.0)

func _reproducir(audio: AudioStreamPlayer):
	# Reproducir el AudioStreamPlayer si existir y tener stream válido para evitar errores en tiempo de ejecución
	if audio and audio.stream:
		audio.play()

# -------------------------
# JUGADOR
# -------------------------
func _dibujar_jugador():
	# Dibujar la textura del jugador dentro de su Rect2 (respetando el orden de capas)
	draw_texture_rect(TEX_JUGADOR, jugador, false)

func _mover_jugador(delta: float):
	# Calcular dirección (-1, 0, +1), aplicar velocidad con delta y limitar dentro de pantalla
	var dir = 0
	if tocando_izquierda:
		dir = -1
	elif tocando_derecha:
		dir = 1

	jugador.position.x = clamp(
		jugador.position.x + dir * vel_jugador * delta,
		0.0,
		pantalla.x - TAM_JUGADOR
	)

# -------------------------
# CONTROLES
# -------------------------
func _comprobar_pantalla_tactil_y_raton(event: InputEvent):
	# Gestionar toques/clics: cerrar si pulsar en botón, fijar dirección según lado de la nave y disparar al presionar
	if event is InputEventScreenTouch or event is InputEventMouseButton:
		if event.pressed:
			if boton_cerrar.has_point(event.position):
				if get_tree():
					get_tree().quit.call_deferred()  # Cerrar de forma segura
				return

			var centro_nave = jugador.position.x + TAM_JUGADOR * 0.5
			if event.position.x < centro_nave:
				tocando_izquierda = true
				tocando_derecha = false
			else:
				tocando_derecha = true
				tocando_izquierda = false

			_crear_disparo()  # Disparar al presionar (clic o toque)
		else:
			# Al soltar: detener movimiento
			tocando_izquierda = false
			tocando_derecha = false

func _comprobar_teclado(event: InputEvent):
	# Gestionar reinicio con ui_accept, disparo con ui_up y banderas de movimiento con ui_left/ui_right
	if muerto and event.is_action_pressed("ui_accept"):
		_reiniciar_juego()
		return

	if event.is_action_pressed("ui_up"):
		_crear_disparo()

	if event.is_action_pressed("ui_left"):
		tocando_izquierda = true
	if event.is_action_released("ui_left"):
		tocando_izquierda = false

	if event.is_action_pressed("ui_right"):
		tocando_derecha = true
	if event.is_action_released("ui_right"):
		tocando_derecha = false

# -------------------------
# BOTÓN CERRAR
# -------------------------
func _crear_boton_cerrar():
	# Definir el área clicable del botón de cierre en la esquina superior derecha
	boton_cerrar = Rect2(pantalla.x - TAM_BOTON_CERRAR, 0, TAM_BOTON_CERRAR, TAM_BOTON_CERRAR)

func _mostrar_boton_cerrar():
	# Dibujar la textura del botón de cierre dentro de su rectángulo clicable
	draw_texture_rect(TEX_BOTON_CERRAR, boton_cerrar, false)

# -------------------------
# UI
# -------------------------
func _crear_ui_tiempo():
	# Crear un Label para el cronómetro y añadirlo a la escena con tamaño de fuente legible
	etiqueta_tiempo = Label.new()
	etiqueta_tiempo.text = "0.0 s"
	etiqueta_tiempo.set("theme_override_font_sizes/font_size", TAM_TEXTO)
	etiqueta_tiempo.visible = true
	add_child(etiqueta_tiempo)

func _actualizar_tiempo(delta: float):
	# Acumular delta si seguimos vivos y actualizar el Label con tiempo redondeado a 0.1 s
	if not muerto:
		tiempo_total += delta
	etiqueta_tiempo.text = str(snappedf(tiempo_total, 0.1)) + " s"

# -------------------------
# ASTEROIDES
# -------------------------
func _crear_asteroides(delta: float):
	# Acumular tiempo y crear un asteroide cuando se supera el intervalo configurado
	tiempo_proximo_asteroide += delta
	if tiempo_proximo_asteroide >= intervalo_asteroides:
		tiempo_proximo_asteroide = 0.0
		var x := randf_range(0.0, pantalla.x - TAM_ASTEROIDE)
		var tam := randf_range(TAM_ASTEROIDE * 0.5, TAM_ASTEROIDE * 2.0)
		var asteroide := Rect2(x, -tam, tam, tam)  # Entrar desde arriba del viewport
		asteroides.append(asteroide)

func _mover_asteroides(delta: float):
	# Desplazar asteroides hacia abajo y eliminar los que salen por la parte inferior
	for i in range(asteroides.size() - 1, -1, -1):
		asteroides[i].position.y += vel_asteroides * delta
		if asteroides[i].position.y > pantalla.y:
			asteroides.remove_at(i)

func _dibujar_asteroides():
	# Dibujar cada asteroide usando su Rect2 como destino para la textura
	for a in asteroides:
		draw_texture_rect(TEX_ASTEROIDE, a, false)

# -------------------------
# DISPAROS
# -------------------------
func _crear_disparo():
	# Crear un Rect2 para el proyectil centrado en la nave y enlistarlo, reproduciendo SFX de disparo
	var x = jugador.position.x + TAM_JUGADOR * 0.5 - TAM_DISPARO * 0.5
	var y = jugador.position.y - TAM_DISPARO
	disparos.append(Rect2(x, y, TAM_DISPARO, TAM_DISPARO))
	_reproducir(sfx_disparo)

func _mover_disparos(delta: float):
	# Desplazar proyectiles hacia arriba y eliminar los que salen por el borde superior
	for i in range(disparos.size() - 1, -1, -1):
		disparos[i].position.y -= vel_disparo * delta
		if disparos[i].position.y <= 0.0:
			disparos.remove_at(i)

func _dibujar_disparos():
	# Pintar cada disparo con su textura correspondiente sobre el rectángulo destino
	for d in disparos:
		draw_texture_rect(TEX_DISPARO, d, false)

func _colisiones_disparos_asteroides():
	# Detectar impactos entre disparos y asteroides, eliminarlos y reproducir SFX de destrucción
	if disparos.is_empty() or asteroides.is_empty():
		return
	for a in range(asteroides.size() - 1, -1, -1):
		for d in range(disparos.size() - 1, -1, -1):
			if asteroides[a].intersects(disparos[d]):
				asteroides.remove_at(a)
				disparos.remove_at(d)
				_reproducir(sfx_destruccion)
				break

# -------------------------
# COLISIONES Y GAME OVER
# -------------------------
func _escalar_rect(r: Rect2):
	# Encoger uniformemente la hitbox según FACTOR_HITBOX para hacer la colisión más justa
	return r.grow((FACTOR_HITBOX - 1.0) * r.size.x / 2.0)

func _comprobar_colision_jugador():
	# Comprobar intersección entre la hitbox ajustada del jugador y la de cada asteroide, y finalizar partida si chocar
	var jugador_escalado = _escalar_rect(jugador)
	for a in asteroides:
		if jugador_escalado.intersects(_escalar_rect(a)):
			_game_over()
			return

func _pausa():
	# Activar una pausa breve no bloqueante tras el Game Over esperando a un temporizador asincrónico
	pausa = true
	await get_tree().create_timer(PAUSA_GAME_OVER).timeout
	pausa = false

func _game_over():
	# Marcar estado de muerte, detener controles y música, mostrar mensaje y lanzar pausa breve
	muerto = true
	tocando_izquierda = false
	tocando_derecha = false

	if musica_fondo:
		musica_fondo.stop()
	_reproducir(sfx_muerte)

	_pausa()

func _reiniciar_juego():
	# Recargar la escena actual para devolver todo a su estado inicial
	get_tree().reload_current_scene()

# -------------------------
# FONDO Y CAPAS
# -------------------------
func _mostrar_fondo_jugando():
	# Pintar dos copias del fondo (offset y offset-altura) para simular scroll vertical infinito
	draw_texture_rect(TEX_FONDO, Rect2(0, pos_fondo, pantalla.x, pantalla.y), false)
	draw_texture_rect(TEX_FONDO, Rect2(0, pos_fondo - pantalla.y, pantalla.x, pantalla.y), false)

func _mover_fondo(delta: float):
	# Actualizar offset vertical del fondo con velocidad constante y envolverlo para scroll infinito
	pos_fondo = wrapf(pos_fondo + vel_fondo * delta, 0.0, pantalla.y)

func _mostrar_game_over():
	# Cubrir la pantalla con la textura de Game Over modulada para crear un velo semitransparente
	draw_texture_rect(TEX_GAME_OVER, Rect2(Vector2.ZERO, pantalla), false, Color(1, 1, 1, 0.5))

# -------------------------
# DIFICULTAD PROGRESIVA
# -------------------------
func _actualizar_dificultad(delta: float):
	# Incrementar gradualmente la velocidad global y reducir el intervalo de spawn con límites seguros
	var inc_velocidad := INC_VELOCIDAD * delta
	vel_asteroides += inc_velocidad
	vel_estrellas += inc_velocidad * 0.1
	vel_fondo += inc_velocidad * 0.01
	intervalo_asteroides = max(0.1, intervalo_asteroides - inc_velocidad * 0.001)
