extends Node3D

@export_group("Riferimenti Base")
@export var path_treno: Path3D 

@export_group("I 3 Biomi Principali")
@export var bioma_campagna: Array[PackedScene] 
@export var bioma_foresta: Array[PackedScene]  
@export var bioma_altro: Array[PackedScene]    

@export_group("Impostazioni Griglia e Aree")
@export var dimensione_chunk: float = 20.0
@export var raggio_visivo: int = 3 
@export var scala_distretti: float = 0.05 

var scacchiera: Dictionary = {}
var ultima_pos_griglia_treno: Vector2i = Vector2i(999999, 999999) 
var generatore_rumore: FastNoiseLite 

# Variabile per il Toggle del terzo bioma
var usa_terzo_bioma: bool = true

func _ready() -> void:
	if bioma_campagna.is_empty() or path_treno == null:
		print("❌ ERRORE: Assegna almeno il 'Bioma Campagna' e il 'Path Treno' nell'Inspector!")
		return
		
	generatore_rumore = FastNoiseLite.new()
	generatore_rumore.noise_type = FastNoiseLite.TYPE_PERLIN
	generatore_rumore.seed = randi() 
	generatore_rumore.frequency = scala_distretti 
	
	print("✅ Generatore a Distretti Dinamici Pronto.")
	print("Premere il tasto 'B' per abilitare/disabilitare il terzo bioma.")

func _input(event: InputEvent) -> void:
	if event is InputEventKey and event.pressed and not event.echo:
		if event.keycode == KEY_B:
			usa_terzo_bioma = !usa_terzo_bioma
			if usa_terzo_bioma:
				print("🌍 Terzo Bioma: ATTIVATO")
			else:
				print("🌍 Terzo Bioma: DISATTIVATO (La mappa si ferma alla Foresta)")

func _physics_process(_delta: float) -> void:
	if path_treno == null or path_treno.treno_istanziato == null: return
		
	var pos_treno = path_treno.treno_istanziato.global_position
	var grid_x = roundi(pos_treno.x / dimensione_chunk)
	var grid_z = roundi(pos_treno.z / dimensione_chunk)
	var pos_attuale = Vector2i(grid_x, grid_z)
	
	if pos_attuale != ultima_pos_griglia_treno:
		ultima_pos_griglia_treno = pos_attuale
		_genera_attorno_al_treno(pos_attuale)
		
		# Accendiamo il GPS
		_radar_bioma_treno()

func _genera_attorno_al_treno(centro: Vector2i) -> void:
	for x in range(-raggio_visivo, raggio_visivo + 1):
		for z in range(-raggio_visivo, raggio_visivo + 1):
			var pos_griglia = centro + Vector2i(x, z)
			
			if not scacchiera.has(pos_griglia):
				var ce_ostacolo = _registra_cella_con_laser(pos_griglia)
				
				if not ce_ostacolo:
					_piazza_bioma_compatibile(pos_griglia)

# --- SCELTA DEI CATALOGHI CON IL 50/50 MATEMATICO ---
func _scegli_catalogo_per_cella(pos_griglia: Vector2i) -> Array[PackedScene]:
	var valore = generatore_rumore.get_noise_2d(pos_griglia.x, pos_griglia.y)
	var nome_bioma_scelto = _get_nome_bioma(valore)
	
	var catalogo_scelto: Array[PackedScene] = []
	
	if nome_bioma_scelto == "Campagna": 
		catalogo_scelto = bioma_campagna
		
	elif nome_bioma_scelto == "Misto Campagna-Foresta": 
		# Lancia la moneta: 50% Campagna, 50% Foresta
		catalogo_scelto = bioma_campagna if randf() > 0.5 else bioma_foresta
		
	elif nome_bioma_scelto == "Foresta": 
		catalogo_scelto = bioma_foresta
		
	elif nome_bioma_scelto == "Misto Foresta-Altro": 
		# Lancia la moneta: 50% Foresta, 50% Altro
		catalogo_scelto = bioma_foresta if randf() > 0.5 else bioma_altro
		
	else: 
		catalogo_scelto = bioma_altro
		
	if catalogo_scelto.is_empty():
		catalogo_scelto = bioma_campagna
		nome_bioma_scelto += " (FALLBACK)"
		
	print("🌱 Generato: [", nome_bioma_scelto, "] in cella ", pos_griglia)
		
	return catalogo_scelto

# --- IL LASER INTELLIGENTE ---
func _registra_cella_con_laser(pos_griglia: Vector2i) -> bool:
	if scacchiera.has(pos_griglia): return true 
	
	var pos_mondo = Vector3(pos_griglia.x * dimensione_chunk, 0, pos_griglia.y * dimensione_chunk)
	var space_state = get_world_3d().direct_space_state
	var origine_raggio = Vector3(pos_mondo.x, 50.0, pos_mondo.z)
	var fine_raggio = Vector3(pos_mondo.x, -10.0, pos_mondo.z)
	
	var query = PhysicsRayQueryParameters3D.create(origine_raggio, fine_raggio)
	var result = space_state.intersect_ray(query)
	
	if result.size() > 0:
		var collider = result["collider"]
		var nodo_corrente = collider
		var uscite_trovate = {"nord": false, "est": false, "sud": false, "ovest": false}
		
		while nodo_corrente != null and nodo_corrente != get_tree().root:
			if nodo_corrente.has_method("get_uscite_ruotate"):
				var scatti_rotazione = roundi(rad_to_deg(nodo_corrente.global_rotation.y) / -90.0)
				uscite_trovate = nodo_corrente.get_uscite_ruotate(scatti_rotazione)
				break 
			nodo_corrente = nodo_corrente.get_parent()
			
		scacchiera[pos_griglia] = {"tipo": "ostacolo", "uscite": uscite_trovate}
		return true 
		
	return false 

