import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:mirror_protocol/mirror_protocol.dart'; import 'package:mirror_ui/mirror_ui.dart'; import '../providers/display_state_provider.dart'; import '../providers/player_provider.dart'; class WorkoutScreen extends ConsumerWidget { const WorkoutScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final workout = ref.watch(displayStateProvider.select((s) => s.workout)); final program = ref.watch(displayStateProvider.select((s) => s.program)); final playback = ref.watch(displayStateProvider.select((s) => s.playback)); // Program session takes priority if active if (program != null && program.phase != 'completed') { return _buildProgramSession(context, ref, program, playback); } if (workout == null || workout.phase == WorkoutPhase.idle) { return _buildIdle(context); } return switch (workout.phase) { WorkoutPhase.browsing => _buildBrowsing(context), WorkoutPhase.playing => _buildPlaying(context, workout, playback), WorkoutPhase.resting => _buildResting(context, workout), WorkoutPhase.completed => _buildCompleted(context), _ => _buildIdle(context), }; } // --- Program session rendering --- Widget _buildProgramSession( BuildContext context, WidgetRef ref, ProgramSessionState program, PlaybackState? playback, ) { return switch (program.phase) { 'playing' => _buildProgramVideo(context, ref, program, playback), 'youtube' => _buildProgramYouTube(context, program), 'instruction' => _buildProgramInstruction(context, program), _ => _buildCompleted(context), }; } Widget _buildProgramVideo( BuildContext context, WidgetRef ref, ProgramSessionState program, PlaybackState? playback, ) { if (playback == null) return _buildIdle(context); final progress = playback.duration.inMilliseconds > 0 ? playback.position.inMilliseconds / playback.duration.inMilliseconds : 0.0; return Container( color: Colors.black, width: double.infinity, height: double.infinity, child: Stack( children: [ Consumer( builder: (context, ref, _) { final controller = ref.watch(videoControllerProvider); return Video(controller: controller); }, ), if (playback.isPlaying == false) const Center( child: Icon(Icons.pause_circle_filled, size: 120, color: Colors.white70), ), // Program info top-right Positioned( top: 30, right: 30, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( program.programName, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: MirrorTheme.accent, ), ), const SizedBox(height: 4), Text( 'Set ${program.currentSet}/${program.totalSets}', style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), ), // Bottom overlay with title and progress Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(40), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black87], ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( playback.title, style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 12), ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress, minHeight: 4, backgroundColor: Colors.white24, color: MirrorTheme.accent, ), ), const SizedBox(height: 8), Text( '${_formatDuration(playback.position)} / ${_formatDuration(playback.duration)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), const SizedBox(height: 12), _buildProgramProgressBar(context, program), ], ), ), ), ], ), ); } Widget _buildProgramYouTube(BuildContext context, ProgramSessionState program) { return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Stack( children: [ Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 80), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.smart_display, size: 64, color: MirrorTheme.accent), const SizedBox(height: 24), Text( program.workoutName, style: Theme.of(context).textTheme.headlineSmall?.copyWith( color: MirrorTheme.textSecondary, ), textAlign: TextAlign.center, ), const SizedBox(height: 48), TimerDisplay(seconds: program.timerRemaining), const SizedBox(height: 32), Text( 'Set ${program.currentSet}/${program.totalSets}', style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: MirrorTheme.textPrimary, ), ), const SizedBox(height: 16), Text( 'Follow along with YouTube reference', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), ], ), ), ), // Program progress at bottom Positioned( bottom: 40, left: 40, right: 40, child: _buildProgramProgressBar(context, program), ), ], ), ); } Widget _buildProgramInstruction(BuildContext context, ProgramSessionState program) { return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Stack( children: [ Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 80), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( program.workoutName, style: Theme.of(context).textTheme.headlineSmall?.copyWith( color: MirrorTheme.textSecondary, ), textAlign: TextAlign.center, ), const SizedBox(height: 48), TimerDisplay(seconds: program.timerRemaining), const SizedBox(height: 32), Text( 'Set ${program.currentSet}/${program.totalSets}', style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: MirrorTheme.textPrimary, ), ), const SizedBox(height: 16), Text( '${program.currentExerciseIndex + 1} of ${program.exercises.length}', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), ], ), ), ), // Program progress at bottom Positioned( bottom: 40, left: 40, right: 40, child: _buildProgramProgressBar(context, program), ), ], ), ); } Widget _buildProgramProgressBar(BuildContext context, ProgramSessionState program) { final total = program.exercises.length; if (total == 0) return const SizedBox.shrink(); return ClipRRect( borderRadius: BorderRadius.circular(4), child: SizedBox( height: 8, child: Row( children: List.generate(total, (i) { final Color color; if (i < program.currentExerciseIndex) { color = MirrorTheme.success; // completed } else if (i == program.currentExerciseIndex) { color = MirrorTheme.accent; // current } else { color = MirrorTheme.surfaceLight; // remaining } return Expanded( child: Container( margin: EdgeInsets.only(right: i < total - 1 ? 2 : 0), color: color, ), ); }), ), ), ); } // --- Standard workout rendering --- Widget _buildIdle(BuildContext context) { return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.fitness_center, size: 64, color: MirrorTheme.textMuted), const SizedBox(height: 24), Text( 'Browse workouts on your phone', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), const SizedBox(height: 8), Text( 'mirror.local:9090', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: MirrorTheme.accent, ), ), ], ), ), ); } Widget _buildBrowsing(BuildContext context) { return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.phone_android, size: 48, color: MirrorTheme.accent), const SizedBox(height: 24), Text( 'Select a workout on your phone', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), ); } Widget _buildPlaying(BuildContext context, WorkoutSessionState workout, PlaybackState? playback) { if (playback == null) return _buildIdle(context); final progress = playback.duration.inMilliseconds > 0 ? playback.position.inMilliseconds / playback.duration.inMilliseconds : 0.0; return Container( color: Colors.black, width: double.infinity, height: double.infinity, child: Stack( children: [ // Real video output Consumer( builder: (context, ref, _) { final controller = ref.watch(videoControllerProvider); return Video(controller: controller); }, ), // Paused indicator if (!playback.isPlaying) const Center( child: Icon(Icons.pause_circle_filled, size: 120, color: Colors.white70), ), // Queue indicator if (workout.queue.length > 1) Positioned( top: 30, right: 30, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: Text( '${workout.currentIndex + 1} / ${workout.queue.length}', style: Theme.of(context).textTheme.bodyMedium, ), ), ), // Bottom overlay Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(40), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black87], ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( playback.title, style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 12), ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress, minHeight: 4, backgroundColor: Colors.white24, color: MirrorTheme.accent, ), ), const SizedBox(height: 8), Text( '${_formatDuration(playback.position)} / ${_formatDuration(playback.duration)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), ], ), ), ), ], ), ); } Widget _buildResting(BuildContext context, WorkoutSessionState workout) { final nextTitle = workout.currentIndex < workout.queue.length ? workout.queue[workout.currentIndex].title : ''; return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'REST', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: MirrorTheme.accent, letterSpacing: 8, fontSize: 24, ), ), const SizedBox(height: 40), TimerDisplay(seconds: workout.restRemaining), const SizedBox(height: 40), if (nextTitle.isNotEmpty) Text( 'Up next: $nextTitle', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), ], ), ), ); } Widget _buildCompleted(BuildContext context) { return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.check_circle, size: 96, color: MirrorTheme.success), const SizedBox(height: 24), Text( 'DONE', style: Theme.of(context).textTheme.displaySmall?.copyWith( color: MirrorTheme.success, ), ), const SizedBox(height: 12), Text( 'Session complete', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), ], ), ), ); } String _formatDuration(Duration d) { final minutes = d.inMinutes; final seconds = (d.inSeconds % 60).toString().padLeft(2, '0'); return '$minutes:$seconds'; } }