import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show rootBundle; import '../models/tradition_model.dart'; class TraditionsRepository { static final TraditionsRepository _instance = TraditionsRepository._internal(); factory TraditionsRepository() => _instance; TraditionsRepository._internal(); List _traditions = const []; bool _loaded = false; List get traditions => List.unmodifiable(_traditions); bool get isLoaded => _loaded; static const Map filterToCanonId = { 'ᚱ Руны': 'runes', '᚛ Огам': 'ogham', '✡️ Каббала': 'kabbalah', '⚗️ Алхимия': 'alchemy', '🎴 Таро': 'tarot', '🪶 Ленорман': 'lenormand', '☯️ И Цзин': 'iching', '⌛ Геомантия': 'geomancy', '♈ Гороскоп': 'western_zodiac', '🐉 Китайский зодиак': 'zodiac', '🌌 Космология': 'cosmology', '📅 Дни недели': 'days_of_week', '🗓️ Месяцы года': 'months_of_year', '🔢 Нумерология': 'numerology', }; TraditionCanon? byId(String id) { for (final t in _traditions) { if (t.id == id) return t; } return null; } /// Finds all stations across traditions that relate to this word (either as primary station or in cluster) List<({TraditionCanon tradition, TraditionStation station, bool isPrimary})> findMatchesForWord(String word, {int? wordId}) { final cleanWord = word.trim().toLowerCase(); final results = <({TraditionCanon tradition, TraditionStation station, bool isPrimary})>[]; for (final tradition in _traditions) { for (final station in tradition.stations) { if (station.primary != null && (station.primary!.word.trim().toLowerCase() == cleanWord || (wordId != null && station.primary!.id == wordId))) { results.add((tradition: tradition, station: station, isPrimary: true)); } else if (station.cluster.any((cw) => cw.word.trim().toLowerCase() == cleanWord || (wordId != null && cw.id == wordId))) { results.add((tradition: tradition, station: station, isPrimary: false)); } } } return results; } Future init() async { if (_loaded && _traditions.isNotEmpty) return; Map? decoded; final cwd = Directory.current.path; final candidates = { 'assets/data/traditions_canon.json', '../assets/data/traditions_canon.json', '$cwd/assets/data/traditions_canon.json', '$cwd/../assets/data/traditions_canon.json', }; for (final path in candidates) { try { final file = File(path); if (file.existsSync()) { decoded = json.decode(file.readAsStringSync()) as Map; break; } } catch (e) { debugPrint('traditions_canon.json skip $path: $e'); } } if (decoded == null) { try { final jsonStr = await rootBundle.loadString('assets/data/traditions_canon.json'); decoded = json.decode(jsonStr) as Map; } catch (e) { debugPrint('Could not load traditions_canon.json: $e'); } } if (decoded == null) { _traditions = const []; _loaded = true; return; } _traditions = decoded.values .whereType>() .map(TraditionCanon.fromMap) .toList(); _loaded = true; } }