# --- WAVE FUNCTION COLLAPSE ---
func _piazza_bioma_compatibile(pos_griglia: Vector2i) -> void:
	var req_nord = _richiede_connessione(pos_griglia + Vector2i(0, -1), "sud")
	var req_est = _richiede_connessione(pos_griglia + Vector2i(1, 0), "ovest")
	var req_sud = _richiede_connessione(pos_griglia + Vector2i(0, 1), "nord")
	var req_ovest = _richiede_connessione(pos_griglia + Vector2i(-1, 0), "est")
	
	var catalogo_zona = _scegli_catalogo_per_cella(pos_griglia)
	var candidati = []
	
	for scena in catalogo_zona:
		var test_chunk = scena.instantiate()
		
		if test_chunk.has_method("get_uscite_ruotate"):
			for rot in range(4):
				var u = test_chunk.get_uscite_ruotate(rot)
				
				var match_n = (req_nord == -1) or ((req_nord == 1) == u["nord"])
				var match_e = (req_est == -1)  or ((req_est == 1) == u["est"])
				var match_s = (req_sud == -1)  or ((req_sud == 1) == u["sud"])
				var match_o = (req_ovest == -1) or ((req_ovest == 1) == u["ovest"])
				
				if match_n and match_e and match_s and match_o:
					candidati.append({
						"scena": scena,
						"rotazione": rot,
						"uscite": u
					})
					
		test_chunk.queue_free()
		
	if candidati.size() > 0:
		var scelto = candidati.pick_random()
		var nuovo_chunk = scelto.scena.instantiate()
		add_child(nuovo_chunk)
		nuovo_chunk.position = Vector3(pos_griglia.x * dimensione_chunk, 0, pos_griglia.y * dimensione_chunk)
		nuovo_chunk.rotation.y = scelto.rotazione * (-PI / 2.0)
		scacchiera[pos_griglia] = {"tipo": "bioma", "uscite": scelto.uscite}
	else:
		var backup = catalogo_zona[0].instantiate()
		add_child(backup)
		backup.position = Vector3(pos_griglia.x * dimensione_chunk, 0, pos_griglia.y * dimensione_chunk)
		scacchiera[pos_griglia] = {"tipo": "bioma", "uscite": {"nord":false, "est":false, "sud":false, "ovest":false}}

func _richiede_connessione(pos_vicino: Vector2i, lato_richiesto: String) -> int:
	if not scacchiera.has(pos_vicino):
		_registra_cella_con_laser(pos_vicino)
		
	if scacchiera.has(pos_vicino):
		if scacchiera[pos_vicino]["uscite"][lato_richiesto] == true:
			return 1 
		else:
			return 0 
			
	return -1 

# --- DIZIONARIO DEI BIOMI (SOGLIE RIGOROSE PER EVITARE SALTI) ---
func _get_nome_bioma(valore: float) -> String:
	# Convertiamo il valore da [-1.0 a 1.0] a un percorso continuo
	# per assicurare la presenza dei mix
	if valore < -0.4: 
		return "Campagna"
	elif valore < -0.15: 
		return "Misto Campagna-Foresta"
		
	if not usa_terzo_bioma:
		return "Foresta"
		
	if valore < 0.15: 
		return "Foresta"
	elif valore < 0.4: 
		return "Misto Foresta-Altro"
	else: 
		return "Altro (Deserto/Neve)"

# --- IL GPS DEL TRENO ---
func _radar_bioma_treno() -> void:
	if path_treno == null or path_treno.curve == null: return
		
	var pos_treno = path_treno.treno_istanziato.global_position
	var grid_x = roundi(pos_treno.x / dimensione_chunk)
	var grid_z = roundi(pos_treno.z / dimensione_chunk)
	var valore_attuale = generatore_rumore.get_noise_2d(grid_x, grid_z)
	var bioma_attuale = _get_nome_bioma(valore_attuale)
	
	var progresso_attuale = path_treno.progresso_treno
	var lunghezza_totale = path_treno.curve.get_baked_length()
	var distanza_cambio = 0.0
	var prossimo_bioma = ""
	
	for step in range(1, 31):
		var progresso_futuro = wrapf(progresso_attuale + (step * dimensione_chunk), 0.0, lunghezza_totale)
		var pos_futura_locale = path_treno.curve.sample_baked(progresso_futuro, true)
		var pos_futura_globale = path_treno.to_global(pos_futura_locale)
		
		var f_x = roundi(pos_futura_globale.x / dimensione_chunk)
		var f_z = roundi(pos_futura_globale.z / dimensione_chunk)
		var valore_futuro = generatore_rumore.get_noise_2d(f_x, f_z)
		var bioma_futuro = _get_nome_bioma(valore_futuro)
		
		if bioma_futuro != bioma_attuale:
			distanza_cambio = step * dimensione_chunk
			prossimo_bioma = bioma_futuro
			break
			
	if prossimo_bioma != "":
		print("🚂 [GPS TRENO] Sei in: ", bioma_attuale, " | Tra circa ", distanza_cambio, " metri entrerai in: ", prossimo_bioma)
	else:
		print("🚂 [GPS TRENO] Sei in: ", bioma_attuale, " | Nessun cambio in vista per 600m.")
