extends Path3D

@export_group("Impostazioni Treno")
@export var velocita_treno: float = 6.0
@export var in_movimento: bool = true
@export var modello_treno: PackedScene 
@export var distanza_assi: float = 3.0 
@export var larghezza_binari: float = 1.2 
@export var gruppo_camere: Node3D 

@export_group("Costruzione Rotaie")
@export var distanza_traversine: float = 1.0
@export var modello_traversina: PackedScene 

@export_group("Controlli Manuali")
@export var velocita_max: float = 15.0
@export var velocita_min: float = -5.0 
@export var accelerazione_manuale: float = 5.0 

@export_group("Fermate (Stazioni)")
@export var abilita_fermate: bool = true
@export var tempo_di_sosta: float = 4.0 
@export var distanza_frenata: float = 15.0 
@export var tempo_ripartenza: float = 3.0 
@export var scena_fuochi: PackedScene 

var colori_fuochi: Array[Color] = [
	Color(1.0, 0.2, 0.2), # Rosso vivo
	Color(0.2, 1.0, 0.2), # Verde lime
	Color(0.3, 0.5, 1.0), # Blu cielo
	Color(1.0, 0.8, 0.1), # Oro
	Color(0.8, 0.2, 1.0), # Viola
	Color(0.1, 1.0, 0.9)  # Ciano
]

@export_group("Movimento e Oscillazione")
@export_range(0.0, 1.0) var intensita_oscillazione_base: float = 0.5 

var treno_istanziato: Node3D
var progresso_treno: float = 0.0
var tempo_oscillazione: float = 0.0

var offsets_fermate: Array[float] = []
var posizioni_fermate: Array[Vector3] = [] 
var fermata_in_corso: bool = false
var indice_prossima_fermata: int = 0
var moltiplicatore_stazione: float = 1.0
var in_ripartenza: bool = false
var tween_ripartenza: Tween

func _ready() -> void:
	if curve != null and curve.get_baked_length() > 0:
		_costruisci_rotaie_da_zero()
		_costruisci_treno()
		_calcola_fermate()
	else:
		print("ATTENZIONE: Devi prima disegnare i punti del Path3D!")

func _costruisci_rotaie_da_zero() -> void:
	var mat_legno = StandardMaterial3D.new()
	mat_legno.albedo_color = Color(0.35, 0.2, 0.1) 
	var mat_ferro = StandardMaterial3D.new()
	mat_ferro.albedo_color = Color(0.6, 0.6, 0.65) 
	mat_ferro.metallic = 0.8
	
	var mesh_traversina_default = BoxMesh.new()
	mesh_traversina_default.size = Vector3(larghezza_binari + 0.6, 0.1, 0.3)
	var mesh_binario = BoxMesh.new()
	mesh_binario.size = Vector3(0.1, 0.15, distanza_traversine + 0.05) 
	
	var lunghezza_totale = curve.get_baked_length()
	var numero_pezzi = int(lunghezza_totale / distanza_traversine)
	
	for i in range(numero_pezzi):
		var distanza = i * distanza_traversine
		var pezzo_binario = Node3D.new()
		add_child(pezzo_binario)
		
		var pos_attuale_locale = curve.sample_baked(distanza, true)
		var distanza_avanti = distanza + 0.1
		var pos_avanti_locale = Vector3.ZERO
		
		if distanza_avanti > lunghezza_totale:
			var pos_dietro = curve.sample_baked(distanza - 0.1, true)
			pos_avanti_locale = pos_attuale_locale + (pos_attuale_locale - pos_dietro)
		else:
			pos_avanti_locale = curve.sample_baked(distanza_avanti, true)
			
		pezzo_binario.position = pos_attuale_locale
		var globale_attuale = to_global(pos_attuale_locale)
		var globale_avanti = to_global(pos_avanti_locale)
		if globale_attuale.distance_to(globale_avanti) > 0.001:
			pezzo_binario.look_at(globale_avanti, Vector3.UP)
		
		if modello_traversina != null:
			var traversina_custom = modello_traversina.instantiate()
			pezzo_binario.add_child(traversina_custom)
		else:
			var traversina = MeshInstance3D.new()
			traversina.mesh = mesh_traversina_default
			traversina.material_override = mat_legno
			pezzo_binario.add_child(traversina)
		
		var binario_sx = MeshInstance3D.new()
		binario_sx.mesh = mesh_binario
		binario_sx.material_override = mat_ferro
		binario_sx.position = Vector3(-larghezza_binari / 2.0, 0.1, 0) 
		pezzo_binario.add_child(binario_sx)
		
		var binario_dx = MeshInstance3D.new()
		binario_dx.mesh = mesh_binario
		binario_dx.material_override = mat_ferro
		binario_dx.position = Vector3(larghezza_binari / 2.0, 0.1, 0) 
		pezzo_binario.add_child(binario_dx)

