import 'package:latlong2/latlong.dart'; import '../models/sar_marker.dart'; import '../models/message.dart'; /// Parser for SAR (Search & Rescue) special messages /// Format: S::, /// Examples: /// S:🧑:37.7749,-122.4194 /// S:🔥:40.7128,-74.0060 /// S:🏕️:34.0522,-118.2437 class SarMessageParser { static final RegExp _sarPattern = RegExp( r'^S:(.):(-?\d+\.?\d*),(-?\d+\.?\d*)$', multiLine: false, ); /// Check if a message is a SAR marker message static bool isSarMessage(String text) { return text.trim().startsWith('S:') && _sarPattern.hasMatch(text.trim()); } /// Parse a SAR message and extract marker information /// Returns null if the message is not a valid SAR message static SarMarkerInfo? parse(String text) { final trimmed = text.trim(); if (!trimmed.startsWith('S:')) return null; final match = _sarPattern.firstMatch(trimmed); if (match == null) return null; try { final emoji = match.group(1)!; final latitude = double.parse(match.group(2)!); final longitude = double.parse(match.group(3)!); // Validate coordinates if (latitude < -90 || latitude > 90) return null; if (longitude < -180 || longitude > 180) return null; final markerType = SarMarkerType.fromEmoji(emoji); final location = LatLng(latitude, longitude); return SarMarkerInfo( type: markerType, location: location, emoji: emoji, ); } catch (e) { return null; } } /// Enhance a Message with SAR marker information static Message enhanceMessage(Message message) { final sarInfo = parse(message.text); if (sarInfo == null) return message; return message.copyWith( isSarMarker: true, sarMarkerType: sarInfo.type, sarGpsCoordinates: sarInfo.location, ); } /// Create a SAR marker message text static String createSarMessage({ required SarMarkerType type, required LatLng location, String? notes, }) { final text = 'S:${type.emoji}:${location.latitude},${location.longitude}'; if (notes != null && notes.isNotEmpty) { return '$text\n$notes'; } return text; } /// Extract additional notes from SAR message (text after the marker) static String? extractNotes(String text) { final trimmed = text.trim(); final lines = trimmed.split('\n'); if (lines.length <= 1) return null; // Everything after the first line is considered notes return lines.sublist(1).join('\n').trim(); } /// Validate SAR message format static bool isValidFormat(String text) { return isSarMessage(text) && parse(text) != null; } /// Get a user-friendly error message for invalid SAR format static String? getFormatError(String text) { if (!text.trim().startsWith('S:')) { return 'SAR message must start with "S:"'; } final parts = text.trim().split(':'); if (parts.length < 3) { return 'Invalid format. Use: S::,'; } final emoji = parts[1]; if (emoji.isEmpty) { return 'Missing emoji marker (🧑, 🔥, or 🏕️)'; } final coords = parts[2]; if (!coords.contains(',')) { return 'Coordinates must be separated by comma'; } final coordParts = coords.split(','); if (coordParts.length != 2) { return 'Invalid coordinates format'; } try { final lat = double.parse(coordParts[0]); final lon = double.parse(coordParts[1]); if (lat < -90 || lat > 90) { return 'Latitude must be between -90 and 90'; } if (lon < -180 || lon > 180) { return 'Longitude must be between -180 and 180'; } } catch (e) { return 'Invalid coordinate values'; } return null; } } /// Parsed SAR marker information class SarMarkerInfo { final SarMarkerType type; final LatLng location; final String emoji; SarMarkerInfo({ required this.type, required this.location, required this.emoji, }); @override String toString() { return 'SarMarkerInfo(type: ${type.displayName}, location: $location)'; } }