// lib/model/activity/activity_entry.dart import 'package:aves/model/activity/activity_origins.dart'; class ActivityEntry { final int? id; // 0 = locale, 1000 = remote final int origin; final String uri; final String? relativePath; final String? directoryName; final String fileName; final String fileExtension; final String format; final String? displayName; final String? routeName; final String? title; final String? description; final String? notes; final String? sport; final String? activityType; final String? deviceManufacturer; final String? deviceModel; final String? sourceApp; final int? startTimeMillis; final int? endTimeMillis; final int? durationMillis; final double? distanceMeters; final double? elevationGainMeters; final double? elevationLossMeters; final double? minElevationMeters; final double? maxElevationMeters; final double? fileDistanceMeters; final double? fileElevationGainMeters; final double? computedDistanceMeters; final double? computedElevationGainMeters; final double? averageSpeedMps; final double? maxSpeedMps; final double? averagePaceSecPerKm; final int? averageHeartRate; final int? maxHeartRate; final double? averageCadence; final double? maxCadence; final double? calories; final double? powerAvg; final double? powerMax; final int? pointCount; final int? segmentCount; final int? lapCount; final double? minLatitude; final double? maxLatitude; final double? minLongitude; final double? maxLongitude; final double? centerLatitude; final double? centerLongitude; final int? sizeBytes; final int? lastModifiedMillis; final int dateAddedMillis; final int dateModifiedMillis; final int? lastScanMillis; final bool isFavorite; final bool isHidden; final int? rating; final String? tags; final String scanStatus; final String? scanError; final String? diagnostics; const ActivityEntry({ this.id, this.origin = ActivityOrigins.local, required this.uri, this.relativePath, this.directoryName, required this.fileName, required this.fileExtension, required this.format, this.displayName, this.routeName, this.title, this.description, this.notes, this.sport, this.activityType, this.deviceManufacturer, this.deviceModel, this.sourceApp, this.startTimeMillis, this.endTimeMillis, this.durationMillis, this.distanceMeters, this.elevationGainMeters, this.elevationLossMeters, this.minElevationMeters, this.maxElevationMeters, this.fileDistanceMeters, this.fileElevationGainMeters, this.computedDistanceMeters, this.computedElevationGainMeters, this.averageSpeedMps, this.maxSpeedMps, this.averagePaceSecPerKm, this.averageHeartRate, this.maxHeartRate, this.averageCadence, this.maxCadence, this.calories, this.powerAvg, this.powerMax, this.pointCount, this.segmentCount, this.lapCount, this.minLatitude, this.maxLatitude, this.minLongitude, this.maxLongitude, this.centerLatitude, this.centerLongitude, this.sizeBytes, this.lastModifiedMillis, required this.dateAddedMillis, required this.dateModifiedMillis, this.lastScanMillis, this.isFavorite = false, this.isHidden = false, this.rating, this.tags, this.scanStatus = 'ok', this.scanError, this.diagnostics, }); String get bestName { final customTitle = title?.trim(); if (customTitle != null && customTitle.isNotEmpty) return customTitle; final route = routeName?.trim(); if (route != null && route.isNotEmpty) return route; final display = displayName?.trim(); if (display != null && display.isNotEmpty) return display; return fileName; } static const Object _unset = Object(); ActivityEntry copyWith({ Object? id = _unset, int? origin, String? uri, Object? relativePath = _unset, Object? directoryName = _unset, String? fileName, String? fileExtension, String? format, Object? displayName = _unset, Object? routeName = _unset, Object? title = _unset, Object? description = _unset, Object? notes = _unset, Object? sport = _unset, Object? activityType = _unset, Object? deviceManufacturer = _unset, Object? deviceModel = _unset, Object? sourceApp = _unset, Object? startTimeMillis = _unset, Object? endTimeMillis = _unset, Object? durationMillis = _unset, Object? distanceMeters = _unset, Object? elevationGainMeters = _unset, Object? elevationLossMeters = _unset, Object? minElevationMeters = _unset, Object? maxElevationMeters = _unset, Object? fileDistanceMeters = _unset, Object? fileElevationGainMeters = _unset, Object? computedDistanceMeters = _unset, Object? computedElevationGainMeters = _unset, Object? averageSpeedMps = _unset, Object? maxSpeedMps = _unset, Object? averagePaceSecPerKm = _unset, Object? averageHeartRate = _unset, Object? maxHeartRate = _unset, Object? averageCadence = _unset, Object? maxCadence = _unset, Object? calories = _unset, Object? powerAvg = _unset, Object? powerMax = _unset, Object? pointCount = _unset, Object? segmentCount = _unset, Object? lapCount = _unset, Object? minLatitude = _unset, Object? maxLatitude = _unset, Object? minLongitude = _unset, Object? maxLongitude = _unset, Object? centerLatitude = _unset, Object? centerLongitude = _unset, Object? sizeBytes = _unset, Object? lastModifiedMillis = _unset, int? dateAddedMillis, int? dateModifiedMillis, Object? lastScanMillis = _unset, bool? isFavorite, bool? isHidden, Object? rating = _unset, Object? tags = _unset, String? scanStatus, Object? scanError = _unset, Object? diagnostics = _unset, }) { return ActivityEntry( id: identical(id, _unset) ? this.id : id as int?, origin: origin ?? this.origin, uri: uri ?? this.uri, relativePath: identical(relativePath, _unset) ? this.relativePath : relativePath as String?, directoryName: identical(directoryName, _unset) ? this.directoryName : directoryName as String?, fileName: fileName ?? this.fileName, fileExtension: fileExtension ?? this.fileExtension, format: format ?? this.format, displayName: identical(displayName, _unset) ? this.displayName : displayName as String?, routeName: identical(routeName, _unset) ? this.routeName : routeName as String?, title: identical(title, _unset) ? this.title : title as String?, description: identical(description, _unset) ? this.description : description as String?, notes: identical(notes, _unset) ? this.notes : notes as String?, sport: identical(sport, _unset) ? this.sport : sport as String?, activityType: identical(activityType, _unset) ? this.activityType : activityType as String?, deviceManufacturer: identical(deviceManufacturer, _unset) ? this.deviceManufacturer : deviceManufacturer as String?, deviceModel: identical(deviceModel, _unset) ? this.deviceModel : deviceModel as String?, sourceApp: identical(sourceApp, _unset) ? this.sourceApp : sourceApp as String?, startTimeMillis: identical(startTimeMillis, _unset) ? this.startTimeMillis : startTimeMillis as int?, endTimeMillis: identical(endTimeMillis, _unset) ? this.endTimeMillis : endTimeMillis as int?, durationMillis: identical(durationMillis, _unset) ? this.durationMillis : durationMillis as int?, distanceMeters: identical(distanceMeters, _unset) ? this.distanceMeters : distanceMeters as double?, elevationGainMeters: identical(elevationGainMeters, _unset) ? this.elevationGainMeters : elevationGainMeters as double?, elevationLossMeters: identical(elevationLossMeters, _unset) ? this.elevationLossMeters : elevationLossMeters as double?, minElevationMeters: identical(minElevationMeters, _unset) ? this.minElevationMeters : minElevationMeters as double?, maxElevationMeters: identical(maxElevationMeters, _unset) ? this.maxElevationMeters : maxElevationMeters as double?, fileDistanceMeters: identical(fileDistanceMeters, _unset) ? this.fileDistanceMeters : fileDistanceMeters as double?, fileElevationGainMeters: identical(fileElevationGainMeters, _unset) ? this.fileElevationGainMeters : fileElevationGainMeters as double?, computedDistanceMeters: identical(computedDistanceMeters, _unset) ? this.computedDistanceMeters : computedDistanceMeters as double?, computedElevationGainMeters: identical(computedElevationGainMeters, _unset) ? this.computedElevationGainMeters : computedElevationGainMeters as double?, averageSpeedMps: identical(averageSpeedMps, _unset) ? this.averageSpeedMps : averageSpeedMps as double?, maxSpeedMps: identical(maxSpeedMps, _unset) ? this.maxSpeedMps : maxSpeedMps as double?, averagePaceSecPerKm: identical(averagePaceSecPerKm, _unset) ? this.averagePaceSecPerKm : averagePaceSecPerKm as double?, averageHeartRate: identical(averageHeartRate, _unset) ? this.averageHeartRate : averageHeartRate as int?, maxHeartRate: identical(maxHeartRate, _unset) ? this.maxHeartRate : maxHeartRate as int?, averageCadence: identical(averageCadence, _unset) ? this.averageCadence : averageCadence as double?, maxCadence: identical(maxCadence, _unset) ? this.maxCadence : maxCadence as double?, calories: identical(calories, _unset) ? this.calories : calories as double?, powerAvg: identical(powerAvg, _unset) ? this.powerAvg : powerAvg as double?, powerMax: identical(powerMax, _unset) ? this.powerMax : powerMax as double?, pointCount: identical(pointCount, _unset) ? this.pointCount : pointCount as int?, segmentCount: identical(segmentCount, _unset) ? this.segmentCount : segmentCount as int?, lapCount: identical(lapCount, _unset) ? this.lapCount : lapCount as int?, minLatitude: identical(minLatitude, _unset) ? this.minLatitude : minLatitude as double?, maxLatitude: identical(maxLatitude, _unset) ? this.maxLatitude : maxLatitude as double?, minLongitude: identical(minLongitude, _unset) ? this.minLongitude : minLongitude as double?, maxLongitude: identical(maxLongitude, _unset) ? this.maxLongitude : maxLongitude as double?, centerLatitude: identical(centerLatitude, _unset) ? this.centerLatitude : centerLatitude as double?, centerLongitude: identical(centerLongitude, _unset) ? this.centerLongitude : centerLongitude as double?, sizeBytes: identical(sizeBytes, _unset) ? this.sizeBytes : sizeBytes as int?, lastModifiedMillis: identical(lastModifiedMillis, _unset) ? this.lastModifiedMillis : lastModifiedMillis as int?, dateAddedMillis: dateAddedMillis ?? this.dateAddedMillis, dateModifiedMillis: dateModifiedMillis ?? this.dateModifiedMillis, lastScanMillis: identical(lastScanMillis, _unset) ? this.lastScanMillis : lastScanMillis as int?, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, rating: identical(rating, _unset) ? this.rating : rating as int?, tags: identical(tags, _unset) ? this.tags : tags as String?, scanStatus: scanStatus ?? this.scanStatus, scanError: identical(scanError, _unset) ? this.scanError : scanError as String?, diagnostics: identical(diagnostics, _unset) ? this.diagnostics : diagnostics as String?, ); } Map toMap() { return { 'id': id, 'origin': origin, 'uri': uri, 'relative_path': relativePath, 'directory_name': directoryName, 'file_name': fileName, 'file_extension': fileExtension, 'format': format, 'display_name': displayName, 'route_name': routeName, 'title': title, 'description': description, 'notes': notes, 'sport': sport, 'activity_type': activityType, 'device_manufacturer': deviceManufacturer, 'device_model': deviceModel, 'source_app': sourceApp, 'start_time_millis': startTimeMillis, 'end_time_millis': endTimeMillis, 'duration_millis': durationMillis, 'distance_meters': distanceMeters, 'elevation_gain_meters': elevationGainMeters, 'elevation_loss_meters': elevationLossMeters, 'min_elevation_meters': minElevationMeters, 'max_elevation_meters': maxElevationMeters, 'file_distance_meters': fileDistanceMeters, 'file_elevation_gain_meters': fileElevationGainMeters, 'computed_distance_meters': computedDistanceMeters, 'computed_elevation_gain_meters': computedElevationGainMeters, 'average_speed_mps': averageSpeedMps, 'max_speed_mps': maxSpeedMps, 'average_pace_sec_per_km': averagePaceSecPerKm, 'average_heart_rate': averageHeartRate, 'max_heart_rate': maxHeartRate, 'average_cadence': averageCadence, 'max_cadence': maxCadence, 'calories': calories, 'power_avg': powerAvg, 'power_max': powerMax, 'point_count': pointCount, 'segment_count': segmentCount, 'lap_count': lapCount, 'min_latitude': minLatitude, 'max_latitude': maxLatitude, 'min_longitude': minLongitude, 'max_longitude': maxLongitude, 'center_latitude': centerLatitude, 'center_longitude': centerLongitude, 'size_bytes': sizeBytes, 'last_modified_millis': lastModifiedMillis, 'date_added_millis': dateAddedMillis, 'date_modified_millis': dateModifiedMillis, 'last_scan_millis': lastScanMillis, 'is_favorite': isFavorite ? 1 : 0, 'is_hidden': isHidden ? 1 : 0, 'rating': rating, 'tags': tags, 'scan_status': scanStatus, 'scan_error': scanError, 'diagnostics': diagnostics, }; } static ActivityEntry fromMap(Map map) { return ActivityEntry( id: map['id'] as int?, origin: map['origin'] as int? ?? ActivityOrigins.local, uri: map['uri'] as String, relativePath: map['relative_path'] as String?, directoryName: map['directory_name'] as String?, fileName: map['file_name'] as String, fileExtension: map['file_extension'] as String, format: map['format'] as String, displayName: map['display_name'] as String?, routeName: map['route_name'] as String?, title: map['title'] as String?, description: map['description'] as String?, notes: map['notes'] as String?, sport: map['sport'] as String?, activityType: map['activity_type'] as String?, deviceManufacturer: map['device_manufacturer'] as String?, deviceModel: map['device_model'] as String?, sourceApp: map['source_app'] as String?, startTimeMillis: map['start_time_millis'] as int?, endTimeMillis: map['end_time_millis'] as int?, durationMillis: map['duration_millis'] as int?, distanceMeters: _double(map['distance_meters']), elevationGainMeters: _double(map['elevation_gain_meters']), elevationLossMeters: _double(map['elevation_loss_meters']), minElevationMeters: _double(map['min_elevation_meters']), maxElevationMeters: _double(map['max_elevation_meters']), fileDistanceMeters: _double(map['file_distance_meters']), fileElevationGainMeters: _double(map['file_elevation_gain_meters']), computedDistanceMeters: _double(map['computed_distance_meters']), computedElevationGainMeters: _double(map['computed_elevation_gain_meters']), averageSpeedMps: _double(map['average_speed_mps']), maxSpeedMps: _double(map['max_speed_mps']), averagePaceSecPerKm: _double(map['average_pace_sec_per_km']), averageHeartRate: map['average_heart_rate'] as int?, maxHeartRate: map['max_heart_rate'] as int?, averageCadence: _double(map['average_cadence']), maxCadence: _double(map['max_cadence']), calories: _double(map['calories']), powerAvg: _double(map['power_avg']), powerMax: _double(map['power_max']), pointCount: map['point_count'] as int?, segmentCount: map['segment_count'] as int?, lapCount: map['lap_count'] as int?, minLatitude: _double(map['min_latitude']), maxLatitude: _double(map['max_latitude']), minLongitude: _double(map['min_longitude']), maxLongitude: _double(map['max_longitude']), centerLatitude: _double(map['center_latitude']), centerLongitude: _double(map['center_longitude']), sizeBytes: map['size_bytes'] as int?, lastModifiedMillis: map['last_modified_millis'] as int?, dateAddedMillis: map['date_added_millis'] as int, dateModifiedMillis: map['date_modified_millis'] as int, lastScanMillis: map['last_scan_millis'] as int?, isFavorite: (map['is_favorite'] as int? ?? 0) != 0, isHidden: (map['is_hidden'] as int? ?? 0) != 0, rating: map['rating'] as int?, tags: map['tags'] as String?, scanStatus: map['scan_status'] as String? ?? 'ok', scanError: map['scan_error'] as String?, diagnostics: map['diagnostics'] as String?, ); } static double? _double(Object? value) { if (value == null) return null; if (value is num) return value.toDouble(); return double.tryParse('$value'); } }