func _calcola_fermate() -> void:
	var fermate_temp = []
	for figlio in get_children():
		if figlio is Marker3D:
			var offset = curve.get_closest_offset(figlio.position)
			fermate_temp.append({
				"offset": offset,
				"posizione": figlio.global_position 
			})
	fermate_temp.sort_custom(func(a, b): return a["offset"] < b["offset"])
	for dati in fermate_temp:
		offsets_fermate.append(dati["offset"])
		posizioni_fermate.append(dati["posizione"])

func _input(event: InputEvent) -> void:
	if event is InputEventKey and event.pressed and not event.echo:
		if event.keycode == KEY_T:
			_teletrasporta_prossima_fermata()
		elif event.keycode == KEY_F:
			_test_fuochi_manuale()

func _test_fuochi_manuale() -> void:
	if scena_fuochi == null or treno_istanziato == null: return
		
	print("Lancio fuochi d'artificio di test!")
	var num_fuochi = randi_range(5, 8)
	for i in range(num_fuochi):
		var fuoco_root = scena_fuochi.instantiate()
		get_tree().current_scene.add_child(fuoco_root)
		
		var offset_x = randf_range(-6.0, 6.0)
		var offset_z = randf_range(-6.0, 6.0)
		fuoco_root.global_position = treno_istanziato.global_position + Vector3(offset_x, 0.5, offset_z)
		
		if fuoco_root.has_method("imposta_colore"):
			fuoco_root.imposta_colore(colori_fuochi.pick_random())
		if fuoco_root.has_method("accendi"):
			fuoco_root.accendi()
		
		await get_tree().create_timer(randf_range(0.2, 0.6)).timeout

func _teletrasporta_prossima_fermata() -> void:
	if offsets_fermate.size() == 0 or fermata_in_corso or in_ripartenza:
		return
	var target_offset = offsets_fermate[indice_prossima_fermata]
	progresso_treno = target_offset
	_esegui_fermata(velocita_treno >= 0)

func _physics_process(delta: float) -> void:
	_gestione_input_movimento(delta) 
	_gestione_movimento_treno(delta)

func _gestione_input_movimento(delta: float) -> void:
	if not in_movimento: return
	if Input.is_action_pressed("ui_up"):
		velocita_treno += accelerazione_manuale * delta
	elif Input.is_action_pressed("ui_down"):
		velocita_treno -= accelerazione_manuale * delta
	velocita_treno = clamp(velocita_treno, velocita_min, velocita_max)

func _gestione_movimento_treno(delta: float) -> void:
	if in_movimento and treno_istanziato and curve:
		var lunghezza_totale = curve.get_baked_length()
		if lunghezza_totale <= 0: return
		
		if not fermata_in_corso:
			var vecchio_progresso = progresso_treno
			
			if abilita_fermate and offsets_fermate.size() > 0 and not in_ripartenza:
				var target_offset = offsets_fermate[indice_prossima_fermata]
				var dist = target_offset - progresso_treno
				dist = wrapf(dist + lunghezza_totale / 2.0, 0.0, lunghezza_totale) - lunghezza_totale / 2.0
				
				if abs(dist) < distanza_frenata and sign(dist) == sign(velocita_treno):
					var t = abs(dist) / distanza_frenata
					moltiplicatore_stazione = max(smoothstep(0.0, 1.0, t), 0.05)
				else:
					moltiplicatore_stazione = 1.0
			
			var velocita_attuale = velocita_treno * moltiplicatore_stazione
			progresso_treno += velocita_attuale * delta
			
			if abilita_fermate and offsets_fermate.size() > 0:
				var target_offset = offsets_fermate[indice_prossima_fermata]
				var superato_avanti = (velocita_attuale > 0 and vecchio_progresso < target_offset and progresso_treno >= target_offset)
				var superato_indietro = (velocita_attuale < 0 and vecchio_progresso > target_offset and progresso_treno <= target_offset)
				
				if superato_avanti or superato_indietro:
					progresso_treno = target_offset 
					_esegui_fermata(velocita_attuale > 0)
			
			if progresso_treno > lunghezza_totale:
				progresso_treno -= lunghezza_totale
				if offsets_fermate.size() > 0: indice_prossima_fermata = 0 
			elif progresso_treno < 0: 
				progresso_treno += lunghezza_totale
				if offsets_fermate.size() > 0: indice_prossima_fermata = offsets_fermate.size() - 1
		
		var pos_centro = to_global(curve.sample_baked(progresso_treno, true))
		var prog_davanti = wrapf(progresso_treno + 2.0, 0.0, lunghezza_totale)
		var pos_davanti = to_global(curve.sample_baked(prog_davanti, true))
		
		if pos_centro.distance_to(pos_davanti) > 0.01:
			treno_istanziato.global_position = pos_centro
			treno_istanziato.look_at(pos_davanti, Vector3.UP)
			treno_istanziato.rotate_y(PI) 
			
			if gruppo_camere:
				# Nessun ritardo, nessuna molla: posizione e rotazione sono incollate a quelle del treno!
				gruppo_camere.global_position = pos_centro
				gruppo_camere.global_basis = treno_istanziato.global_basis
				
		if not fermata_in_corso:
			var vel_reale = abs(velocita_treno * moltiplicatore_stazione)
			tempo_oscillazione += delta * vel_reale * 2.0
			var ampiezza_z = 0.006 * intensita_oscillazione_base
			var ampiezza_x = 0.004 * intensita_oscillazione_base
			treno_istanziato.rotation.z += sin(tempo_oscillazione) * ampiezza_z
			treno_istanziato.rotation.x += cos(tempo_oscillazione * 0.8) * ampiezza_x

