import 'package:isar/isar.dart'; part 'point_record.g.dart'; enum PointRecordStatus { draft, completed, } @embedded class HitPoint { HitPoint({ this.arrowIndex = 0, this.endIndex = 0, this.normalizedX = 0, this.normalizedY = 0, this.score, this.isMiss = false, }); int arrowIndex; int endIndex; /// Range: -1.0 ~ 1.0. The painter can map the normalized coordinate /// directly onto different target canvases. double normalizedX; double normalizedY; int? score; bool isMiss; } @collection class PointRecord { PointRecord(); Id id = Isar.autoIncrement; @Index(type: IndexType.value) late DateTime updatedAt; @Index(type: IndexType.value) late DateTime createdAt; @Index(type: IndexType.value) DateTime? completedAt; @enumerated PointRecordStatus status = PointRecordStatus.draft; /// Matches [BowOption.id] from point book config. @Index(type: IndexType.value) int bowOptionId = 1; late String bowName; /// Matches [TargetOption.id] from point book config. @Index(type: IndexType.value) int targetOptionId = 1; late String targetName; late String title; int distanceMeters = 18; int endCount = 6; int arrowsPerEnd = 6; String? note; List scores = []; List hitPoints = []; bool get isDraft => status == PointRecordStatus.draft; int get totalArrows => scores.length; int get totalScore => scores.fold(0, (sum, item) => sum + item); double? get averageScore => scores.isEmpty ? null : totalScore / scores.length; factory PointRecord.createDraft({ required int bowOptionId, required String bowName, required int targetOptionId, required String targetName, required int distanceMeters, required int endCount, required int arrowsPerEnd, String? title, DateTime? now, }) { final current = now ?? DateTime.now(); return PointRecord() ..title = title ?? '未完成记分' ..status = PointRecordStatus.draft ..bowOptionId = bowOptionId ..bowName = bowName ..targetOptionId = targetOptionId ..targetName = targetName ..distanceMeters = distanceMeters ..endCount = endCount ..arrowsPerEnd = arrowsPerEnd ..createdAt = current ..updatedAt = current; } PointRecord markCompleted({DateTime? completedAt}) { final current = completedAt ?? DateTime.now(); return PointRecord() ..id = id ..title = title ..status = PointRecordStatus.completed ..bowOptionId = bowOptionId ..bowName = bowName ..targetOptionId = targetOptionId ..targetName = targetName ..distanceMeters = distanceMeters ..endCount = endCount ..arrowsPerEnd = arrowsPerEnd ..note = note ..scores = List.from(scores) ..hitPoints = List.from(hitPoints) ..createdAt = createdAt ..updatedAt = current ..completedAt = current; } /// Returns a draft copy with updated scores/hit points (used while editing). PointRecord copyWithScores(List scores, List hitPoints) { return PointRecord() ..id = id ..title = title ..status = status ..bowOptionId = bowOptionId ..bowName = bowName ..targetOptionId = targetOptionId ..targetName = targetName ..distanceMeters = distanceMeters ..endCount = endCount ..arrowsPerEnd = arrowsPerEnd ..note = note ..scores = List.from(scores) ..hitPoints = List.from(hitPoints) ..createdAt = createdAt ..updatedAt = DateTime.now() ..completedAt = completedAt; } }