import 'dart:convert'; import 'package:drift/drift.dart'; import '../../domain/waypoint/flat_waypoint_list.dart'; import 'app_database.dart'; /// Kopfzeilen-taugliche Zusammenfassung einer gespeicherten Mission, ohne /// die komplette Wegpunktliste zu materialisieren (fuer die Missionsliste /// im Verwaltungsmenue, Doku 3.6/7.1). class MissionSummary { const MissionSummary({ required this.id, required this.name, required this.placeName, required this.waypointCount, required this.updatedAt, }); final int id; final String name; final String? placeName; final int waypointCount; final DateTime updatedAt; } class LoadedMission { const LoadedMission({ required this.name, required this.placeName, required this.waypoints, }); final String name; final String? placeName; final List waypoints; } /// Persistenz-Zugriff fuer Missionen (Doku 4.15): kapselt die Drift-Tabelle /// und die JSON-(De-)Serialisierung der Wegpunktliste - die Domain-Schicht /// (Waypoint) kennt bewusst kein JSON, siehe flat_waypoint_list.dart. class MissionRepository { MissionRepository(this._db); final AppDatabase _db; Stream> watchSummaries() { return _db.select(_db.missions).watch().map((rows) { final summaries = [ for (final row in rows) MissionSummary( id: row.id, name: row.name, placeName: row.location, waypointCount: (jsonDecode(row.waypointsJson) as List).length, updatedAt: row.updatedAt, ), ]; return summaries; }); } Future load(int id) async { final row = await (_db.select(_db.missions) ..where((m) => m.id.equals(id))) .getSingleOrNull(); if (row == null) return null; final decoded = jsonDecode(row.waypointsJson) as List; return LoadedMission( name: row.name, placeName: row.location, waypoints: [ for (final entry in decoded) _waypointFromJson(entry as Map), ], ); } /// Legt eine neue Mission an oder ueberschreibt die bestehende mit [id] - /// analog saveCurrentMission()/Mission.toRecord() im HTML-Demonstrator /// (find-or-insert nach id). Gibt die (ggf. neu vergebene) id zurueck. Future upsert({ int? id, required String name, String? placeName, required List waypoints, }) async { final waypointsJson = jsonEncode([for (final w in waypoints) _waypointToJson(w)]); final companion = MissionsCompanion( name: Value(name), location: Value(placeName), updatedAt: Value(DateTime.now()), waypointsJson: Value(waypointsJson), ); if (id == null) { return _db.into(_db.missions).insert(companion); } final existing = await (_db.select(_db.missions) ..where((m) => m.id.equals(id))) .getSingleOrNull(); if (existing == null) { return _db.into(_db.missions).insert(companion.copyWith(id: Value(id))); } await (_db.update(_db.missions)..where((m) => m.id.equals(id))) .write(companion); return id; } Future rename(int id, String name) async { await (_db.update(_db.missions)..where((m) => m.id.equals(id))).write( MissionsCompanion(name: Value(name), updatedAt: Value(DateTime.now())), ); } Future delete(int id) async { await (_db.delete(_db.missions)..where((m) => m.id.equals(id))).go(); } Map _waypointToJson(Waypoint w) => { 'lat': w.lat, 'lon': w.lon, 'altitudeM': w.altitudeM, 'speedMs': w.speedMs, 'catchRadiusM': w.catchRadiusM, 'action': w.action.name, 'jumpTargetIndex': w.jumpTargetIndex, 'jumpRepeatCount': w.jumpRepeatCount, 'windSpeedMs': w.windSpeedMs, 'windDirFromDeg': w.windDirFromDeg, 'windElevationM': w.windElevationM, }; Waypoint _waypointFromJson(Map json) => Waypoint( lat: (json['lat'] as num).toDouble(), lon: (json['lon'] as num).toDouble(), altitudeM: (json['altitudeM'] as num).toDouble(), speedMs: (json['speedMs'] as num).toDouble(), catchRadiusM: (json['catchRadiusM'] as num).toDouble(), action: WaypointAction.values.firstWhere( (a) => a.name == json['action'], orElse: () => WaypointAction.none, ), jumpTargetIndex: json['jumpTargetIndex'] as int?, jumpRepeatCount: json['jumpRepeatCount'] as int?, windSpeedMs: (json['windSpeedMs'] as num?)?.toDouble(), windDirFromDeg: (json['windDirFromDeg'] as num?)?.toDouble(), windElevationM: (json['windElevationM'] as num?)?.toDouble(), ); }