func _esegui_fermata(andando_avanti: bool = true) -> void:
	fermata_in_corso = true
	moltiplicatore_stazione = 0.0 
	
	var indice_stazione_attuale = indice_prossima_fermata
	
	if andando_avanti:
		indice_prossima_fermata += 1
		if indice_prossima_fermata >= offsets_fermate.size():
			indice_prossima_fermata = 0 
	else:
		indice_prossima_fermata -= 1
		if indice_prossima_fermata < 0:
			indice_prossima_fermata = offsets_fermate.size() - 1
			
	if scena_fuochi != null:
		var posizione_marker = posizioni_fermate[indice_stazione_attuale]
		var num_fuochi = randi_range(5, 8)
		for i in range(num_fuochi):
			var fuoco_root = scena_fuochi.instantiate()
			get_tree().current_scene.add_child(fuoco_root)
			
			var offset_x = randf_range(-5.0, 5.0)
			var offset_z = randf_range(-5.0, 5.0)
			
			fuoco_root.global_position = posizione_marker + Vector3(offset_x, 0.5, offset_z)
			
			if fuoco_root.has_method("imposta_colore"):
				fuoco_root.imposta_colore(colori_fuochi.pick_random())
			if fuoco_root.has_method("accendi"):
				fuoco_root.accendi() 
				
			await get_tree().create_timer(randf_range(0.3, 0.8)).timeout
		
	await get_tree().create_timer(tempo_di_sosta).timeout
	
	fermata_in_corso = false
	in_ripartenza = true
	
	if tween_ripartenza and tween_ripartenza.is_valid():
		tween_ripartenza.kill()
		
	tween_ripartenza = create_tween()
	tween_ripartenza.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
	tween_ripartenza.tween_property(self, "moltiplicatore_stazione", 1.0, tempo_ripartenza)
	tween_ripartenza.tween_callback(func(): in_ripartenza = false)

func _costruisci_treno() -> void:
	if modello_treno != null:
		treno_istanziato = modello_treno.instantiate()
		add_child(treno_istanziato)
	else:
		_costruisci_trenino_default()

func _costruisci_trenino_default() -> void:
	treno_istanziato = Node3D.new()
	add_child(treno_istanziato)
	
	var mat_carrozzeria = StandardMaterial3D.new()
	mat_carrozzeria.albedo_color = Color(0.8, 0.15, 0.15) 
	var mat_vetro = StandardMaterial3D.new()
	mat_vetro.albedo_color = Color(0.2, 0.8, 1.0) 
	
	var base_treno = MeshInstance3D.new()
	var mesh_base = BoxMesh.new()
	mesh_base.size = Vector3(1.4, 0.8, 3.0)
	base_treno.mesh = mesh_base
	base_treno.material_override = mat_carrozzeria
	base_treno.position = Vector3(0, 0.6, 0) 
	treno_istanziato.add_child(base_treno)
	
	var cabina = MeshInstance3D.new()
	var mesh_cabina = BoxMesh.new()
	mesh_cabina.size = Vector3(1.4, 1.0, 1.2)
	cabina.mesh = mesh_cabina
	cabina.material_override = mat_vetro
	cabina.position = Vector3(0, 1.5, -0.8) 
	treno_istanziato.add_child(cabina)
	
	var ciminiera = MeshInstance3D.new()
	var mesh_ciminiera = CylinderMesh.new()
	mesh_ciminiera.top_radius = 0.2
	mesh_ciminiera.bottom_radius = 0.3
	mesh_ciminiera.height = 0.8
	ciminiera.mesh = mesh_ciminiera
	ciminiera.material_override = mat_carrozzeria
	ciminiera.position = Vector3(0, 1.2, 1.0)
	treno_istanziato.add_child(ciminiera)
