/// Metadata for a motorcycle photograph or reference image class MotorcycleImageReference { final String? assetPath; final String? networkUrl; final String? localFilePath; final String caption; final String attribution; final bool isPhoto; const MotorcycleImageReference({ this.assetPath, this.networkUrl, this.localFilePath, required this.caption, this.attribution = 'Technical Reference', this.isPhoto = false, }); bool get hasImage => assetPath != null || networkUrl != null || localFilePath != null; } /// Metadata descriptor for registered motorcycle reference images class MotorcycleImageDescriptor { final String make; final String model; final int? exactYear; final int? minYear; final int? maxYear; final List modelAliases; final String assetPath; final String caption; final String attribution; final bool isPhoto; const MotorcycleImageDescriptor({ required this.make, required this.model, this.exactYear, this.minYear, this.maxYear, this.modelAliases = const [], required this.assetPath, required this.caption, this.attribution = 'Factory Technical Reference', this.isPhoto = true, }); /// Normalizes vehicle names for invariant comparison static String normalize(String input) { return input.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), ''); } /// Evaluates whether this descriptor matches the specified make, model, and year bool matches(String targetMake, String targetModel, int? targetYear) { final normTargetMake = normalize(targetMake); final normMake = normalize(make); if (normMake != normTargetMake) return false; final normTargetModel = normalize(targetModel); final normModel = normalize(model); final isModelMatch = normModel == normTargetModel || modelAliases.any((alias) => normalize(alias) == normTargetModel); if (!isModelMatch) return false; // Exact year check if (exactYear != null) { return targetYear == exactYear; } // Generation / Year range check if (minYear != null || maxYear != null) { if (targetYear == null) return false; if (minYear != null && targetYear < minYear!) return false; if (maxYear != null && targetYear > maxYear!) return false; return true; } return true; } MotorcycleImageReference toReference() { return MotorcycleImageReference( assetPath: assetPath, caption: caption, attribution: attribution, isPhoto: isPhoto, ); } } /// Central extensible repository providing authentic motorcycle reference images class MotorcycleImageService { static const MotorcycleImageService _instance = MotorcycleImageService._internal(); factory MotorcycleImageService() => _instance; const MotorcycleImageService._internal(); /// List of verified physical photographic assets bundled in the APK static const List bundledPhotographicAssets = [ 'assets/images/motorcycles/yamaha_yzf_r6_2001.png', ]; /// Default registry of curated motorcycle reference descriptors static final List _defaultDescriptors = [ // Tier 1 & 2: 1st Gen Yamaha YZF-R6 (1999-2002 5EB) including 2001 field test vehicle const MotorcycleImageDescriptor( make: 'Yamaha', model: 'YZF-R6', minYear: 1999, maxYear: 2002, modelAliases: ['R6', 'YZFR6', 'YZF R6'], assetPath: 'assets/images/motorcycles/yamaha_yzf_r6_2001.png', caption: '2001 Yamaha YZF-R6 (5EB)', attribution: 'Factory Supersport Reference', isPhoto: true, ), ]; static final List _customDescriptors = []; /// Registers a custom image descriptor (for extensions/plugins/tests) static void registerDescriptor(MotorcycleImageDescriptor descriptor) { _customDescriptors.add(descriptor); } /// Clears custom descriptors (resets to default registry) static void resetRegistry() { _customDescriptors.clear(); } /// Returns all active descriptors in evaluation order (custom first, then defaults) List get allDescriptors => [ ..._customDescriptors, ..._defaultDescriptors, ]; /// Resolves the best available photographic image or reference for a motorcycle /// /// Matching Hierarchy: /// 1. Exact Year Match (Make + Model + Exact Year) /// 2. Generation / Year-Range Match (Make + Model + Generation Range) /// 3. Make + Model Match (Ageless / General Model Reference) /// 4. Dynamic Technical Schematic Fallback (Accurate make/model/year vector card) MotorcycleImageReference getReferenceImage({ required String? make, required String? model, int? year, }) { if (make == null || model == null || make.trim().isEmpty || model.trim().isEmpty) { return const MotorcycleImageReference( caption: 'Motorcycle Technical Reference', attribution: 'Schematic Vector', isPhoto: false, ); } final descriptors = allDescriptors; // Tier 1: Exact Year Match if (year != null) { for (final desc in descriptors) { if (desc.exactYear != null && desc.matches(make, model, year)) { return desc.toReference(); } } } // Tier 2: Generation / Year-Range Match (e.g. 1999-2002 1st Gen R6) if (year != null) { for (final desc in descriptors) { if ((desc.minYear != null || desc.maxYear != null) && desc.matches(make, model, year)) { return desc.toReference(); } } } // Tier 3: Make + Model Match (Ageless / General Model Reference) for (final desc in descriptors) { if (desc.exactYear == null && desc.minYear == null && desc.maxYear == null && desc.matches(make, model, year)) { return desc.toReference(); } } // Tier 4: Graceful Technical Schematic Fallback final yearPrefix = year != null && year > 1900 ? '$year ' : ''; final formattedCaption = '$yearPrefix${make.trim()} ${model.trim()}'.trim(); return MotorcycleImageReference( caption: formattedCaption, attribution: 'Technical Schematic', isPhoto: false, ); } /// Checks whether a genuine photographic asset is registered and available for a given vehicle bool hasPhotographicAsset({ required String? make, required String? model, int? year, }) { final ref = getReferenceImage(make: make, model: model, year: year); return ref.isPhoto && ref.assetPath != null; } }