/// A question in a practice deck. class Question { /// Unique identifier for this question. final String id; /// The question prompt. final String prompt; /// List of possible answers. final List answers; /// Indices of correct answers in [answers]. /// For backward compatibility, if empty, falls back to [correctAnswerIndex] (deprecated). final List correctAnswerIndices; /// Deprecated: Use [correctAnswerIndices] instead. /// Kept for backward compatibility with existing data. @Deprecated('Use correctAnswerIndices instead') final int? correctAnswerIndex; /// Number of consecutive correct answers. final int consecutiveCorrect; /// Whether this question is considered "known". final bool isKnown; /// Priority points (higher = more likely to be selected). final int priorityPoints; /// The last attempt index where this question was seen. final int lastAttemptIndex; /// Total number of correct attempts. final int totalCorrectAttempts; /// Total number of attempts. final int totalAttempts; Question({ required this.id, required this.prompt, required this.answers, List? correctAnswerIndices, @Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex, this.consecutiveCorrect = 0, this.isKnown = false, this.priorityPoints = 0, this.lastAttemptIndex = -1, this.totalCorrectAttempts = 0, this.totalAttempts = 0, }) : correctAnswerIndices = correctAnswerIndices ?? (correctAnswerIndex != null ? [correctAnswerIndex] : const []), correctAnswerIndex = correctAnswerIndex; /// Creates a copy of this question with the given fields replaced. Question copyWith({ String? id, String? prompt, List? answers, List? correctAnswerIndices, @Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex, int? consecutiveCorrect, bool? isKnown, int? priorityPoints, int? lastAttemptIndex, int? totalCorrectAttempts, int? totalAttempts, }) { return Question( id: id ?? this.id, prompt: prompt ?? this.prompt, answers: answers ?? this.answers, correctAnswerIndices: correctAnswerIndices ?? this.correctAnswerIndices, correctAnswerIndex: correctAnswerIndex ?? this.correctAnswerIndex, consecutiveCorrect: consecutiveCorrect ?? this.consecutiveCorrect, isKnown: isKnown ?? this.isKnown, priorityPoints: priorityPoints ?? this.priorityPoints, lastAttemptIndex: lastAttemptIndex ?? this.lastAttemptIndex, totalCorrectAttempts: totalCorrectAttempts ?? this.totalCorrectAttempts, totalAttempts: totalAttempts ?? this.totalAttempts, ); } /// Gets the correct answer indices, with backward compatibility. List get correctIndices { if (correctAnswerIndices.isNotEmpty) { return correctAnswerIndices; } // Backward compatibility if (correctAnswerIndex != null) { return [correctAnswerIndex!]; } return []; } /// Checks if an answer index is correct. bool isCorrectAnswer(int index) { return correctIndices.contains(index); } /// Whether this question has multiple correct answers. bool get hasMultipleCorrectAnswers => correctIndices.length > 1; /// Validates that priorityPoints is non-negative. Question withPriorityPoints(int points) { return copyWith(priorityPoints: points < 0 ? 0 : points); } @override bool operator ==(Object other) => identical(this, other) || other is Question && runtimeType == other.runtimeType && id == other.id && prompt == other.prompt && answers.toString() == other.answers.toString() && correctAnswerIndices.toString() == other.correctAnswerIndices.toString() && consecutiveCorrect == other.consecutiveCorrect && isKnown == other.isKnown && priorityPoints == other.priorityPoints && lastAttemptIndex == other.lastAttemptIndex && totalCorrectAttempts == other.totalCorrectAttempts && totalAttempts == other.totalAttempts; @override int get hashCode => id.hashCode ^ prompt.hashCode ^ answers.hashCode ^ correctAnswerIndices.hashCode ^ consecutiveCorrect.hashCode ^ isKnown.hashCode ^ priorityPoints.hashCode ^ lastAttemptIndex.hashCode ^ totalCorrectAttempts.hashCode ^ totalAttempts.hashCode; }