import 'dart:convert'; import 'dart:math'; import 'package:flutter/services.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; /// Categorías disponibles en el banco de palabras. class BancoPalabras { final Map> categorias; final Map pistasPorCategoria; BancoPalabras(this.categorias, {Map? pistasPorCategoria}) : pistasPorCategoria = pistasPorCategoria ?? {}; static final Map _instancias = {}; static Future cargar({String idioma = 'es'}) async { if (_instancias.containsKey(idioma)) return _instancias[idioma]!; String jsonStr; try { jsonStr = await rootBundle.loadString( 'assets/words/palabras_$idioma.json', ); } catch (_) { try { final archivoLegacy = idioma == 'es' ? 'assets/palabras.json' : 'assets/palabras_$idioma.json'; jsonStr = await rootBundle.loadString(archivoLegacy); } catch (_) { if (idioma != 'es') return cargar(idioma: 'es'); rethrow; } } final data = json.decode(jsonStr) as Map; final cats = data['categorias'] as Map; final mapa = >{}; final pistas = {}; for (final entrada in cats.entries) { final valor = entrada.value; if (valor is Map) { mapa[entrada.key] = List.from(valor['palabras'] as List); final pista = valor['pista']; if (pista is String && pista.isNotEmpty) pistas[entrada.key] = pista; } else { mapa[entrada.key] = List.from(valor as List); } } _instancias[idioma] = BancoPalabras(mapa, pistasPorCategoria: pistas); return _instancias[idioma]!; } List get nombresCategorias => categorias.keys.toList(); /// Obtiene una palabra aleatoria de la categoría dada (o de todas si es null). String palabraAleatoria(String? categoria) { final rng = Random(); if (categoria == null || categoria == 'todas') { final todasPalabras = categorias.values.expand((l) => l).toList(); return todasPalabras[rng.nextInt(todasPalabras.length)]; } final lista = categorias[categoria]!; return lista[rng.nextInt(lista.length)]; } /// Devuelve la categoría a la que pertenece una palabra. String? categoriaDepalabra(String palabra) { for (final entrada in categorias.entries) { if (entrada.value.contains(palabra)) return entrada.key; } return null; } /// Devuelve la pista localizada de una categoría si el banco la trae. String? pistaDeCategoria(String categoria) => pistasPorCategoria[categoria]; /// Devuelve el nombre localizado de la categoría usando AppLocalizations. static String nombreBonitoCategoria(String clave, [AppLocalizations? l10n]) { if (l10n != null) { final nombres = { 'todas': l10n.categoryAll, 'animales': l10n.categoryAnimals, 'comida': l10n.categoryFood, 'paises': l10n.categoryCountries, 'deportes': l10n.categorySports, 'profesiones': l10n.categoryProfessions, 'objetos': l10n.categoryObjects, 'lugares': l10n.categoryPlaces, 'peliculas': l10n.categoryMovies, 'musica': l10n.categoryMusic, 'tecnologia': l10n.categoryTechnology, }; return nombres[clave] ?? clave; } const nombres = { 'todas': 'Todas', 'animales': 'Animales', 'comida': 'Comida', 'paises': 'Países', 'deportes': 'Deportes', 'profesiones': 'Profesiones', 'objetos': 'Objetos', 'lugares': 'Lugares', 'peliculas': 'Películas', 'musica': 'Música', 'tecnologia': 'Tecnología', }; return nombres[clave] ?? clave; } } class EntradaPalabraTraducida { final String palabra; final String pista; const EntradaPalabraTraducida({required this.palabra, required this.pista}); } class BancoPalabrasTraducidas { final Map> categorias; const BancoPalabrasTraducidas(this.categorias); static final Map _instancias = {}; static Future cargar({String idioma = 'es'}) async { if (_instancias.containsKey(idioma)) return _instancias[idioma]!; final banco = await BancoPalabras.cargar(idioma: idioma); final mapa = >{}; for (final categoria in banco.categorias.entries) { final pista = banco.pistaDeCategoria(categoria.key) ?? categoria.key; mapa[categoria.key] = categoria.value .map((palabra) => EntradaPalabraTraducida(palabra: palabra, pista: pista)) .toList(); } _instancias[idioma] = BancoPalabrasTraducidas(mapa); return _instancias[idioma]!; } }