import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../datalog/datalog_db.dart'; import '../datalog/datalog_player.dart'; import '../datalog/datalog_recorder.dart'; import '../models/datalog_session.dart'; import 'bt_provider.dart'; import 'settings_provider.dart'; // ─── Singletons ─────────────────────────────────────────────────────────────── final datalogDbProvider = Provider((ref) { final db = DatalogDb(); ref.onDispose(db.close); return db; }); final datalogRecorderProvider = Provider((ref) { final recorder = DatalogRecorder(ref.watch(datalogDbProvider)); ref.onDispose(recorder.dispose); return recorder; }); final datalogPlayerProvider = Provider((ref) { final player = DatalogPlayer(ref.watch(datalogDbProvider)); ref.onDispose(player.dispose); return player; }); // ─── Recording state ────────────────────────────────────────────────────────── enum RecordingStatus { idle, recording } class RecordingState { final RecordingStatus status; final int frameCount; final int? sessionId; const RecordingState({ this.status = RecordingStatus.idle, this.frameCount = 0, this.sessionId, }); bool get isRecording => status == RecordingStatus.recording; RecordingState copyWith({ RecordingStatus? status, int? frameCount, int? sessionId, }) => RecordingState( status: status ?? this.status, frameCount: frameCount ?? this.frameCount, sessionId: sessionId ?? this.sessionId, ); } class RecordingNotifier extends StateNotifier { final Ref _ref; RecordingNotifier(this._ref) : super(const RecordingState()); Future startRecording() async { if (state.isRecording) return; final recorder = _ref.read(datalogRecorderProvider); final btNotifier = _ref.read(btProvider.notifier); final ecuType = _ref.read(settingsProvider).ecuType.name; await recorder.start(btNotifier.frameStream, ecuType); state = RecordingState( status: RecordingStatus.recording, sessionId: recorder.sessionId, ); } Future stopRecording() async { if (!state.isRecording) return null; final recorder = _ref.read(datalogRecorderProvider); final session = await recorder.stop(); state = const RecordingState(); // Refresh session list _ref.invalidate(sessionListProvider); return session; } /// Called periodically from UI to update live frame count display. void syncFrameCount() { if (!state.isRecording) return; final count = _ref.read(datalogRecorderProvider).frameCount; if (count != state.frameCount) { state = state.copyWith(frameCount: count); } } } final recordingProvider = StateNotifierProvider( (ref) => RecordingNotifier(ref), ); // ─── Session list ───────────────────────────────────────────────────────────── final sessionListProvider = FutureProvider>((ref) { return ref.watch(datalogDbProvider).getSessions(); }); // ─── Playback state ─────────────────────────────────────────────────────────── class PlaybackState { final bool isActive; final int? sessionId; final int position; final int total; final double speed; final PlaybackStatus status; const PlaybackState({ this.isActive = false, this.sessionId, this.position = 0, this.total = 0, this.speed = 1.0, this.status = PlaybackStatus.idle, }); double get progress => total == 0 ? 0 : position / total; PlaybackState copyWith({ bool? isActive, int? sessionId, int? position, int? total, double? speed, PlaybackStatus? status, }) => PlaybackState( isActive: isActive ?? this.isActive, sessionId: sessionId ?? this.sessionId, position: position ?? this.position, total: total ?? this.total, speed: speed ?? this.speed, status: status ?? this.status, ); } class PlaybackNotifier extends StateNotifier { final Ref _ref; PlaybackNotifier(this._ref) : super(const PlaybackState()); Future loadSession(int sessionId) async { final player = _ref.read(datalogPlayerProvider); await player.load(sessionId); player.onProgress = (pos, total) { state = state.copyWith( position: pos, total: total, status: player.status, ); }; state = PlaybackState( isActive: true, sessionId: sessionId, total: player.totalFrames, speed: player.speed, status: PlaybackStatus.idle, ); } void play() { _ref.read(datalogPlayerProvider).play(); state = state.copyWith(status: PlaybackStatus.playing); } void pause() { _ref.read(datalogPlayerProvider).pause(); state = state.copyWith(status: PlaybackStatus.paused); } Future stop() async { await _ref.read(datalogPlayerProvider).stop(); state = const PlaybackState(); } void seek(int index) { _ref.read(datalogPlayerProvider).seek(index); state = state.copyWith(position: index); } void setSpeed(double speed) { _ref.read(datalogPlayerProvider).speed = speed; state = state.copyWith(speed: speed); } } final playbackProvider = StateNotifierProvider( (ref) => PlaybackNotifier(ref), );