import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import '../../core/constants/app_theme.dart'; import '../../core/models/recipe.dart'; class DetectedDuration { final int totalMinutes; final String label; DetectedDuration({required this.totalMinutes, required this.label}); } class CookModeScreen extends StatefulWidget { final Recipe recipe; const CookModeScreen({super.key, required this.recipe}); @override State createState() => _CookModeScreenState(); } class _CookModeScreenState extends State { int _currentStepIndex = 0; double _scaleMultiplier = 1.0; final PageController _pageController = PageController(); // Countdown Timer State Timer? _activeTimer; int _timerSecondsRemaining = 0; bool _isTimerRunning = false; int _currentTimerMinutes = 0; // Stopwatch Mode State Timer? _stopwatchTimer; int _stopwatchSeconds = 0; bool _isStopwatchRunning = false; bool _showStopwatch = false; @override void initState() { super.initState(); // Keep screen on during cooking session so hands don't need to touch phone WakelockPlus.enable(); } @override void dispose() { WakelockPlus.disable(); _activeTimer?.cancel(); _stopwatchTimer?.cancel(); _pageController.dispose(); super.dispose(); } void _startTimer(int minutes) { _activeTimer?.cancel(); HapticFeedback.mediumImpact(); setState(() { _currentTimerMinutes = minutes; _timerSecondsRemaining = minutes * 60; _isTimerRunning = true; }); _activeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (_timerSecondsRemaining <= 1) { timer.cancel(); setState(() { _isTimerRunning = false; _timerSecondsRemaining = 0; }); HapticFeedback.vibrate(); if (mounted) { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: AppTheme.surfaceElevated, title: const Text('Timer Finished! ⏰'), content: Text('Your ${minutes}-minute timer has ended.'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('OK', style: TextStyle(color: AppTheme.primary)), ), ], ), ); } } else { setState(() { _timerSecondsRemaining--; }); } }); } void _toggleStopwatch() { HapticFeedback.lightImpact(); if (_isStopwatchRunning) { _stopwatchTimer?.cancel(); setState(() => _isStopwatchRunning = false); } else { setState(() => _isStopwatchRunning = true); _stopwatchTimer = Timer.periodic(const Duration(seconds: 1), (_) { if (mounted) { setState(() => _stopwatchSeconds++); } }); } } void _resetStopwatch() { HapticFeedback.lightImpact(); _stopwatchTimer?.cancel(); setState(() { _isStopwatchRunning = false; _stopwatchSeconds = 0; }); } String _formatTimerTime(int totalSeconds) { final h = totalSeconds ~/ 3600; final m = (totalSeconds % 3600) ~/ 60; final s = totalSeconds % 60; if (h > 0) { return '${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}'; } return '${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}'; } List _detectDurationsInStep(String text) { final List results = []; final regex = RegExp( r'(?:for\s+)?(\d+(?:\.\d+)?)\s*(?:-|to)?\s*(\d+(?:\.\d+)?)?\s*(hours?|hrs?|minutes?|mins?|seconds?|secs?)\b', caseSensitive: false, ); final matches = regex.allMatches(text); for (final match in matches) { final firstNum = match.group(1); final secondNum = match.group(2); final unit = match.group(3)?.toLowerCase() ?? ''; final numStr = secondNum ?? firstNum; if (numStr == null) continue; final val = double.tryParse(numStr); if (val == null || val <= 0) continue; int minutes = 0; if (unit.startsWith('h')) { minutes = (val * 60).round(); } else if (unit.startsWith('s')) { minutes = (val / 60).ceil(); if (minutes < 1) minutes = 1; } else { minutes = val.round(); } if (minutes > 0 && !results.any((d) => d.totalMinutes == minutes)) { results.add(DetectedDuration( totalMinutes: minutes, label: '${match.group(0)?.trim()}', )); } } return results; } void _showIngredientsSheet() { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, isScrollControlled: true, builder: (ctx) => DraggableScrollableSheet( initialChildSize: 0.65, minChildSize: 0.4, maxChildSize: 0.9, builder: (_, controller) => Container( decoration: const BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: AppTheme.border, borderRadius: BorderRadius.circular(2), ), ), ), Row( children: [ const Icon(Icons.restaurant_menu_rounded, color: AppTheme.primary, size: 22), const SizedBox(width: 8), Text( 'Ingredients (${_scaleMultiplier}x)', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700), ), const Spacer(), IconButton( icon: const Icon(Icons.close, size: 20), onPressed: () => Navigator.pop(ctx), ), ], ), const SizedBox(height: 12), Expanded( child: widget.recipe.ingredients.isEmpty ? const Center(child: Text('No ingredients found.', style: TextStyle(color: AppTheme.textMuted))) : ListView.separated( controller: controller, itemCount: widget.recipe.ingredients.length, separatorBuilder: (_, __) => const Divider(color: AppTheme.border, height: 1), itemBuilder: (c, idx) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('• ', style: TextStyle(color: AppTheme.primary, fontSize: 16)), Expanded( child: Text( widget.recipe.ingredients[idx], style: const TextStyle(fontSize: 15, color: Colors.white, height: 1.3), ), ), ], ), ); }, ), ), ], ), ), ), ); } @override Widget build(BuildContext context) { final totalSteps = widget.recipe.instructions.isNotEmpty ? widget.recipe.instructions.length : 1; final currentStepText = (widget.recipe.instructions.isNotEmpty && _currentStepIndex < widget.recipe.instructions.length) ? widget.recipe.instructions[_currentStepIndex] : ''; final detectedTimers = _detectDurationsInStep(currentStepText); return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.black, title: Text(widget.recipe.title, maxLines: 1, overflow: TextOverflow.ellipsis), actions: [ // Ingredients Peek Sheet Button IconButton( tooltip: 'View Ingredients', icon: const Icon(Icons.restaurant_menu_rounded, color: AppTheme.secondary), onPressed: _showIngredientsSheet, ), // Stopwatch Toggle Button IconButton( tooltip: 'Stopwatch', icon: Icon( Icons.timer_outlined, color: _isStopwatchRunning || _showStopwatch ? AppTheme.primary : Colors.white70, ), onPressed: () { setState(() { _showStopwatch = !_showStopwatch; }); }, ), // Scale selector PopupMenuButton( initialValue: _scaleMultiplier, tooltip: 'Scale Servings', icon: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.circular(8), border: Border.all(color: AppTheme.border), ), child: Text( '${_scaleMultiplier}x', style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 13, color: AppTheme.primary), ), ), onSelected: (val) => setState(() => _scaleMultiplier = val), itemBuilder: (ctx) => const [ PopupMenuItem(value: 0.5, child: Text('0.5x Servings')), PopupMenuItem(value: 1.0, child: Text('1.0x (Original)')), PopupMenuItem(value: 1.5, child: Text('1.5x Servings')), PopupMenuItem(value: 2.0, child: Text('2.0x Double Servings')), ], ), const SizedBox(width: 8), ], ), body: SafeArea( child: Column( children: [ // Top: Active Countdown Timer Pill (if active) if (_isTimerRunning || _timerSecondsRemaining > 0) Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: AppTheme.primary.withOpacity(0.15), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppTheme.primary), ), child: Row( children: [ const Icon(Icons.timer_rounded, color: AppTheme.primary), const SizedBox(width: 10), Text( 'Timer: ${_formatTimerTime(_timerSecondsRemaining)}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800, color: AppTheme.primary), ), const Spacer(), IconButton( icon: const Icon(Icons.stop_circle_rounded, color: AppTheme.danger), onPressed: () { _activeTimer?.cancel(); setState(() { _isTimerRunning = false; _timerSecondsRemaining = 0; }); }, ), ], ), ), // Top: Stopwatch Pill (if shown or running) if (_showStopwatch || _isStopwatchRunning || _stopwatchSeconds > 0) Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppTheme.secondary), ), child: Row( children: [ const Icon(Icons.av_timer_rounded, color: AppTheme.secondary, size: 22), const SizedBox(width: 10), Text( 'Stopwatch: ${_formatTimerTime(_stopwatchSeconds)}', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w800, color: AppTheme.secondary), ), const Spacer(), IconButton( visualDensity: VisualDensity.compact, icon: Icon( _isStopwatchRunning ? Icons.pause_circle_filled_rounded : Icons.play_circle_fill_rounded, color: AppTheme.primary, size: 26, ), onPressed: _toggleStopwatch, ), IconButton( visualDensity: VisualDensity.compact, icon: const Icon(Icons.restart_alt_rounded, color: AppTheme.textMuted, size: 22), onPressed: _resetStopwatch, ), ], ), ), // Step Progress Bar Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: (totalSteps > 0) ? (_currentStepIndex + 1) / totalSteps : 1.0, backgroundColor: AppTheme.surfaceElevated, color: AppTheme.primary, minHeight: 6, ), ), ), // Step Carousel (Large readable text) Expanded( child: widget.recipe.instructions.isEmpty ? const Center(child: Text('No instructions found for this recipe.', style: TextStyle(color: AppTheme.textMuted))) : PageView.builder( controller: _pageController, itemCount: totalSteps, onPageChanged: (i) => setState(() => _currentStepIndex = i), itemBuilder: (ctx, index) { final stepText = widget.recipe.instructions[index]; final stepDurations = _detectDurationsInStep(stepText); return Padding( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( 'STEP ${index + 1} OF $totalSteps', style: const TextStyle( color: AppTheme.primary, fontSize: 14, fontWeight: FontWeight.w800, letterSpacing: 1.2, ), ), ], ), const SizedBox(height: 16), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( stepText, style: const TextStyle( color: Colors.white, fontSize: 22, height: 1.5, fontWeight: FontWeight.w500, ), ), // Smart Auto-detected Timers for this step if (stepDurations.isNotEmpty) ...[ const SizedBox(height: 20), Wrap( spacing: 8, runSpacing: 8, children: stepDurations.map((d) { return ActionChip( backgroundColor: AppTheme.primary.withOpacity(0.18), side: const BorderSide(color: AppTheme.primary), avatar: const Icon(Icons.bolt_rounded, size: 16, color: AppTheme.primary), label: Text( 'Start ${d.totalMinutes}m Timer', style: const TextStyle( color: AppTheme.primary, fontWeight: FontWeight.w700, fontSize: 13, ), ), onPressed: () => _startTimer(d.totalMinutes), ); }).toList(), ), ], ], ), ), ), ], ), ); }, ), ), // Bottom Quick Timers & Nav Buttons Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), decoration: const BoxDecoration( color: AppTheme.surface, border: Border(top: BorderSide(color: AppTheme.border)), ), child: Column( children: [ // Quick timer shortcuts Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Quick Timer: ', style: TextStyle(fontSize: 12, color: AppTheme.textMuted)), _buildTimerChip(5), const SizedBox(width: 8), _buildTimerChip(10), const SizedBox(width: 8), _buildTimerChip(15), const SizedBox(width: 8), _buildTimerChip(30), ], ), const SizedBox(height: 16), // Nav Buttons Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _currentStepIndex > 0 ? () => _pageController.previousPage(duration: const Duration(milliseconds: 250), curve: Curves.easeInOut) : null, icon: const Icon(Icons.arrow_back_rounded), label: const Text('Previous'), ), ), const SizedBox(width: 14), Expanded( child: ElevatedButton.icon( onPressed: _currentStepIndex < totalSteps - 1 ? () => _pageController.nextPage(duration: const Duration(milliseconds: 250), curve: Curves.easeInOut) : () => Navigator.pop(context), icon: Icon(_currentStepIndex < totalSteps - 1 ? Icons.arrow_forward_rounded : Icons.check_circle_rounded), label: Text(_currentStepIndex < totalSteps - 1 ? 'Next Step' : 'Finish'), ), ), ], ), ], ), ), ], ), ), ); } Widget _buildTimerChip(int minutes) { return InkWell( onTap: () => _startTimer(minutes), borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.circular(8), border: Border.all(color: AppTheme.border), ), child: Text( '${minutes}m', style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: AppTheme.primary), ), ), ); } }