import 'dart:async'; import 'dart:math' show cos, pi, sin; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import '../../../print.dart'; import 'map_engine_controller.dart'; import 'map_socket_controller.dart'; import 'nearby_drivers_controller.dart'; import 'ride_lifecycle_controller.dart'; import 'ride_state.dart'; import 'location_search_controller.dart'; class RideSimulationController extends GetxController { Timer? _simulationTimer; int _step = 0; bool _isSimulating = false; List _simulatedRoute = []; int _driverRouteIndex = 0; String get simulationStatus => _isSimulating ? 'Simulating... Step $_step' : 'Idle'; // آمود نقاط المسار (Jordan area: Amman center ~ 31.95, 35.91) static const double _centerLat = 31.95; static const double _centerLng = 35.91; static const double _routeDelta = 0.03; static const int _routePointCount = 100; void _generateRoutePoints() { _simulatedRoute = []; for (int i = 0; i < _routePointCount; i++) { double fraction = i / (_routePointCount - 1); double lat = _centerLat + fraction * _routeDelta; double lng = _centerLng + fraction * _routeDelta + 0.005 * sin(fraction * pi * 2); _simulatedRoute.add(LatLng(lat, lng)); } } void startFullSimulation() { if (_isSimulating) return; _isSimulating = true; _step = 0; _driverRouteIndex = 0; _generateRoutePoints(); Log.print('=' * 70); Log.print('🚀 RIDE SIMULATION STARTED — Full lifecycle test'); Log.print('=' * 70); _simulationTimer = Timer.periodic(const Duration(seconds: 2), (timer) { switch (_step) { case 0: _simulateSearchingState(); break; case 1: _simulateDriverAppliedState(); break; case 2: _simulateDriverEnRoute(isFirst: true); break; case 3: _simulateDriverEnRoute(isFirst: false); break; case 4: _simulateDriverArrivedState(); break; case 5: _simulateRideBeginState(); break; case 6: _simulateRideInProgress(); break; case 7: _simulateRideFinishedState(); timer.cancel(); _isSimulating = false; Log.print('=' * 70); Log.print('✅ RIDE SIMULATION COMPLETED'); Log.print('=' * 70); break; } _step++; update(); }); } void stopSimulation() { _simulationTimer?.cancel(); _simulationTimer = null; _isSimulating = false; _step = 0; _driverRouteIndex = 0; Log.print('🛑 Simulation stopped'); update(); } void _logPolylineState(String stage) { final mapEngine = Get.find(); Log.print(''); Log.print('╔══ Polyline State [$stage] ══╗'); if (mapEngine.polyLines.isEmpty) { Log.print(' ║ (no polylines)'); } for (final p in mapEngine.polyLines) { String colorStr; if (p.color == Colors.amber) { colorStr = 'AMBER'; } else if (p.color.value == 0xFF2196F3) { colorStr = 'BLUE'; } else if (p.color == Colors.blueGrey) { colorStr = 'BLUE_GREY'; } else { colorStr = '#${p.color.value.toRadixString(16).padLeft(8, '0')}'; } Log.print(' ║ • ${p.polylineId.value}: ${p.points.length} pts, ' 'color: $colorStr, w: ${p.width}'); } Log.print('╠══ Markers [$stage] ══╣'); if (mapEngine.markers.isEmpty) { Log.print(' ║ (no markers)'); } for (final m in mapEngine.markers) { Log.print(' ║ • ${m.markerId.value}: ' '(${m.position.latitude.toStringAsFixed(5)}, ' '${m.position.longitude.toStringAsFixed(5)}), ' 'heading: ${m.rotation.toStringAsFixed(1)}'); } Log.print('╚══════════════════════╝'); Log.print(''); } void _verifyPolylineCondition(bool condition, String description) { if (!condition) { Log.print(' ❌ VERIFY FAILED: $description'); } else { Log.print(' ✅ VERIFY PASSED: $description'); } } LatLng _interpolateRoutePoint(double fraction) { if (_simulatedRoute.isEmpty) return LatLng(_centerLat, _centerLng); int total = _simulatedRoute.length; double exactIdx = fraction * (total - 1); int idx = exactIdx.floor(); if (idx >= total - 1) return _simulatedRoute.last; double t = exactIdx - idx; return LatLng( _simulatedRoute[idx].latitude + t * (_simulatedRoute[idx + 1].latitude - _simulatedRoute[idx].latitude), _simulatedRoute[idx].longitude + t * (_simulatedRoute[idx + 1].longitude - _simulatedRoute[idx].longitude), ); } void _simulateSearchingState() { Log.print('🟡 [Step $_step] STATE: SEARCHING'); final ride = Get.find(); final locSearch = Get.find(); ride.currentRideState.value = RideState.searching; ride.rideId = 'sim_ride_001'; ride.isSearchingWindow = true; // آمود موقع الراكب LatLng passengerLoc = _simulatedRoute.isNotEmpty ? _simulatedRoute.first : LatLng(_centerLat, _centerLng); locSearch.passengerLocation = passengerLoc; ride.passengerLocation = passengerLoc; ride.update(); _logPolylineState('searching'); _verifyPolylineCondition( Get.find().polyLines.isEmpty, 'No route polylines during searching'); Log.print(''); } void _simulateDriverAppliedState() { Log.print('🟢 [Step $_step] STATE: DRIVER APPLIED'); final ride = Get.find(); final nearbyDrivers = Get.find(); final locSearch = Get.find(); ride.currentRideState.value = RideState.driverApplied; ride.statusRide = 'Apply'; ride.isSearchingWindow = false; // آمود معلومات السائق ride.driverId = 'sim_driver_001'; ride.driverName = 'Simulated Captain'; ride.make = 'Toyota'; ride.model = 'Corolla'; ride.carColor = 'White'; ride.licensePlate = 'SIM-123'; ride.driverRate = '4.8'; ride.driverPhone = '+9627XXXXXXXX'; ride.driverToken = 'sim_token_001'; // آمود مواقع البداية والنهاية ride.passengerLocation = _simulatedRoute.first; ride.myDestination = _simulatedRoute.last; locSearch.passengerLocation = _simulatedRoute.first; locSearch.myDestination = _simulatedRoute.last; // محاكاة موقع السائق (45% على طول المسار) LatLng driverPos = _interpolateRoutePoint(0.45); nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [driverPos]; // رسم مسار السائق إلى الراكب ride.calculateDriverToPassengerRoute(driverPos, ride.passengerLocation); // وضع ماركر السائق على الخريطة ride.updateDriverMarker(driverPos, 45.0); ride.update(); _logPolylineState('driver_applied'); // التحقق من وجود الخطوط المطلوبة final polylines = Get.find().polyLines; _verifyPolylineCondition( polylines.any((p) => p.polylineId.value == 'driver_route_solid'), 'driver_route_solid (amber solid line) exists'); _verifyPolylineCondition( polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')), 'passenger_walk_line (dashed walk line) exists'); _verifyPolylineCondition( polylines.any((p) => p.color == Colors.amber), 'Amber-colored polyline exists (driver route)'); _verifyPolylineCondition( polylines.any((p) => p.color == Colors.blueGrey), 'BlueGrey-colored polyline exists (walk dashes)'); Log.print(''); } void _simulateDriverEnRoute({required bool isFirst}) { String label = isFirst ? '1' : '2'; Log.print('🔵 [Step $_step] STATE: DRIVER APPROACHING (update #$label)'); final ride = Get.find(); final nearbyDrivers = Get.find(); _driverRouteIndex += 18; if (_driverRouteIndex > _routePointCount - 1) { _driverRouteIndex = _routePointCount - 1; } LatLng newPos = _simulatedRoute[_driverRouteIndex]; // تحديث موقع السائق nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos]; ride.updateDriverMarker(newPos, 50.0); ride.updateRemainingRoute(newPos); ride.update(); _logPolylineState('driver_en_route_$label'); // التحقق: driver_route_solid يبقى موجود ويتقلص final polylines = Get.find().polyLines; _verifyPolylineCondition( polylines.any((p) => p.polylineId.value == 'driver_route_solid'), 'driver_route_solid still exists (shrinking)'); _verifyPolylineCondition( polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')), 'passenger_walk_line still exists'); Log.print(''); } void _simulateDriverArrivedState() { Log.print('🟣 [Step $_step] STATE: DRIVER ARRIVED'); final ride = Get.find(); ride.currentRideState.value = RideState.driverArrived; ride.statusRide = 'Arrived'; // محاكاة وصول السائق: تحديد الموقع كآخر نقطة طريق LatLng driverPos = _simulatedRoute.first; // تطبيق arrived logic ride.polyLines = ride.polyLines .where((p) => p.polylineId.value != 'main_route' && p.polylineId.value != 'route_direct' && !p.polylineId.value.startsWith('driver_route')) .toSet(); // رسم المسار الجديد (من السائق إلى الوجهة النهائية) ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination, isBeginPhase: true); ride.update(); _logPolylineState('driver_arrived'); final polylines = Get.find().polyLines; // في حالة الوصول: يجب أن يظهر main_route (أزرق) بدلاً من driver_route_solid _verifyPolylineCondition( !polylines.any((p) => p.polylineId.value == 'driver_route_solid'), 'driver_route_solid (amber) has been cleared'); _verifyPolylineCondition( polylines.any((p) => p.polylineId.value == 'main_route'), 'main_route (blue) has been drawn to destination'); Log.print(''); } void _simulateRideBeginState() { Log.print('🔴 [Step $_step] STATE: RIDE IN PROGRESS (Begin)'); final ride = Get.find(); ride.currentRideState.value = RideState.inProgress; ride.statusRide = 'Begin'; LatLng driverPos = _simulatedRoute.first; // محاكاة processRideBegin: مسح الخطوط القديمة ورسم الخط الجديد ride.polyLines = ride.polyLines .where((p) => p.polylineId.value != 'main_route' && p.polylineId.value != 'route_direct' && !p.polylineId.value.startsWith('driver_route')) .toSet(); ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination, isBeginPhase: true); ride.rideIsBeginPassengerTimer(); ride.update(); _logPolylineState('ride_begin'); final polylines = Get.find().polyLines; _verifyPolylineCondition( polylines.any((p) => p.polylineId.value == 'main_route'), 'main_route (blue) exists for trip'); _verifyPolylineCondition( !polylines.any((p) => p.polylineId.value.startsWith('driver_route')), 'No driver_route* polylines remain'); _verifyPolylineCondition( polylines.any((p) => p.color.value == 0xFF2196F3), 'Blue polyline (main route) is present'); Log.print(''); } void _simulateRideInProgress() { Log.print('📱 [Step $_step] STATE: CAR MOVING (remaining route update)'); final ride = Get.find(); final nearbyDrivers = Get.find(); _driverRouteIndex += 30; if (_driverRouteIndex > _routePointCount - 1) { _driverRouteIndex = _routePointCount - 1; } LatLng newPos = _simulatedRoute[_driverRouteIndex]; nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos]; ride.updateDriverMarker(newPos, 70.0); ride.updateRemainingRoute(newPos, updateEta: true); ride.update(); _logPolylineState('ride_in_progress'); final polylines = Get.find().polyLines; _verifyPolylineCondition( polylines.any((p) => p.polylineId.value == 'main_route'), 'main_route (blue) exists with remaining points'); _verifyPolylineCondition( !polylines.any((p) => p.polylineId.value.startsWith('driver_route')), 'No driver_route* polylines remain'); Log.print(''); } void _simulateRideFinishedState() { Log.print('🏁 [Step $_step] STATE: RIDE FINISHED'); final ride = Get.find(); final mapEngine = Get.find(); ride.currentRideState.value = RideState.finished; ride.statusRide = 'Finished'; ride.isSearchingWindow = false; ride.stopAllTimers(); mapEngine.clearPolyline(); mapEngine.markers = {}; ride.update(); _logPolylineState('ride_finished'); _verifyPolylineCondition( Get.find().polyLines.isEmpty, 'All polylines cleared'); _verifyPolylineCondition( Get.find().markers.isEmpty, 'All markers removed'); Log.print(''); } // Simulate a single WebSocket location update event void simulateSocketLocationUpdate(LatLng position, double heading) { final ride = Get.find(); final nearbyDrivers = Get.find(); final mapSocket = Get.find(); Log.print('📡 Simulating socket location update: ' '(${position.latitude.toStringAsFixed(5)}, ${position.longitude.toStringAsFixed(5)})'); nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [position]; ride.driverCarsLocationToPassengerAfterApplied = [position]; ride.checkAndRecalculateIfDeviated( position, heading: heading, speed: 30.0, ); final mapEngine = Get.find(); if (mapEngine.mapController != null) { mapEngine.mapController! .animateCamera(CameraUpdate.newLatLngZoom(position, 16.5)); } ride.updateDriverMarker(position, heading); ride.updateRemainingRoute(position); ride.update(); } @override void onClose() { _simulationTimer?.cancel(); super.onClose(); } }