import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:uuid/uuid.dart'; import '../models/gps_coordinate.dart'; import '../models/workout.dart'; import '../screens/tracking_screen.dart'; import '../services/database_service.dart'; import '../services/gps_service.dart'; import '../services/sync_service.dart'; import '../services/analytics_service.dart'; import '../utils/polyline_utility.dart'; import '../utils/geo_utils.dart'; /// المتحكم الرئيسي للتمرين (Main Workout Controller) /// يدير حالة التتبع الحية، التفاعل مع الـ GPS، وحفظ البيانات. class WorkoutController extends GetxController { final GpsService _gps = Get.find(); final DatabaseService _db = Get.find(); final SyncService _sync = Get.find(); final AnalyticsService _analytics = Get.find(); // حالة التتبع الحية final Rxn currentWorkout = Rxn(); final RxList liveCoordinates = [].obs; final RxInt elapsedSeconds = 0.obs; final RxDouble liveDistance = 0.0.obs; final RxDouble liveSpeed = 0.0.obs; Timer? _timer; Future _coordinateWriteQueue = Future.value(); // ─── التحكم في الجلسة ────────────────────────────────────────── Future startWorkout(WorkoutType type) async { bool ok = await _gps.startTracking(distanceFilter: 5); if (!ok) { Get.snackbar("خطأ", "فشل الوصول إلى الموقع (GPS). تأكد من الصلاحيات."); return; } final id = const Uuid().v4(); currentWorkout.value = Workout(id: id, workoutType: type); currentWorkout.value!.start(); liveCoordinates.clear(); _coordinateWriteQueue = Future.value(); elapsedSeconds.value = 0; liveDistance.value = 0; liveSpeed.value = 0; // ربط مستمع الإحداثيات _gps.onNewCoordinate = _handleNewCoordinate; // الانتقال لشاشة التتبع Get.to(() => const TrackingScreen()); // بدء المؤقت الزمني _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (currentWorkout.value != null && !currentWorkout.value!.isPaused) { elapsedSeconds.value = currentWorkout.value!.activeDurationSeconds; } }); } void pauseWorkout() { currentWorkout.value?.pause(); update(); } void resumeWorkout() { currentWorkout.value?.resume(); update(); } Future stopWorkout() async { if (currentWorkout.value == null) return; await _gps.stopTracking(); _timer?.cancel(); await _coordinateWriteQueue; final workout = currentWorkout.value!; workout.stop(); // إسناد الإحداثيات النهائية workout.coordinates = List.from(liveCoordinates); workout.distanceMeters = liveDistance.value; // التحليل الذكي للمقاطع workout.segments = _analytics.analyzeSegments(workout.coordinates, workout.workoutType); // تحويل المسار إلى Polyline مضغوط workout.routePolyline = PolylineUtility.encode(workout.coordinates); if (workout.coordinates.isNotEmpty) { workout.startLat = workout.coordinates.first.latitude; workout.startLng = workout.coordinates.first.longitude; workout.endLat = workout.coordinates.last.latitude; workout.endLng = workout.coordinates.last.longitude; } // Save the workout and its outbox record in one local transaction. await _sync.queueWorkout(workout); currentWorkout.value = null; Get.snackbar( "تم الحفظ", workout.coordinates.length >= 2 ? "تم حفظ التمرين محليًا وجاري المزامنة عند توفر الاتصال." : "حُفظ النشاط على الجهاز؛ لم تتوفر نقاط GPS كافية لمزامنته.", ); } void _handleNewCoordinate(GpsCoordinate coord) { if (currentWorkout.value == null || currentWorkout.value!.isPaused) return; liveCoordinates.add(coord); liveSpeed.value = coord.speed ?? 0; // حفظ فوري في DB (للحماية من انهيار التطبيق) final workoutId = currentWorkout.value!.id; _coordinateWriteQueue = _coordinateWriteQueue.then((_) async { await _db.saveCoordinate(workoutId, coord); }).catchError((Object error, StackTrace stackTrace) { debugPrint('[TRACKING] Coordinate persistence failed: $error'); }); // حساب المسافة التراكمية حياً if (liveCoordinates.length > 1) { // تم إضافة المسافة في الموديل أو هنا مباشرة // للتسهيل سنقوم بالتحديث هنا } // تحديث المسافة الإجمالية (تبسيطاً سنعتمد على المجموع) _calculateLiveDistance(); } void _calculateLiveDistance() { if (liveCoordinates.length < 2) return; double totalDist = 0; for (int i = 1; i < liveCoordinates.length; i++) { totalDist += GeoUtils.haversineDistance( liveCoordinates[i - 1].latitude, liveCoordinates[i - 1].longitude, liveCoordinates[i].latitude, liveCoordinates[i].longitude); } liveDistance.value = totalDist; } @override void onClose() { _timer?.cancel(); super.onClose(); } }