Files
intaleq/lib/controller/home/map_passenger_controller.dart

7752 lines
299 KiB
Dart

// import 'dart:async';
// import 'package:Intaleq/services/offline_map_service.dart';
// import 'package:Intaleq/services/emergency_signal_service.dart';
// import 'package:Intaleq/views/widgets/mycircular.dart';
// import 'dart:convert';
// import 'dart:io';
// import 'dart:math' show Random, atan2, cos, max, min, pi, pow, sin, sqrt;
// import 'dart:math' as math;
// import 'dart:ui';
// import 'dart:typed_data';
// import 'package:image/image.dart' as img;
// import 'package:Intaleq/services/ride_live_notification.dart';
// import 'package:crypto/crypto.dart';
// import 'package:Intaleq/views/Rate/rate_captain.dart';
// import 'package:Intaleq/views/Rate/rating_driver_bottom.dart';
// import 'package:device_info_plus/device_info_plus.dart';
// import 'package:flutter/foundation.dart';
// import 'package:flutter/services.dart';
// import 'package:http/http.dart' as http;
// import 'package:Intaleq/constant/univeries_polygon.dart';
// import 'package:Intaleq/controller/firebase/local_notification.dart';
// import 'package:flutter/cupertino.dart';
// import 'package:flutter_confetti/flutter_confetti.dart' hide Circle;
// import 'package:socket_io_client/socket_io_client.dart' as IO;
// import 'package:vector_math/vector_math.dart' show radians;
// import 'package:Intaleq/controller/functions/tts.dart';
// import 'package:Intaleq/views/home/map_page_passenger.dart';
// import 'package:Intaleq/views/widgets/my_textField.dart';
// import 'package:flutter/material.dart';
// import 'package:geolocator/geolocator.dart';
// import 'package:get/get.dart';
// import 'package:intaleq_maps/intaleq_maps.dart';
// // import 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
// import 'package:intl/intl.dart';
// import 'package:location/location.dart';
// import 'package:Intaleq/constant/country_polygons.dart';
// import 'package:Intaleq/constant/links.dart';
// import 'package:Intaleq/constant/style.dart';
// import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
// import 'package:Intaleq/views/home/map_widget.dart/form_serch_multiy_point.dart';
// import '../../constant/api_key.dart';
// import '../../constant/box_name.dart';
// import '../../constant/colors.dart';
// import '../../constant/info.dart';
// import '../../constant/table_names.dart';
// import '../../env/env.dart';
// import '../../main.dart';
// import '../../models/model/locations.dart';
// import '../../models/model/painter_copoun.dart';
// import '../../print.dart';
// import '../../services/pip_service.dart';
// import '../../views/home/map_widget.dart/cancel_raide_page.dart';
// import '../../views/home/map_widget.dart/car_details_widget_to_go.dart';
// import '../../views/home/map_widget.dart/select_driver_mishwari.dart';
// import '../../views/widgets/elevated_btn.dart';
// import '../../views/widgets/error_snakbar.dart';
// import '../../views/widgets/mydialoug.dart';
// import '../firebase/firbase_messge.dart';
// import '../firebase/notification_service.dart';
// import '../functions/audio_record1.dart';
// import '../functions/crud.dart';
// import '../functions/launch.dart';
// import '../functions/package_info.dart';
// import '../functions/secure_storage.dart';
// import '../payment/payment_controller.dart';
// import 'decode_polyline_isolate.dart';
// import 'deep_link_controller.dart';
// import 'device_performance.dart';
// import 'ios_live_activity_service.dart';
// import 'vip_waitting_page.dart';
// enum RideState {
// noRide, // لا يوجد رحلة جارية، عرض واجهة البحث
// cancelled, // تم إلغاء الرحلة
// preCheckReview, // يوجد رحلة منتهية، تحقق من التقييم
// searching, // جاري البحث عن كابتن
// driverApplied, // تم قبول الطلب
// driverArrived, // وصل السائق
// inProgress, // الرحلة بدأت بالفعل
// finished, // انتهت الرحلة (سيتم تحويلها إلى preCheckReview)
// }
// class MapPassengerController extends GetxController {
// bool isLoading = true;
// TextEditingController placeDestinationController = TextEditingController();
// TextEditingController increasFeeFromPassenger = TextEditingController();
// TextEditingController placeStartController = TextEditingController();
// TextEditingController wayPoint0Controller = TextEditingController();
// TextEditingController wayPoint1Controller = TextEditingController();
// TextEditingController wayPoint2Controller = TextEditingController();
// TextEditingController wayPoint3Controller = TextEditingController();
// TextEditingController wayPoint4Controller = TextEditingController();
// TextEditingController sosPhonePassengerProfile = TextEditingController();
// TextEditingController whatsAppLocationText = TextEditingController();
// TextEditingController messageToDriver = TextEditingController();
// final sosFormKey = GlobalKey<FormState>();
// final promoFormKey = GlobalKey<FormState>();
// final messagesFormKey = GlobalKey<FormState>();
// final increaseFeeFormKey = GlobalKey<FormState>();
// List data = [];
// List<LatLng> bounds = [];
// List placesStart = [];
// List<String> driversToken = [];
// LatLng previousLocationOfDrivers = const LatLng(0, 0);
// double angleDegrees = 0;
// LatLng currentLocationOfDrivers = const LatLng(0, 0);
// List<TextEditingController> allTextEditingPlaces = [];
// List placesDestination = [];
// List wayPoint0 = [];
// List wayPoint1 = [];
// List wayPoint2 = [];
// List wayPoint3 = [];
// List wayPoint4 = [];
// final firebaseMessagesController =
// Get.isRegistered<FirebaseMessagesController>()
// ? Get.find<FirebaseMessagesController>()
// : Get.put(FirebaseMessagesController());
// List<List<dynamic>> placeListResponseAll = [];
// List<Widget> placeListResponse = [
// formSearchPlaces(0),
// formSearchPlaces(1),
// formSearchPlaces(2),
// formSearchPlaces(3),
// ];
// IntaleqMapController? mapController;
// bool isStyleLoaded = false;
// Set<Marker> markers = {};
// Set<Polyline> polyLines = {};
// Set<Polygon> polygons = {};
// Set<Circle> circles = {};
// double speed = 0;
// PermissionStatus? permissionGranted;
// LatLngBounds? lastComputedBounds;
// late LatLng passengerLocation = const LatLng(32, 34);
// late LatLng newMyLocation = const LatLng(32.115295, 36.064773);
// late LatLng newStartPointLocation = const LatLng(32.115295, 36.064773);
// late LatLng newPointLocation0 = const LatLng(32.115295, 36.064773);
// late LatLng newPointLocation1 = const LatLng(32.115295, 36.064773);
// late LatLng newPointLocation2 = const LatLng(32.115295, 36.064773);
// late LatLng newPointLocation3 = const LatLng(32.115295, 36.064773);
// late LatLng newPointLocation4 = const LatLng(32.115295, 36.064773);
// late LatLng myDestination;
// List<LatLng> polylineCoordinates = [];
// List<LatLng> polylineCoordinates0 = [];
// List<LatLng> polylineCoordinates1 = [];
// List<LatLng> polylineCoordinates2 = [];
// List<LatLng> polylineCoordinates3 = [];
// List<LatLng> polylineCoordinates4 = [];
// List<List<LatLng>> polylineCoordinatesPointsAll = [];
// List carsLocationByPassenger = [];
// List<LatLng> driverCarsLocationToPassengerAfterApplied = [];
// String markerIcon = "marker_icon";
// String tripIcon = "trip_icon";
// String startIcon = "start_icon";
// String endIcon = "end_icon";
// String carIcon = "car_icon";
// String motoIcon = "moto_icon";
// String ladyIcon = "lady_icon";
// double height = 150;
// DateTime currentTime = DateTime.now();
// final location = Location();
// late LocationData currentLocation;
// double heightMenu = 0;
// double widthMenu = 0;
// double heightPickerContainer = 90;
// double heightPointsPageForRider = 0;
// double mainBottomMenuMapHeight = Get.height * .2;
// double wayPointSheetHeight = 0;
// String stringRemainingTimeToPassenger = '';
// String stringRemainingTimeDriverWaitPassenger5Minute = '';
// bool isDriverInPassengerWay = false;
// bool isDriverArrivePassenger = false;
// bool startLocationFromMap = false;
// bool isAnotherOreder = false;
// bool isWhatsAppOrder = false;
// bool passengerStartLocationFromMap = false;
// bool workLocationFromMap = false;
// bool homeLocationFromMap = false;
// bool isPassengerRideLocationWidget = false;
// bool startLocationFromMap0 = false;
// bool startLocationFromMap1 = false;
// bool startLocationFromMap2 = false;
// bool startLocationFromMap3 = false;
// bool startLocationFromMap4 = false;
// List startLocationFromMapAll = [];
// double latePrice = 0;
// double fuelPrice = 0;
// double heavyPrice = 0;
// double naturePrice = 0;
// bool heightMenuBool = false;
// String statusRide = 'wait';
// String statusRideVip = 'wait';
// bool statusRideFromStart = false;
// bool isPickerShown = false;
// bool isPointsPageForRider = false;
// bool isBottomSheetShown = false;
// bool mapType = false;
// bool reloadStartApp = false;
// bool mapTrafficON = false;
// bool isCancelRidePageShown = false;
// bool isCashConfirmPageShown = false;
// bool isPaymentMethodPageShown = false;
// bool isRideFinished = false;
// bool rideConfirm = false;
// bool isMarkersShown = false;
// bool isMainBottomMenuMap = true;
// int durationToPassenger = 0;
// bool isWayPointSheet = false;
// bool isWayPointStopsSheet = false;
// bool isWayPointStopsSheetUtilGetMap = false;
// double heightBottomSheetShown = 0;
// double cashConfirmPageShown = 250;
// late String driverId = '';
// late String gender = '';
// double widthMapTypeAndTraffic = 50;
// double paymentPageShown = Get.height * .6;
// late LatLng southwest;
// late LatLng northeast;
// List<CarLocationModel> carLocationsModels = <CarLocationModel>[];
// var dataCarsLocationByPassenger;
// var datadriverCarsLocationToPassengerAfterApplied;
// CarLocation? nearestCar;
// bool shouldFetch = true; // Flag to determine if fetch should be executed
// int selectedPassengerCount = 1;
// double progress = 0;
// double progressTimerToPassengerFromDriverAfterApplied = 0;
// double progressTimerDriverWaitPassenger5Minute = 0;
// int durationTimer = 9;
// int durationToRide = 0;
// int remainingTime = 25;
// int remainingTimeToPassengerFromDriverAfterApplied = 60;
// int remainingTimeDriverWaitPassenger5Minute = 60;
// int timeToPassengerFromDriverAfterApplied = 0;
// Timer? timerToPassengerFromDriverAfterApplied;
// bool rideTimerBegin = false;
// double progressTimerRideBegin = 0;
// int remainingTimeTimerRideBegin = 60;
// String stringRemainingTimeRideBegin = '';
// String hintTextStartPoint = 'Search for your Start point'.tr;
// String hintTextwayPoint0 = 'Search for waypoint'.tr;
// String hintTextwayPoint1 = 'Search for waypoint'.tr;
// String hintTextwayPoint2 = 'Search for waypoint'.tr;
// String hintTextwayPoint3 = 'Search for waypoint'.tr;
// String hintTextwayPoint4 = 'Search for waypoint'.tr;
// String currentLocationString = 'Current Location'.tr;
// String currentLocationString0 = 'Current Location'.tr;
// String currentLocationString1 = 'Add Location 1'.tr;
// String currentLocationString2 = 'Add Location 2'.tr;
// String currentLocationString3 = 'Add Location 3'.tr;
// String currentLocationString4 = 'Add Location 4'.tr;
// String placesCoordinate0 = ''.tr;
// String placesCoordinate1 = ''.tr;
// String placesCoordinate2 = ''.tr;
// String placesCoordinate3 = ''.tr;
// String placesCoordinate4 = ''.tr;
// List<String> currentLocationStringAll = [];
// List<String> hintTextwayPointStringAll = [];
// var placesCoordinate = <String>[];
// String hintTextDestinationPoint = 'Select your destination'.tr;
// late String rideId = 'yet';
// bool noCarString = false;
// bool isCashSelectedBeforeConfirmRide = false;
// bool isPassengerChosen = false;
// bool isSearchingWindow = false;
// bool currentLocationToFormPlaces = false;
// bool currentLocationToFormPlaces0 = false;
// bool currentLocationToFormPlaces1 = false;
// bool currentLocationToFormPlaces2 = false;
// bool currentLocationToFormPlaces3 = false;
// bool currentLocationToFormPlaces4 = false;
// List currentLocationToFormPlacesAll = [];
// // ── Multi-Waypoint (max 2 stops) ──────────────────────────────────────────
// List<LatLng?> menuWaypoints = [null, null];
// List<String> menuWaypointNames = ['', ''];
// int activeMenuWaypointCount = 0;
// bool isPickingWaypoint = false;
// int pickingWaypointIndex = -1;
// late String driverToken = '';
// int carsOrder = 0;
// int wayPointIndex = 0;
// late double kazan = 8;
// String? mapAPIKEY;
// late double totalME = 0;
// late double tax = 0;
// late double totalPassenger = 0;
// late double totalCostPassenger = 0;
// late double totalPassengerComfort = 0;
// late double totalPassengerComfortDiscount = 0;
// late double totalPassengerElectricDiscount = 0;
// late double totalPassengerLadyDiscount = 0;
// late double totalPassengerSpeedDiscount = 0;
// late double totalPassengerBalashDiscount = 0;
// late double totalPassengerRaihGaiDiscount = 0;
// late double totalPassengerScooter = 0;
// late double totalPassengerVan = 0;
// late double totalDriver = 0;
// late double averageDuration = 0;
// late double costDuration = 0;
// late double costDistance = 0;
// late double distance = 0;
// late double duration = 0;
// bool _isDriverAppliedLogicExecuted = false; // فلاج لمنع التنفيذ المتكرر
// bool _isDriverArrivedLogicExecuted = false;
// bool _isRideBeginLogicExecuted = false;
// DateTime? _searchStartTime; // لتتبع مدة البحث
// DateTime? _lastDriversNotifyTime; // لتتبع آخر مرة تم إرسال إشعار للسائقين
// final int _masterTimerIntervalSeconds = 5; // فاصل زمني ثابت للمؤقت الرئيسي
// final int _searchTimeoutSeconds = 60; // مهلة البحث قبل عرض خيار زيادة السعر
// final int _notifyDriversIntervalSeconds =
// 25; // إرسال إشعار للسائقين كل 25 ثانية
// // متغير لمنع أي عمليات تحديث أثناء التقييم
// bool _isRatingScreenOpen = false;
// // --- إضافة جديدة: متغيرات لإدارة البحث المتوسع ---
// int _currentSearchPhase = 0; // لتتبع المرحلة الحالية للبحث
// bool _isFetchingDriverLocation = false; // متغير لمنع تكرار الطلب
// // === استبدل initSocket بالكامل ===
// late IO.Socket socket;
// bool isSocketConnected = false;
// int _reconnectAttempts = 0;
// final int _maxReconnectAttempts = 5;
// Timer? _reconnectTimer;
// var currentRideId;
// // لتخزين نقاط مسار السائق الحالية للمقارنة
// List<LatLng> _currentDriverRoutePoints = [];
// // متغير لتتبع مصدر القبول — Socket أم غيره
// String _rideAcceptedViaSource = "Unknown";
// // عدّاد تحديثات الموقع المستلمة من السوكيت (لقياس الصحة)
// int _socketLocationUpdatesCount = 0;
// final Map<RideState, int> _pollingIntervals = {
// RideState.noRide: 6,
// RideState.searching: 8,
// RideState.driverApplied: 10,
// RideState.driverArrived: 15,
// RideState.inProgress: 15,
// RideState.cancelled: 3600,
// RideState.finished: 3600,
// RideState.preCheckReview: 3600,
// };
// // لمنع التكرار (عشان ما يعمل 100 طلب في نفس اللحظة)
// bool _isRecalculatingRoute = false;
// // متغير لمراقبة صحة السوكيت
// DateTime? _lastSocketLocationTime;
// // مسافة السماحية (مثلاً 150 متر) قبل اعتبار السائق "خارج المسار"
// final double _deviationThresholdMeters = 150.0;
// // ... (باقي الـ Imports)
// // متغيرات التحكم
// Timer? _locationPollingTimer; // تايمر مخصص للموقع فقط
// // ==============================================================================
// // 1. الدالة الرئيسية لتأسيس الاتصال (تستدعى عند بدء البحث startSearchingForDriver)
// // ==============================================================================
// Timer? _heartbeatTimer;
// void initConnectionWithSocket() {
// if (isSocketConnected && socket != null) return;
// String passengerId = box.read(BoxName.passengerID).toString();
// Log.print("🔌 Initializing Socket for Passenger: $passengerId");
// socket = IO.io(
// AppLink.serverSocket,
// IO.OptionBuilder()
// .setTransports(['websocket'])
// .disableAutoConnect()
// .setQuery({'id': passengerId})
// // ✅ [FIX] إعادة اتصال شبه-لانهائية (999 محاولة) بدلاً من 20
// .setReconnectionAttempts(20)
// // ✅ [FIX] تأخير أقل (1.5 ثانية) مع حد أقصى (8 ثواني) للتسريع
// .setReconnectionDelay(1500)
// .setReconnectionDelayMax(8000)
// .enableReconnection()
// .setExtraHeaders({'Connection': 'Upgrade'})
// .build(),
// );
// socket.connect();
// // ✅ معالج الاتصال الأول
// socket.onConnect((_) {
// Log.print("✅ Socket Connected Successfully");
// isSocketConnected = true;
// _reconnectAttempts = 0;
// _startHeartbeat();
// // ✅ [FIX] الاشتراك مجدداً في أحداث الرحلة عند كل اتصال
// if (rideId != null && rideId != 'yet' && driverId.isNotEmpty) {
// socket.emit('subscribe_driver_location', {
// 'ride_id': rideId,
// 'driver_id': driverId,
// });
// Log.print("📡 Re-subscribed to driver location after connect");
// }
// update();
// });
// // ⚠️ معالج الانقطاع
// socket.onDisconnect((_) {
// Log.print("⚠️ Socket Disconnected — Auto-Reconnect will handle it");
// isSocketConnected = false;
// // تفعيل Polling أسرع كـ Fallback مؤقت (سيتم إيقافه عند عودة الاتصال)
// if (_isActiveRideState()) {
// Log.print("🔄 Enabling Fast Polling Fallback (4s) until reconnect...");
// _startMasterTimerWithInterval(4);
// }
// update();
// });
// // 🔁 [FIX] معالج إعادة الاتصال الناجحة
// socket.onReconnect((_) {
// Log.print("🔁 Socket Reconnected Successfully!");
// isSocketConnected = true;
// _reconnectAttempts = 0;
// // استئناف النبضة فوراً
// _startHeartbeat();
// // إعادة الاشتراك في أحداث الرحلة
// if (rideId != null && rideId != 'yet' && driverId.isNotEmpty) {
// socket.emit('subscribe_driver_location', {
// 'ride_id': rideId,
// 'driver_id': driverId,
// });
// Log.print("📡 Re-subscribed to driver location after reconnect");
// }
// // ✅ [FIX] إيقاف الـ Fast Polling لأن السوكيت عاد
// if (_isActiveRideState()) {
// Log.print("✅ Socket back online — stopping Fast Polling Fallback");
// _masterTimer?.cancel();
// _masterTimer = null;
// }
// update();
// });
// // 🔄 [FIX] معالج محاولات إعادة الاتصال (للتشخيص)
// socket.onReconnectAttempt((attemptNumber) {
// Log.print("🔄 Socket Reconnect Attempt #$attemptNumber...");
// });
// // ❌ معالج الأخطاء
// socket.onError((error) {
// Log.print("❌ Socket Error: $error");
// isSocketConnected = false;
// });
// // 📩 معالج تحديثات الحالة
// socket.on('ride_status_change', (data) {
// Log.print("📩 Socket Event: ride_status_change -> $data");
// _handleRideStatusChangeWithSocket(data);
// });
// // 📍 معالج موقع السائق
// socket.on('driver_location_update', (data) {
// handleDriverLocationUpdate(data);
// });
// }
// void _startHeartbeat() {
// _heartbeatTimer?.cancel();
// _heartbeatTimer = Timer.periodic(const Duration(seconds: 25), (timer) {
// if (isSocketConnected && socket.connected) {
// socket.emit('heartbeat',
// {'passenger_id': box.read(BoxName.passengerID).toString()});
// }
// });
// }
// // دالة مساعدة
// bool _isActiveRideState() {
// return currentRideState.value == RideState.searching ||
// currentRideState.value == RideState.driverApplied ||
// currentRideState.value == RideState.driverArrived ||
// currentRideState.value == RideState.inProgress;
// }
// /// فحص سريع: هل السوكيت يعمل ويرسل بيانات؟
// bool _isSocketHealthy() {
// if (!isSocketConnected) return false;
// if (_lastSocketLocationTime == null) return false;
// final diff = DateTime.now().difference(_lastSocketLocationTime!).inSeconds;
// return diff < 20; // إذا آخر تحديث قبل أقل من 20 ثانية
// }
// /// 🧠 خوارزمية ذكية: حساب أقصر مسافة بين موقع السائق والـ Polyline (بدون API)
// double _calculateDistanceToPolyline(LatLng point, List<LatLng> polyline) {
// if (polyline.isEmpty) return 999.0;
// double minDistance = double.infinity;
// for (int i = 0; i < polyline.length - 1; i++) {
// double d = _distToSegment(point, polyline[i], polyline[i + 1]);
// if (d < minDistance) minDistance = d;
// }
// return minDistance;
// }
// double _distToSegment(LatLng p, LatLng v, LatLng w) {
// double l2 = _dist2(v, w);
// if (l2 == 0) return _distanceBetween(p, v);
// double t = ((p.latitude - v.latitude) * (w.latitude - v.latitude) +
// (p.longitude - v.longitude) * (w.longitude - v.longitude)) /
// l2;
// t = max(0, min(1, t));
// return _distanceBetween(
// p,
// LatLng(v.latitude + t * (w.latitude - v.latitude),
// v.longitude + t * (w.longitude - v.longitude)));
// }
// double _dist2(LatLng v, LatLng w) {
// return pow(v.latitude - w.latitude, 2).toDouble() +
// pow(v.longitude - w.longitude, 2).toDouble();
// }
// double _distanceBetween(LatLng p1, LatLng p2) {
// return Geolocator.distanceBetween(
// p1.latitude, p1.longitude, p2.latitude, p2.longitude);
// }
// // ==============================================================================
// // 2. العقل المدبر: توجيه الحالات
// // ==============================================================================
// void _handleRideStatusChangeWithSocket(dynamic data) {
// if (data == null || data['status'] == null) return;
// String newStatus = data['status'].toString().toLowerCase();
// Log.print("🔔 Socket Status Update: $newStatus");
// // استخراج بيانات السائق إذا توفرت (تأتي من acceptRide.php)
// Map<String, dynamic>? driverInfo;
// if (data['driver_info'] != null && data['driver_info'] is Map) {
// driverInfo = Map<String, dynamic>.from(data['driver_info']);
// }
// switch (newStatus) {
// case 'accepted': // أو apply/applied حسب تسمية السيرفر
// _onDriverAcceptedWithSocket(data, driverData: driverInfo);
// break;
// case 'arrived':
// _onDriverArrivedWithSocket();
// break;
// case 'started': // أو begin
// _onRideStartedWithSocket();
// break;
// case 'finished': // أو ended
// _onRideFinishedWithSocket(data);
// break;
// case 'cancelled':
// _onRideCancelledWithSocket(data);
// break;
// case 'no_drivers_found':
// showNoDriverDialog();
// break;
// }
// }
// // ==============================================================================
// // 3. دوال المعالجة التفصيلية (Actions)
// // ==============================================================================
// void showNoDriverDialog() {
// Get.defaultDialog(
// title: "No Drivers Found".tr,
// middleText:
// "Sorry, there are no cars available of this type right now.".tr,
// textConfirm: "Refresh Map".tr,
// textCancel: "Cancel".tr,
// confirmTextColor: Colors.white,
// onConfirm: () {
// Get.back(); // إغلاق الديالوج
// restCounter();
// stopAllTimers();
// Get.offAll(() => MapPagePassenger()); // إعادة تحميل صفحة الخريطة
// },
// );
// }
// // أ) عند قبول السائق للرحلة
// // أ) عند قبول السائق للرحلة (معدلة)
// // دالة الاستقبال من السوكيت (تصبح مجرد محول)
// void _onDriverAcceptedWithSocket(dynamic data,
// {Map<String, dynamic>? driverData}) {
// // استخراج البيانات وتمريرها للدالة الموحدة
// Map<String, dynamic>? info = driverData;
// // دعم الهيكلية الجديدة
// if (info == null && data['driver_info'] != null) {
// info = Map<String, dynamic>.from(data['driver_info']);
// }
// // دعم الهيكلية القديمة (إن وجدت)
// else if (info == null && data['driverList'] != null) {
// // تحويل driverList إلى map إذا لزم الأمر
// }
// processRideAcceptance(driverData: info, source: "Socket");
// }
// void _fillDriverDataLocally(Map<String, dynamic> data) {
// try {
// // تعبئة المتغيرات بناءً على أسماء الحقول في acceptRide.php
// driverId = data['driver_id']?.toString() ?? '';
// driverPhone = data['phone']?.toString() ?? '';
// String fName = data['first_name']?.toString() ?? '';
// String lName = data['last_name']?.toString() ?? '';
// passengerName = lName.isNotEmpty
// ? "$fName $lName"
// : fName; // (هنا المتغير اسمه passengerName لكنه يحمل اسم السائق في الكود لديك)
// driverName = passengerName;
// make = data['make']?.toString() ?? '';
// model = data['model']?.toString() ?? '';
// carColor = data['color']?.toString() ?? '';
// colorHex = data['color_hex']?.toString() ?? '';
// licensePlate = data['car_plate']?.toString() ?? '';
// carYear = data['year']?.toString() ?? '';
// driverRate = data['ratingDriver']?.toString() ?? '5.0';
// driverToken = data['token']?.toString() ?? '';
// // إذا كان هناك أي بيانات أخرى تحتاجها الواجهة
// update();
// } catch (e) {
// Log.print("Error parsing socket driver data: $e");
// }
// }
// // دالة موحدة: تجلب المسار + الوقت + المسافة + ترسم الخط + تضبط الكاميرا
// Future<void> calculateDriverToPassengerRoute(
// LatLng driverPos, LatLng passengerPos) async {
// // 1. تجهيز الرابط (نفس API الـ Direction)
// // نستخدم overview=full للحصول على الرسمة، و steps=false لتخفيف البيانات
// final Map<String, String> queryParams = {
// 'fromLat': driverPos.latitude.toString(),
// 'fromLng': driverPos.longitude.toString(),
// 'toLat': passengerPos.latitude.toString(),
// 'toLng': passengerPos.longitude.toString(),
// };
// final uri =
// Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams);
// Log.print('📍 Calculating Driver Route: $uri');
// try {
// final response = await http.get(uri, headers: {
// 'x-api-key': Env.mapSaasKey,
// }).timeout(const Duration(seconds: 20));
// if (response.statusCode == 200) {
// final responseData = json.decode(response.body);
// // Support both old format (routes[0]) and new SaaS format (top-level)
// var routeData = responseData['routes'] != null
// ? responseData['routes'][0]
// : responseData;
// // 2. تحديث المتغيرات (المسافة والوقت)
// double durationSecondsRaw = (routeData['duration'] as num).toDouble();
// int finalDurationSeconds =
// (durationSecondsRaw * kDurationScalar).toInt();
// double distanceMeters = (routeData['distance'] as num).toDouble();
// timeToPassengerFromDriverAfterApplied = finalDurationSeconds;
// remainingTimeToPassengerFromDriverAfterApplied = finalDurationSeconds;
// distanceByPassenger = distanceMeters.toStringAsFixed(0);
// // تحديث نصوص الواجهة
// int minutes = (finalDurationSeconds / 60).floor();
// int seconds = finalDurationSeconds % 60;
// stringRemainingTimeToPassenger =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// Log.print(
// '✅ Driver Route Info: $minutes min, ${distanceMeters.toInt()} m');
// // 3. معالجة الرسم (Polyline)
// // SaaS uses 'points', OSRM uses 'geometry'
// String pointsString =
// routeData['points'] ?? routeData['geometry'] ?? "";
// if (pointsString.isNotEmpty) {
// List<LatLng> decodedPoints =
// await compute(decodePolylineIsolate, pointsString);
// // حفظ نسخة للمقارنة
// _currentDriverRoutePoints = decodedPoints;
// // إزالة خط مسار السائق القديم فقط
// polyLines = polyLines
// .where((p) => p.polylineId.value != 'driver_route')
// .toSet();
// // إضافة الخط الجديد (بستايل مميز للسائق)
// polyLines = {
// ...polyLines,
// Polyline(
// polylineId: const PolylineId('driver_route'),
// points: decodedPoints,
// color:
// const Color(0xFF333333), // لون مختلف عن مسار الرحلة الأساسي
// width: 5,
// )
// };
// }
// // 4. ضبط الكاميرا لتشمل السائق والراكب
// _fitCameraToPoints(driverPos, passengerPos);
// update(); // تحديث واحد للكل
// }
// } catch (e) {
// Log.print('❌ Error calculating driver route: $e');
// }
// }
// Future<void> _checkAndRecalculateIfDeviated(LatLng driverPos) async {
// // 1. شروط الخروج السريع
// if (_isRecalculatingRoute || _currentDriverRoutePoints.isEmpty) return;
// // 2. حساب المسافة لأقرب نقطة في المسار (خوارزمية سريعة)
// // نستخدم مكتبة Geolocator أو حساب رياضي بسيط
// double minDistance = 100000.0;
// // لتقليل الحمل، لا نفحص كل النقاط، نفحص عينة (كل 5 نقاط مثلاً) أو الكل إذا المسار قصير
// for (var point in _currentDriverRoutePoints) {
// double dist = Geolocator.distanceBetween(driverPos.latitude,
// driverPos.longitude, point.latitude, point.longitude);
// if (dist < minDistance) minDistance = dist;
// }
// // 3. اتخاذ القرار
// if (minDistance > _deviationThresholdMeters) {
// Log.print("⚠️ Driver deviated ($minDistance m). Recalculating route...");
// _isRecalculatingRoute = true;
// // إعادة حساب المسار من موقع السائق الجديد
// await calculateDriverToPassengerRoute(driverPos, passengerLocation);
// _isRecalculatingRoute = false;
// }
// }
// // ب) عند وصول السائق
// void _onDriverArrivedWithSocket() {
// Log.print("🚖 Driver Arrived (Socket)");
// processDriverArrival("Socket");
// }
// // ج) عند بدء الرحلة
// void _onRideStartedWithSocket() {
// Log.print("🚀 Ride Started (Socket)");
// processRideBegin(source: "Socket");
// }
// // ربط السوكيت
// // د) عند انتهاء الرحلة (Socket Listener)
// void _onRideFinishedWithSocket(dynamic data) {
// Log.print("🏁 Ride Finished (Socket)");
// // نحاول استخراج DriverList من البيلود القادم من PHP
// // في finish_ride_updates.php أسميناه 'DriverList'
// var rawList = data['DriverList'];
// List<dynamic> listToSend = [];
// if (rawList != null) {
// if (rawList is List) {
// listToSend = rawList;
// } else if (rawList is String) {
// // احتياطاً لو وصل كنص
// try {
// listToSend = jsonDecode(rawList);
// } catch (e) {
// Log.print("Error: $e");
// }
// }
// }
// // إذا كانت القائمة فارغة، نحاول بناءها من البيانات المتفرقة (Fallback)
// if (listToSend.isEmpty && data['price'] != null) {
// listToSend = [
// driverId, // 0
// rideId, // 1
// driverToken, // 2
// data['price'].toString() // 3
// ];
// }
// // استدعاء المعالج الموحد
// processRideFinished(listToSend, source: "Socket");
// }
// // هـ) عند الإلغاء
// void _onRideCancelledWithSocket(dynamic data) {
// processRideCancelledByDriver(data, source: "Socket");
// }
// // ==============================================================================
// // 4. إدارة تتبع الموقع (Polling) - مفصولة عن السوكيت
// // ==============================================================================
// // متغير لمنع التكرار (Race Condition Guard)
// bool _isCancelProcessed = false;
// /// **معالجة إلغاء الرحلة الموحدة (Gatekeeper)**
// ///
// /// تستدعى من [Socket] أو [FCM] عند قيام السائق بإلغاء الرحلة.
// /// تضمن عدم تضارب الإشعارات وتوحد تجربة المستخدم.
// Future<void> processRideCancelledByDriver(dynamic data,
// {String source = "Unknown"}) async {
// if (_isCancelProcessed) return;
// _isCancelProcessed = true;
// stopAllTimers();
// if (Get.isDialogOpen == true) Get.back();
// await RideLiveNotification.cancel();
// IosLiveActivityService.endRideActivity(); // ✅ أضف هذا السطر
// PipService.disablePip(); // ✅ إيقاف PiP عند انتهاء الرحلة
// if (Get.isDialogOpen == true) Get.back();
// await RideLiveNotification.cancel();
// Get.defaultDialog(
// title: "Sorry 😔".tr, // استخدام المفتاح الإنجليزي
// titleStyle:
// const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
// barrierDismissible: false,
// content: Column(
// children: [
// const Icon(Icons.cancel_presentation,
// size: 50, color: Colors.redAccent),
// const SizedBox(height: 10),
// Text(
// "The driver cancelled the trip for an emergency reason.\nDo you want to search for another driver immediately?"
// .tr,
// textAlign: TextAlign.center,
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () {
// Get.back();
// handleNoDriverFound();
// },
// child: Text("Cancel Trip".tr,
// style: const TextStyle(color: Colors.grey)),
// ),
// ElevatedButton.icon(
// style:
// ElevatedButton.styleFrom(backgroundColor: AppColor.primaryColor),
// icon: const Icon(Icons.refresh, color: Colors.white),
// label: Text("Search for another driver".tr,
// style: const TextStyle(color: Colors.white)),
// onPressed: () {
// Get.back();
// retrySearchForDrivers();
// },
// ),
// ],
// );
// }
// Future<void> handleNoDriverFound() async {
// stopAllTimers();
// await RideLiveNotification.cancel();
// IosLiveActivityService.endRideActivity(); // ✅ أضف هذا السطر
// PipService.disablePip(); // ✅ إيقاف PiP
// _isCancelProcessed = false;
// currentRideState.value = RideState.noRide;
// resetAllMapStates();
// Get.offAll(() => const MapPagePassenger());
// Get.defaultDialog(
// title: "We apologize 😔".tr,
// middleText: "No drivers found at the moment.\nPlease try again later.".tr,
// confirm: ElevatedButton(
// onPressed: () => Navigator.pop(Get.context!),
// child: Text("Ok".tr),
// ),
// );
// }
// /// **إعادة البحث عن سائقين (Retry Search)**
// ///
// /// تقوم باستدعاء السكربت لإعادة تفعيل الرحلة وبدء عداد البحث من جديد.
// void retrySearchForDrivers() async {
// _isCancelProcessed = false;
// isSearchingWindow = true;
// currentRideState.value = RideState.searching;
// driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr;
// update();
// try {
// Log.print("🔄 Retrying search for ride ID: $rideId");
// // تجهيز البيانات المخزنة للإرسال
// var payload = {
// "ride_id": rideId.toString(),
// "passenger_id": box.read(BoxName.passengerID).toString(),
// "passenger_name": box.read(BoxName.name).toString(),
// "passenger_phone": box.read(BoxName.phone).toString(),
// "passenger_email": box.read(BoxName.email).toString(),
// "passenger_token": box.read(BoxName.tokenFCM).toString(),
// "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
// "passenger_rating": "5.0", // أو قراءة التقييم الحقيقي إن وجد
// // قراءة البيانات من المتغيرات المحفوظة في الكنترولر أو الـ Box
// "start_lat": startLocation.latitude.toString(),
// "start_lng": startLocation.longitude.toString(),
// "end_lat": endLocation.latitude.toString(),
// "end_lng": endLocation.longitude.toString(),
// "start_name": startNameAddress,
// "end_name": endNameAddress,
// "distance": distance.toString(),
// "distance_text": distanceByPassenger ?? "",
// "duration_text": durationToPassenger.toString(),
// "price": totalPassenger.toString(),
// "price_for_driver": costForDriver.toString(),
// "car_type": box.read(BoxName.carType).toString(),
// "is_wallet": Get.find<PaymentController>().isWalletChecked.toString(),
// // الخطوات (اختياري)
// "has_steps": Get.find<WayPointController>().wayPoints.length > 1
// ? "true"
// : "false",
// // يمكنك إضافة الخطوات إذا كانت لديك في مصفوفة
// };
// var response = await CRUD().post(
// link: "${AppLink.rideServerSide}/rides/retry_search_drivers.php",
// payload: payload,
// );
// if (response['status'] == 'success') {
// Log.print("✅ Search reset successfully.");
// startSearchingTimer();
// } else {
// mySnackbarWarning("Failed to search, please try again later".tr);
// handleNoDriverFound();
// }
// } catch (e) {
// Log.print("Error retrying search: $e");
// mySnackbarWarning("Please check your internet connection".tr);
// handleNoDriverFound();
// }
// }
// Timer? _searchTimer;
// /// **بدء مؤقت البحث (Search Timer)**
// ///
// /// يبدأ عداداً (مثلاً 90 ثانية). إذا لم يتم قبول الرحلة خلال هذه المدة،
// /// يتم إنهاء البحث واستدعاء [handleNoDriverFound].
// Future<void> startSearchingTimer() async {
// _searchTimer?.cancel();
// int seconds = 0;
// Log.print("⏳ Search Timer Started (90s)...");
// await RideLiveNotification.showSearching(driversStatusForSearchWindow);
// _searchTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
// seconds++;
// // إذا تغيرت الحالة (مثلاً سائق قبل الرحلة)، نوقف العداد
// if (currentRideState.value != RideState.searching) {
// timer.cancel();
// return;
// }
// // انتهاء الوقت (90 ثانية)
// if (seconds >= 90) {
// timer.cancel();
// handleNoDriverFound(); // ⏳ انتهى الوقت ولم نجد سائقاً
// }
// });
// }
// // متغير لمنع التكرار (Race Condition Guard)
// bool _isArrivalProcessed = false;
// /// **معالجة وصول السائق الموحدة (Unified Driver Arrival Handler)**
// ///
// /// تقوم هذه الدالة بإدارة حدث وصول السائق إلى موقع الراكب، وتعمل كـ "حارس بوابة" (Gatekeeper)
// /// لتوحيد المصادر سواء كانت من **WebSocket** أو **Firebase Notification**.
// ///
// /// **المهام التي تقوم بها:**
// /// 1. **منع التضارب (Race Condition Guard):** تتأكد أن الحدث لم يتم تنفيذه مسبقاً لتجنب تكرار الإشعارات أو إعادة ضبط العدادات.
// /// 2. **تحديث الحالة:** تغير حالة الرحلة فوراً إلى [RideState.driverArrived].
// /// 3. **تفعيل الواجهة:** تظهر ديالوج "السائق وصل" وتبدأ عداد الانتظار المجاني (5 دقائق).
// ///
// /// * [source] : نص يوضح مصدر الاستدعاء (مثل "Socket" أو "FCM") لأغراض التتبع (Logging).
// Future<void> processDriverArrival(String source) async {
// // 1. الحارس: إذا تم التنفيذ سابقاً، توقف
// if (currentRideState.value == RideState.driverArrived ||
// _isArrivalProcessed) {
// Log.print("✋ Ignored Arrival from $source. Already processed.");
// return;
// }
// _isArrivalProcessed = true;
// Log.print("🚖 Driver Arrived via $source! Processing...");
// // 2. تحديث الحالة
// currentRideState.value = RideState.driverArrived;
// statusRide = 'Arrived';
// await RideLiveNotification.showDriverArrived(driverName ?? '');
// // 3. تشغيل واجهة الوصول والعداد
// driverArrivePassengerDialoge();
// startTimerDriverWaitPassenger5Minute();
// // 4. إزالة مسار السائق واستعادة مسار الراكب للوجهة
// if (polylineCoordinates.isNotEmpty) {
// _playRouteAnimation(polylineCoordinates, lastComputedBounds);
// }
// update();
// }
// // متغير لمنع التكرار
// bool _isFinishProcessed = false;
// /// **معالجة إنهاء الرحلة الموحدة (Unified Ride Finish Handler)**
// ///
// /// تستدعى عند استلام حدث النهاية من السوكيت أو FCM.
// /// تقوم بإغلاق السوكيت، إيقاف التتبع، والانتقال لشاشة التقييم والدفع.
// ///
// /// * [driverList]: قائمة البيانات [driverId, rideId, token, price].
// Future<void> processRideFinished(List<dynamic> driverList,
// {String source = "Unknown"}) async {
// // 1. الحارس: منع التكرار
// if (currentRideState.value == RideState.finished || _isFinishProcessed) {
// Log.print("✋ Ignored Finish Request from $source. Already Finished.");
// return;
// }
// _isFinishProcessed = true;
// Log.print(
// "🏁 Ride Finished via $source. Price: ${driverList.length > 3 ? driverList[3] : 'N/A'}");
// // 2. تحديث الحالة
// currentRideState.value = RideState.finished;
// // 3. تنظيف الموارد
// disposeRideSocket(); // إغلاق السوكيت
// _stopDriverLocationPolling(); // إيقاف تتبع الموقع
// if (Get.isRegistered<AudioRecorderController>()) {
// Get.find<AudioRecorderController>().stopRecording();
// }
// // إغلاق أي ديالوج سابق
// if (Get.isDialogOpen == true) Get.back();
// // 4. إشعار "لا تنسى أغراضك"
// NotificationController().showNotification(
// 'Alert'.tr,
// "Please make sure not to leave any personal belongings in the car.".tr,
// 'tone1',
// );
// IosLiveActivityService.endRideActivity();
// PipService.disablePip(); // ✅ إيقاف PiP
// await RideLiveNotification.cancel();
// // 5. استخراج البيانات والانتقال
// if (driverList.length >= 4) {
// String price = driverList[3].toString();
// // الانتقال لصفحة التقييم
// Get.offAll(() => RateDriverFromPassenger(), arguments: {
// 'driverId': driverList[0].toString(),
// 'rideId': driverList[1].toString(),
// 'price': price
// });
// }
// }
// void _startDriverLocationPollingWithTimer() {
// Log.print("📍 Starting Driver Location Polling (6s interval)");
// _locationPollingTimer?.cancel();
// // استدعاء فوري لأول مرة
// // getDriverCarsLocationToPassengerAfterApplied();
// _locationPollingTimer = Timer.periodic(Duration(seconds: 6), (timer) {
// // شرط التوقف: إذا انتهت الرحلة أو ألغيت
// if (currentRideState.value == RideState.finished ||
// currentRideState.value == RideState.cancelled ||
// currentRideState.value == RideState.noRide) {
// timer.cancel();
// return;
// }
// // جلب الموقع وتحديث الماركر
// getDriverCarsLocationToPassengerAfterApplied();
// });
// }
// void _stopDriverLocationPolling() {
// Log.print("🛑 Stopping Location Polling");
// _locationPollingTimer?.cancel();
// _locationPollingTimer = null;
// }
// // ==============================================================================
// // 5. التنظيف والإغلاق
// // ==============================================================================
// void disposeRideSocket() {
// if (socket != null) {
// socket!.disconnect();
// socket!.dispose();
// // socket = null;
// isSocketConnected = false;
// Log.print("🔌 Socket Disposed");
// }
// }
// /// ==============================================================================
// /// 6. معالجة تحديث موقع السائق من السوكيت
// /// ==============================================================================
// void handleDriverLocationUpdate(dynamic data) {
// if (!isSocketConnected || data == null) return;
// // 🔥 1. تسجيل وقت استلام البيانات (تغذية الـ Watchdog)
// _lastSocketLocationTime = DateTime.now();
// _socketLocationUpdatesCount++;
// // 🧠 إذا وصلتنا 3 تحديثات ناجحة من السوكيت، أوقف البولينج (إن كان يعمل)
// if (_socketLocationUpdatesCount >= 3 && _locationPollingTimer != null) {
// Log.print("✅ Socket delivering locations reliably. Stopping polling.");
// _stopDriverLocationPolling();
// }
// try {
// double lat = double.tryParse(data['latitude']?.toString() ?? '0') ?? 0;
// double lng = double.tryParse(data['longitude']?.toString() ?? '0') ?? 0;
// double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0;
// if (lat == 0 || lng == 0) return;
// LatLng newPos = LatLng(lat, lng);
// // تحديث القائمة (نفس المنطق القديم)
// if (driverCarsLocationToPassengerAfterApplied.isEmpty) {
// driverCarsLocationToPassengerAfterApplied.add(newPos);
// } else {
// driverCarsLocationToPassengerAfterApplied[0] = newPos;
// }
// currentLocationOfDrivers = newPos;
// // ------------------------------------------------------------------
// // 🔥 2. Smart Rerouting Logic ( deviation > 50m )
// // ------------------------------------------------------------------
// if (_currentDriverRoutePoints.isNotEmpty) {
// double deviation =
// _calculateDistanceToPolyline(newPos, _currentDriverRoutePoints);
// if (deviation > 50.0) {
// Log.print(
// "⚠️ Driver deviated by ${deviation.toStringAsFixed(1)}m. Smart Rerouting triggered.");
// // إعادة رسم المسار محلياً (لا يتم استدعاؤه إلا عند الانحراف الحقيقي)
// calculateDriverToPassengerRoute(newPos, passengerLocation);
// }
// }
// // 🔥 تحديث الكاميرا: ضمان بقاء السيارة في منتصف الخريطة
// // ------------------------------------------------------------------
// // ملاحظة: تأكد من أن mapController قد تم تهيئته (initialized)
// if (mapController != null) {
// // نستخدم animateCamera لحركة ناعمة بدلاً من moveCamera القاسية
// mapController!.animateCamera(CameraUpdate.newLatLng(newPos));
// }
// // ------------------------------------------------------------------
// // تحديث الوقت المتبقي (إذا أرسله السيرفر)
// if (data['eta_seconds'] != null) {
// int etaSeconds = int.tryParse(data['eta_seconds'].toString()) ?? 0;
// if (etaSeconds > 0) {
// remainingTimeToPassengerFromDriverAfterApplied = etaSeconds;
// int minutes = (etaSeconds / 60).floor();
// int seconds = etaSeconds % 60;
// stringRemainingTimeToPassenger =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// }
// }
// // تحديث الماركر
// _updateDriverMarker(newPos, heading);
// } catch (e) {
// Log.print('Error in handleDriverLocationUpdate: $e');
// }
// }
// // دالة مساعدة لتحديث ماركر السائق
// void _updateDriverMarker(LatLng position, double heading) {
// const String markerId = 'driver_location';
// final mId = MarkerId(markerId);
// final existingMarker = markers.cast<Marker?>().firstWhere(
// (m) => m?.markerId == mId,
// orElse: () => null,
// );
// if (existingMarker != null) {
// _smoothlyUpdateMarker(existingMarker, position, heading, carIcon);
// } else {
// markers = {
// ...markers,
// Marker(
// markerId: mId,
// position: position,
// icon: InlqBitmap.fromStyleImage(carIcon),
// rotation: heading,
// anchor: const Offset(0.5, 0.5),
// ),
// };
// update();
// }
// }
// // === إضافة متغير للتحكم ===
// bool _isUsingFallback = false;
// void _startPollingFallback() {
// if (_isUsingFallback) return;
// Log.print('🔄 Starting Polling Fallback Mode');
// _isUsingFallback = true;
// // استخدام _handleRideState الموجود (كما كان)
// _startMasterTimer();
// }
// void handleActiveRideOnStartup(dynamic data) {
// try {
// if (data == null || data['has_active_ride'] != true) {
// Log.print('[Startup] No active ride');
// currentRideState.value = RideState.noRide;
// _startMasterTimer();
// return;
// }
// Log.print('[Startup] ✅ Active ride found!');
// var rideData = data['ride'];
// rideId = rideData['ride_id'].toString();
// currentRideId = rideId;
// driverId = rideData['driver_id']?.toString() ?? '';
// String status = rideData['status']?.toString().toLowerCase() ?? '';
// // تحديد الحالة
// if (status == 'waiting' || status == 'searching') {
// currentRideState.value = RideState.searching;
// isSearchingWindow = true;
// } else if (status == 'apply' || status == 'applied') {
// currentRideState.value = RideState.driverApplied;
// statusRide = 'Apply';
// // الاشتراك في موقع السائق
// socket.emit('subscribe_driver_location', {
// 'ride_id': rideId,
// 'driver_id': driverId,
// });
// // استعادة بيانات السائق
// if (rideData['driver_info'] != null) {
// var dInfo = rideData['driver_info'];
// passengerName = dInfo['first_name']?.toString() ?? '';
// driverPhone = dInfo['phone']?.toString() ?? '';
// model = dInfo['model']?.toString() ?? '';
// licensePlate = dInfo['license_plate']?.toString() ?? '';
// }
// } else if (status == 'arrived') {
// currentRideState.value = RideState.driverArrived;
// statusRide = 'Arrived';
// isDriverArrivePassenger = true;
// } else if (status == 'begin' || status == 'started') {
// currentRideState.value = RideState.inProgress;
// statusRide = 'Begin';
// rideTimerBegin = true;
// // استعادة Polyline
// if (rideData['polyline'] != null) {
// _restorePolyline(rideData['polyline']);
// }
// rideIsBeginPassengerTimer();
// }
// update();
// _startMasterTimer(); // للـ Safety Checks
// } catch (e) {
// Log.print('[Startup] Error: $e');
// currentRideState.value = RideState.noRide;
// _startMasterTimer();
// }
// }
// Future<void> _restorePolyline(String polylineString) async {
// try {
// List<LatLng> points =
// await compute(decodePolylineIsolate, polylineString);
// polylineCoordinates.clear();
// polylineCoordinates.addAll(points);
// polyLines.clear();
// polyLines = {
// ...polyLines,
// Polyline(
// polylineId: const PolylineId('route_direct'),
// points: polylineCoordinates,
// color: const Color(0xFF2196F3),
// width: 6,
// )
// };
// update();
// } catch (e) {
// Log.print('Error restoring polyline: $e');
// }
// }
// // متغير لمنع التكرار (Race Condition Guard)
// bool _isAcceptanceProcessed = false;
// // ==============================================================================
// // الدالة الموحدة لمعالجة القبول (من السوكيت أو FCM)
// // ==============================================================================
// Future<void> processRideAcceptance(
// {Map<String, dynamic>? driverData, required String source}) async {
// // 1. الحماية: إذا تم المعالجة مسبقاً، تجاهل
// // نستثني حالة واحدة: إذا كنا في وضع البحث (Searching) نقبل الأمر
// // إذا كنا في driverApplied، نتجاهل الأمر
// if (currentRideState.value != RideState.searching &&
// _isAcceptanceProcessed) {
// Log.print("✋ Ignored Acceptance from $source. Already processed.");
// return;
// }
// _rideAcceptedViaSource = source;
// _socketLocationUpdatesCount = 0;
// _isAcceptanceProcessed = true; // قفل الباب
// Log.print("🚀 Winner: $source triggered acceptance! Processing...");
// // 2. إيقاف جميع التايمرات القديمة فوراً
// _masterTimer?.cancel();
// // stopSearchingTimer(); // إذا كان لديك تايمر للبحث
// // 3. تحديث الحالة في الواجهة
// currentRideState.value = RideState.driverApplied;
// statusRide = 'Apply';
// isSearchingWindow = false;
// // 4. معالجة البيانات (تعبئة المتغيرات)
// if (driverData != null && driverData.isNotEmpty) {
// Log.print("📥 Populating Data from $source payload...");
// _fillDriverDataLocally(driverData);
// } else {
// Log.print("⚠️ No Data in Payload. Fallback to API.");
// await getUpdatedRideForDriverApply(rideId);
// }
// // أضف هذا بعد السطر الذي تستدعي فيه RideTrackingNative
// await IosLiveActivityService.startRideActivity(
// rideId: rideId,
// driverName: driverName ?? 'السائق',
// carDetails: '$make • $carColor',
// etaText: stringRemainingTimeToPassenger,
// progress: 0.0,
// );
// // إشعارات (الأسعار، الأمان...)
// _showRideStartNotifications();
// final etaText = stringRemainingTimeToPassenger; // مثال: "8 دقائق"
// final carInfo = '$make • $model • $licensePlate';
// await RideLiveNotification.showDriverOnWay(
// driverName: driverName,
// etaText: etaText,
// carInfo: carInfo,
// );
// update(); // تحديث الواجهة لإظهار بيانات السائق
// // 5. 🔥 العمليات الجغرافية (المسار والوقت) 🔥
// // أ) جلب موقع السائق الأولي
// await getDriverCarsLocationToPassengerAfterApplied();
// _startSocketWatchdog();
// // ب) رسم المسار وحساب الوقت
// if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
// LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last;
// // نستخدم الدالة الموحدة الجديدة للحساب والرسم
// await calculateDriverToPassengerRoute(driverPos, passengerLocation);
// // ج) تشغيل التايمر المحلي (للعد التنازلي فقط)
// startTimerFromDriverToPassengerAfterApplied();
// }
// final int timeToPassengerSeconds =
// timeToPassengerFromDriverAfterApplied; // مثلاً من السيرفر
// final double distanceDriverToPassengerMeters =
// double.tryParse(distanceByPassenger.toString()) ?? 0.0;
// // [PiP] تفعيل PiP عند بدء الرحلة (سيدخل وضع النافذة العائمة عند خروج المستخدم)
// PipService.enablePip();
// // 6. 🔥 القرار الذكي: هل نبدأ البولينج أم نعتمد على السوكيت؟ 🔥
// if (source == "Socket" && isSocketConnected) {
// // ✅ السوكيت هو من أخبرنا بالقبول — نثق به ولا نشغل البولينج
// Log.print(
// "🧠 Smart Mode: Socket accepted ride. Skipping polling, relying on WebSocket.");
// // الـ watchdog وحده يكفي كشبكة أمان
// } else {
// // ⚠️ القبول من FCM أو Polling — نشغل البولينج كالمعتاد
// Log.print("🔄 Fallback Mode: $source accepted ride. Starting polling.");
// _startDriverLocationPollingWithTimer();
// }
// }
// Timer? _watchdogTimer;
// void _startSocketWatchdog() {
// _watchdogTimer?.cancel();
// Log.print("👀 Starting Socket Watchdog (Hybrid Mode)...");
// _watchdogTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
// if (currentRideState.value != RideState.driverApplied &&
// currentRideState.value != RideState.driverArrived &&
// currentRideState.value != RideState.inProgress) {
// timer.cancel();
// return;
// }
// final lastTime = _lastSocketLocationTime ??
// DateTime.now().subtract(const Duration(minutes: 1));
// final difference = DateTime.now().difference(lastTime).inSeconds;
// if (difference < 15 && isSocketConnected) {
// // ✅ السوكيت صحي — تأكد أن البولينج متوقف إذا كنا في وضع السوكيت
// if (_locationPollingTimer != null &&
// _rideAcceptedViaSource == "Socket") {
// Log.print("✅ Socket recovered. Stopping polling fallback.");
// _stopDriverLocationPolling();
// }
// } else if (difference >= 15 && difference < 30) {
// // ⚠️ تأخر متوسط — جلب واحد فقط
// Log.print("⚠️ Socket silent for ${difference}s. Single API Poll...");
// await getDriverCarsLocationToPassengerAfterApplied();
// } else if (difference >= 30) {
// // 🔴 السوكيت ميت — تفعيل البولينج الكامل كـ fallback
// if (_locationPollingTimer == null) {
// Log.print(
// "🔴 Socket dead for ${difference}s. Activating polling fallback!");
// _startDriverLocationPollingWithTimer();
// } else {
// // البولينج يعمل بالفعل، فقط أكمل
// await getDriverCarsLocationToPassengerAfterApplied();
// }
// }
// });
// }
// // قائمة بأنصاف الأقطار (بالأمتار) لكل مرحلة
// final List<int> _searchRadii = [
// 2400,
// 3000,
// 3100
// ]; // 0 ثانية، 30 ثانية، 60 ثانية
// // المدة الزمنية لكل مرحلة بحث (بالثواني)
// final int _searchPhaseDurationSeconds = 30;
// // المهلة الإجمالية للبحث قبل عرض خيار زيادة السعر
// final int _totalSearchTimeoutSeconds = 90; // 90 ثانية
// // --- noRide throttling ---
// int _noRideSearchCount = 0;
// final int _noRideMaxTries = 3; // نفذ البحث 6 مرات فقط
// final int _noRideIntervalSec = 5; // بين كل محاولة وأخرى 5 ثواني
// DateTime? _noRideNextAllowed; // متى نسمح بالمحاولة التالية
// bool _noRideSearchCapped = false; // وصلنا للحد وتوقفنا
// // ============== new design to manage ride state ==============
// // === 1. حالة الرحلة والمؤقت الرئيسي (Single Source of Truth) ===
// Rx<RideState> currentRideState = RideState.noRide.obs;
// Timer? _masterTimer;
// final int _pollingIntervalSeconds = 13; // فاصل زمني موحد للاستعلام
// void _startMasterTimer() {
// // نضمن أن مؤقت واحد فقط يعمل في أي وقت
// _masterTimer?.cancel();
// _masterTimer =
// Timer.periodic(Duration(seconds: _pollingIntervalSeconds), (_) {
// _handleRideState(currentRideState.value);
// });
// }
// void stopAllTimers() {
// Log.print('🛑 FORCE STOP: Stopping ALL Timers and Streams 🛑');
// // 1. إيقاف الماكينة الرئيسية
// _masterTimer?.cancel();
// _masterTimer = null;
// // 2. إيقاف مؤقتات تتبع السائق
// timerToPassengerFromDriverAfterApplied?.cancel();
// _timer?.cancel();
// _uiCountdownTimer?.cancel();
// // 3. إيقاف مؤقتات الخريطة
// _animationTimers.forEach((key, timer) => timer.cancel());
// _animationTimers.clear();
// // 4. إغلاق الستريمز
// if (!_rideStatusStreamController.isClosed)
// _rideStatusStreamController.close();
// if (!_beginRideStreamController.isClosed)
// _beginRideStreamController.close();
// if (!timerController.isClosed) timerController.close();
// // 5. تصفير العدادات لمنع إعادة الدخول
// isTimerRunning = false;
// isBeginRideFromDriverRunning = false;
// _isFetchingDriverLocation = false;
// update();
// }
// final int _maxNoRideSearch = 3; // عدد المرات القصوى
// final int _noRideDelaySeconds = 6; // الفاصل الزمني بين كل بحث
// //
// //
// // !!! يرجى استبدال الدالة القديمة بالكامل بهذه الدالة الجديدة !!!
// //
// //
// bool _isStateProcessing = false;
// Future<void> _handleRideState(RideState state) async {
// if (_isRatingScreenOpen) {
// Log.print('⛔ Rating Screen is Open. Skipping Logic.');
// stopAllTimers(); // تأكيد إضافي للإيقاف
// return;
// }
// Log.print('Handling state: $state');
// // int effectivePollingInterval = _pollingIntervalSeconds;
// // الحصول على الفاصل الزمني من الخريطة
// int effectivePollingInterval =
// _pollingIntervals[state] ?? _pollingIntervalSeconds;
// switch (state) {
// case RideState.noRide:
// final now = DateTime.now();
// if (_noRideSearchCount >= _noRideMaxTries) {
// if (!_noRideSearchCapped) {
// _noRideSearchCapped = true;
// Log.print('[noRide] search capped at $_noRideMaxTries attempts');
// }
// break;
// }
// if (_noRideNextAllowed != null && now.isBefore(_noRideNextAllowed!)) {
// break;
// }
// _noRideSearchCount++;
// Log.print('_noRideSearchCount: $_noRideSearchCount');
// _noRideNextAllowed = now.add(Duration(seconds: _noRideIntervalSec));
// String currentCarType = box.read(BoxName.carType) ?? 'yet';
// getCarsLocationByPassengerAndReloadMarker();
// getNearestDriverByPassengerLocation();
// break;
// case RideState.cancelled:
// Log.print('[handleRideState] Ride cancelled. Stopping polling.');
// stopAllTimers();
// // effectivePollingInterval = 3600;
// break;
// case RideState.preCheckReview:
// stopAllTimers();
// _checkLastRideForReview();
// break;
// case RideState.searching:
// // Guard: Don't poll if ride is not registered yet
// if (rideId == 'yet' || rideId.isEmpty) break;
// // 1. التحقق من حالة الطلب (هل قبله أحد؟) عبر البولينج كشبكة أمان
// try {
// String statusFromServer = await getRideStatus(rideId);
// if (statusFromServer == 'Apply' || statusFromServer == 'Applied') {
// await processRideAcceptance(source: "Polling");
// break;
// }
// } catch (e) {
// Log.print('Error polling getRideStatus: $e');
// }
// final now = DateTime.now();
// final int elapsedSeconds = now.difference(_searchStartTime!).inSeconds;
// // انتهاء وقت البحث الكلي
// if (elapsedSeconds > _totalSearchTimeoutSeconds) {
// stopAllTimers();
// currentRideState.value = RideState.noRide;
// isSearchingWindow = false;
// update();
// _showIncreaseFeeDialog();
// break;
// }
// // 2. إدارة مراحل البحث (توسيع النطاق)
// // السيناريو الجديد: لا نقوم بالقصف العشوائي، نرسل بناء على المرحلة أو مرور وقت كافٍ لدخول سائقين جدد
// int targetPhase =
// (elapsedSeconds / _searchPhaseDurationSeconds).floor();
// if (targetPhase >= _searchRadii.length) {
// targetPhase = _searchRadii.length - 1;
// }
// // هل تغيرت المرحلة (توسع النطاق)؟ أو هل مر 10 ثواني منذ آخر محاولة إرسال؟
// // هذا يمنع إرسال الإشعار في كل دورة (كل 5 ثواني) ويقلل الازعاج
// bool isNewPhase = targetPhase > _currentSearchPhase;
// bool timeToScanForNewDrivers =
// (elapsedSeconds % 15 == 0); // كل 15 ثانية نفحص الدخول الجديد
// if (isNewPhase || timeToScanForNewDrivers || elapsedSeconds < 5) {
// _currentSearchPhase = targetPhase;
// int currentRadius = _searchRadii[_currentSearchPhase];
// Log.print(
// '[Search Logic] Scanning for drivers. Phase: $_currentSearchPhase, Radius: $currentRadius');
// // استدعاء دالة الإشعار الذكية
// // _findAndNotifyNearestDrivers(currentRadius);
// }
// // تحديث نصوص الواجهة
// if (elapsedSeconds < 5) {
// driversStatusForSearchWindow = 'Your order is being prepared'.tr;
// } else if (elapsedSeconds < 15) {
// driversStatusForSearchWindow = 'Your order sent to drivers'.tr;
// } else {
// driversStatusForSearchWindow =
// 'The drivers are reviewing your request'.tr;
// }
// update();
// break;
// case RideState.driverApplied:
// // effectivePollingInterval = 10;
// if (!_isDriverAppliedLogicExecuted) {
// Log.print('[handleRideState] Execution driverApplied logic.');
// rideAppliedFromDriver(true);
// _isDriverAppliedLogicExecuted = true;
// }
// if (!isSocketConnected) {
// try {
// String statusFromServer = await getRideStatus(rideId);
// if (statusFromServer == 'Arrived') {
// currentRideState.value = RideState.driverArrived;
// break;
// } else if (statusFromServer == 'Begin' ||
// statusFromServer == 'inProgress') {
// processRideBegin();
// break;
// }
// } catch (e) {
// Log.print('Error polling for Arrived/Begin status: $e');
// }
// }
// // 🧠 جلب الموقع فقط إذا السوكيت غير صحي
// if (!_isSocketHealthy()) {
// getDriverCarsLocationToPassengerAfterApplied();
// }
// break;
// case RideState.driverArrived:
// if (!_isDriverArrivedLogicExecuted) {
// _isDriverArrivedLogicExecuted = true;
// startTimerDriverWaitPassenger5Minute();
// driverArrivePassengerDialoge();
// }
// // effectivePollingInterval = 8;
// break;
// case RideState.inProgress:
// // effectivePollingInterval = 6;
// if (!isSocketConnected) {
// try {
// String statusFromServer = await getRideStatus(rideId);
// // !!! هنا التغيير الجذري !!!
// if (statusFromServer == 'Finished' ||
// statusFromServer == 'finished') {
// Log.print(
// '🏁 DETECTED FINISHED: Killing processes and forcing Review.');
// // 1. قتل العمليات فوراً
// stopAllTimers();
// // 2. تغيير الحالة الداخلية لمنع أي كود آخر من العمل
// currentRideState.value = RideState.preCheckReview;
// // 3. تنظيف الواجهة
// tripFinishedFromDriver();
// // 4. استدعاء شاشة التقييم فوراً
// _checkLastRideForReview();
// return; // خروج نهائي من الدالة لمنع أي كود بالأسفل من التنفيذ
// }
// } catch (e) {
// Log.print('Error polling status: $e');
// }
// }
// // بقية كود التتبع العادي (لن يتم الوصول إليه إذا انتهت الرحلة)
// if (!_isRideBeginLogicExecuted) {
// _isRideBeginLogicExecuted = true;
// _executeBeginRideLogic();
// }
// // 🧠 جلب الموقع فقط إذا السوكيت غير صحي
// if (!_isSocketHealthy()) {
// getDriverCarsLocationToPassengerAfterApplied();
// }
// break;
// case RideState.finished:
// tripFinishedFromDriver();
// stopAllTimers();
// effectivePollingInterval = 3600;
// break;
// }
// // تحديث الماكينة الرئيسية إذا تغير الفاصل الزمني
// _startMasterTimerWithInterval(effectivePollingInterval);
// }
// int _masterIntervalSeconds = -1;
// void _startMasterTimerWithInterval(int seconds) {
// // نفس الانترفَل؟ لا تعمل شيء
// if (_masterTimer != null && _masterIntervalSeconds == seconds) return;
// _masterIntervalSeconds = seconds;
// _masterTimer?.cancel();
// _masterTimer = Timer.periodic(Duration(seconds: seconds), (_) {
// _handleRideState(currentRideState.value);
// });
// }
// Future<void> _checkInitialRideStatus() async {
// // 1. جلب الحالة من السيرفر (باستخدام getRideStatusFromStartApp)
// await getRideStatusFromStartApp();
// String _status = rideStatusFromStartApp['data']['status'];
// // Log.print('rideStatusFromStartApp: ${rideStatusFromStartApp}');
// // Log.print('_status: ${_status}');
// if (_status == 'waiting' || _status == 'Apply' || _status == 'Begin') {
// // رحلة جارية
// rideId = rideStatusFromStartApp['data']['rideId'].toString();
// currentRideState.value = _status == 'waiting'
// ? RideState.searching
// : _status == 'Apply'
// ? RideState.driverApplied
// : _status == 'Begin'
// ? RideState.inProgress
// : _status == 'Cancel'
// ? RideState.cancelled
// : RideState.noRide;
// } else if (_status == 'Finished') {
// // رحلة منتهية/ملغاة
// if (rideStatusFromStartApp['data']['needsReview'] == 1) {
// currentRideState.value = RideState.preCheckReview;
// } else {
// currentRideState.value = RideState.noRide;
// }
// } else {
// currentRideState.value = RideState.noRide;
// }
// // بدء المعالجة الفورية
// _handleRideState(currentRideState.value);
// }
// Future<void> _checkLastRideForReview() async {
// Log.print('⭐ FORCE OPEN RATING PAGE (Get.to mode)');
// // جلب البيانات
// await getRideStatusFromStartApp();
// if (rideStatusFromStartApp['data'] == null) {
// currentRideState.value = RideState.noRide;
// _startMasterTimer();
// return;
// }
// String needsReview =
// rideStatusFromStartApp['data']['needsReview'].toString();
// if (needsReview == '1') {
// _isRatingScreenOpen = true;
// // 1. تجهيز البيانات (Arguments)
// var args = {
// 'driverId': rideStatusFromStartApp['data']['driver_id'].toString(),
// 'rideId': rideStatusFromStartApp['data']['rideId'].toString(),
// 'driverName': rideStatusFromStartApp['data']['driverName'],
// 'price': rideStatusFromStartApp['data']['price'],
// };
// // 2. استخدام Get.to مع await (هذا هو الحل الجذري)
// // الكود سيتوقف هنا ولن يكمل التنفيذ حتى يتم إغلاق صفحة التقييم
// await Get.to(
// () => RatingDriverBottomSheet(),
// arguments: args, // تمرير البيانات بالطريقة التي تريدها
// preventDuplicates: true, // لمنع فتح الصفحة مرتين
// popGesture: false, // لمنع السحب للرجوع (في iOS)
// );
// // 3. هذا الكود لن يتنفذ إلا بعد أن يضغط المستخدم "تم" في التقييم ويغلق الصفحة
// Log.print('✅ Rating Page Closed. Resetting App.');
// _isRatingScreenOpen = false;
// restCounter();
// currentRideState.value = RideState.noRide;
// _startMasterTimer(); // إعادة تشغيل البحث الآن فقط
// } else {
// currentRideState.value = RideState.noRide;
// _startMasterTimer();
// }
// }
// void startSearchingForDriver() async {
// // ✅ منع الضغط المزدوج
// if (currentRideState.value == RideState.searching) {
// return;
// }
// // 1. تحديث الحالة الأولية
// isSearchingWindow = true;
// currentRideState.value = RideState.searching;
// driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr;
// update();
// // 2. إرسال الطلب للسيرفر (add_ride.php)
// bool rideCreated = await postRideDetailsToServer();
// if (!rideCreated) {
// // فشل الإنشاء
// isSearchingWindow = false;
// currentRideState.value = RideState.noRide;
// mySnackbarWarning("Could not create ride. Please try again.".tr);
// update();
// return;
// }
// // 3. نجاح الإنشاء: إضافة لجدول الانتظار المحلي (اختياري حسب منطقك)
// _addRideToWaitingTable();
// // 4. 🔥 الاتصال بالسوكيت فوراً وانتظار الرد الحقيقي 🔥
// // نغلق أي اتصال سابق ونبدأ اتصالاً جديداً مخصصاً لهذه الرحلة
// initConnectionWithSocket();
// // تشغيل الماكينة الرئيسية للمراقبة (كحماية إضافية)
// // _startMasterTimer();
// }
// /// دالة لإظهار النافذة المنبثقة لزيادة السعر
// void _showIncreaseFeeDialog() {
// Get.dialog(
// CupertinoAlertDialog(
// title: Text("No drivers accepted your request yet".tr),
// content: Text(
// "Increasing the fare might attract more drivers. Would you like to increase the price?"
// .tr),
// actions: [
// CupertinoDialogAction(
// child: Text("Cancel Ride".tr,
// style: TextStyle(color: AppColor.redColor)),
// onPressed: () {
// Get.back();
// changeCancelRidePageShow();
// // cancelRide(); // دالة إلغاء الرحلة
// },
// ),
// CupertinoDialogAction(
// child: Text("Increase Fare".tr,
// style: TextStyle(color: AppColor.greenColor)),
// onPressed: () {
// Get.back();
// // هنا يمكنك عرض نافذة أخرى لإدخال السعر الجديد
// // وبعدها استدعاء الدالة التالية
// // كمثال، سنزيد السعر بنسبة 10%
// double newPrice = totalPassenger * 1.10;
// increasePriceAndRestartSearch(newPrice);
// },
// ),
// ],
// ),
// barrierDismissible: false,
// );
// }
// /// دالة لتحديث السعر وإعادة بدء البحث
// Future<void> increasePriceAndRestartSearch(double newPrice) async {
// totalPassenger = newPrice;
// update();
// // await CRUD().post(link: AppLink.updateRides, payload: {
// // "id": rideId,
// // "price": newPrice.toStringAsFixed(2),
// // });
// CRUD().post(link: "${AppLink.server}/ride/rides/update.php", payload: {
// "id": rideId,
// "price": newPrice.toStringAsFixed(2),
// });
// // تصفير القائمة لأن السعر تغير، ويجب إبلاغ الجميع (حتى من وصله الإشعار سابقاً)
// Log.print(
// '[increasePrice] Price changed. Clearing notified list to resend.');
// notifiedDrivers.clear();
// _searchStartTime = DateTime.now();
// _currentSearchPhase = 0;
// isSearchingWindow = true;
// update();
// _startMasterTimer();
// }
// /// (دالة جديدة موحدة)
// /// هذه هي الدالة الوحيدة المسؤولة عن بدء عملية قبول الرحلة.
// /// يتم استدعاؤها إما من إشعار Firebase أو من البحث الدوري (Polling).
// /// هي تحتوي على "حارس البوابة" لمنع تضارب السباق.
// /// (دالة موحدة وصارمة)
// /// تستدعى من FCM أو من Polling عند اكتشاف قبول السائق
// /// تمنع تضارب السباق وتضمن تنفيذ المنطق مرة واحدة فقط
// /// متغير لمنع التكرار (Race Condition Guard)
// Future<dynamic> driverArrivePassengerDialoge() {
// return Get.defaultDialog(
// barrierDismissible: false,
// title: 'Hi ,I Arrive your site'.tr,
// titleStyle: AppStyle.title,
// middleText: 'Please go to Car Driver'.tr,
// middleTextStyle: AppStyle.title,
// confirm: MyElevatedButton(
// title: 'Ok I will go now.'.tr,
// onPressed: () {
// NotificationService.sendNotification(
// target: driverToken.toString(),
// title: 'Hi ,I will go now'.tr,
// body: 'I will go now'.tr,
// isTopic: false, // Important: this is a token
// tone: 'ding',
// driverList: [],
// category: 'Hi ,I will go now',
// );
// Get.back();
// remainingTime = 0;
// update();
// }));
// }
// /// (دالة خاصة جديدة)
// /// تحتوي على كل المنطق الفعلي لبدء الرحلة.
// ///
// Timer? _waitPassengerTimer;
// static const int _waitPassengerTotalSeconds = 300;
// int _waitPassengerElapsedSeconds = 0;
// /// **إيقاف عداد انتظار الراكب (Stop Wait Timer)**
// ///
// /// تقوم هذه الدالة بإلغاء التايمر النشط فوراً لتحرير الموارد ومنع تسريب الذاكرة.
// ///
// /// * [resetUI]: (اختياري) عند وضعه `true`، يتم تصفير العدادات وتحديث الواجهة لإخفاء التوقيت القديم.
// void _stopWaitPassengerTimer({bool resetUI = false}) {
// // 1. الإلغاء الآمن للتايمر (Safe Cancellation)
// _waitPassengerTimer?.cancel();
// _waitPassengerTimer = null;
// // 2. تصفير قيم الواجهة (Reset State)
// if (resetUI) {
// progressTimerDriverWaitPassenger5Minute = 0.0;
// remainingTimeDriverWaitPassenger5Minute = 0;
// stringRemainingTimeDriverWaitPassenger5Minute = '00:00';
// // ✅ تحديث الواجهة فوراً (GetX)
// update();
// }
// }
// void _executeBeginRideLogic() {
// Log.print('[executeBeginRideLogic] تنفيذ منطق بدء الرحلة...');
// _stopWaitPassengerTimer(resetUI: true); // <-- إضافة
// // 1. تصفير كل عدادات ما قبل الرحلة
// timeToPassengerFromDriverAfterApplied = 0;
// remainingTime = 0;
// remainingTimeToPassengerFromDriverAfterApplied = 0;
// remainingTimeDriverWaitPassenger5Minute = 0;
// // 2. تحديث الحالة والواجهة
// rideTimerBegin = true;
// statusRide = 'Begin';
// isDriverInPassengerWay = false;
// isDriverArrivePassenger = false; // لإخفاء واجهة "السائق وصل"
// // 3. (من كود الإشعار الخاص بك)
// box.write(BoxName.passengerWalletTotal, '0');
// update(); // تحديث الواجهة قبل بدء المؤقتات
// // 4. بدء مؤقتات الرحلة الفعلية
// rideIsBeginPassengerTimer(); // مؤقت عداد مدة الرحلة
// // runWhenRideIsBegin(); // مؤقت تتبع موقع السائق أثناء الرحلة
// // 5. إشعار الراكب (من كود الإشعار الخاص بك)
// NotificationController().showNotification(
// 'Trip is Begin'.tr,
// 'The trip has started! Feel free to contact emergency numbers, share your trip, or activate voice recording for the journey'
// .tr,
// 'start');
// }
// // متغير لمنع التكرار
// bool _isRideStartedProcessed = false;
// /// **معالجة بدء الرحلة الموحدة (Unified Ride Start Handler)**
// ///
// /// تستدعى عند استلام حدث بدء الرحلة سواء من السوكيت أو FCM.
// /// تضمن انتقال التطبيق لحالة [RideState.inProgress] مرة واحدة فقط.
// Future<void> processRideBegin({String source = "Unknown"}) async {
// // منطقك الحالي
// if (currentRideState.value == RideState.inProgress ||
// _isRideStartedProcessed) {
// return;
// }
// _isRideStartedProcessed = true;
// currentRideState.value = RideState.inProgress;
// statusRide = 'Begin';
// // إيقاف مؤقت الانتظار
// remainingTimeDriverWaitPassenger5Minute = 0;
// _stopWaitPassengerTimer();
// // 1) بيانات السائق والرحلة
// rideIsBeginPassengerTimer();
// update();
// }
// late Duration durationToAdd;
// late DateTime newTime = DateTime.now();
// int hours = 0;
// int minutes = 0;
// // --- إضافة جديدة: للوصول إلى وحدة التحكم بالروابط ---
// final DeepLinkController _deepLinkController =
// Get.isRegistered<DeepLinkController>()
// ? Get.find<DeepLinkController>()
// : Get.put(DeepLinkController());
// // ------------------------------------------------
// void onChangedPassengerCount(int newValue) {
// selectedPassengerCount = newValue;
// update();
// }
// void onChangedPassengersChoose() {
// isPassengerChosen = true;
// update();
// }
// void getCurrentLocationFormString() async {
// currentLocationToFormPlaces = true;
// currentLocationString = 'Waiting for your location'.tr;
// await getLocation();
// currentLocationString = passengerLocation.toString();
// newStartPointLocation = passengerLocation;
// update();
// }
// List<String> coordinatesWithoutEmpty = [];
// void getMapPointsForAllMethods() async {
// clearPolyline();
// isMarkersShown = false;
// isWayPointStopsSheetUtilGetMap = false;
// isWayPointSheet = false;
// durationToRide = 0;
// distanceOfDestination = 0;
// wayPointSheetHeight = 0;
// remainingTime = 25;
// haveSteps = true;
// // Filter out empty value
// coordinatesWithoutEmpty =
// placesCoordinate.where((coord) => coord.isNotEmpty).toList();
// latestPosition = LatLng(
// double.parse(coordinatesWithoutEmpty.last.split(',')[0]),
// double.parse(coordinatesWithoutEmpty.last.split(',')[1]));
// for (var i = 0; i < coordinatesWithoutEmpty.length; i++) {
// if ((i + 1) < coordinatesWithoutEmpty.length) {
// await getMapPoints(
// coordinatesWithoutEmpty[i].toString(),
// coordinatesWithoutEmpty[i + 1].toString(),
// i,
// );
// if (i == 0) {
// startNameAddress = data[0]['start_address'];
// }
// if (i == coordinatesWithoutEmpty.length) {
// endNameAddress = data[0]['end_address'];
// }
// }
// }
// // isWayPointStopsSheet = false;
// if (haveSteps) {
// String latestWaypoint =
// placesCoordinate.lastWhere((coord) => coord.isNotEmpty);
// latestPosition = LatLng(
// double.parse(latestWaypoint.split(',')[0]),
// double.parse(latestWaypoint.split(',')[1]),
// );
// }
// updateCameraForDistanceAfterGetMap();
// changeWayPointStopsSheet();
// bottomSheet();
// showBottomSheet1();
// update();
// }
// void convertHintTextPlaces(int index, var res) {
// if (placeListResponseAll[index].isEmpty) {
// placeListResponseAll[index] = res;
// hintTextwayPointStringAll[index] = 'Search for your Start point'.tr;
// update();
// } else {
// hintTextwayPointStringAll[index] = res['name'];
// currentLocationStringAll[index] = res['name'];
// placesCoordinate[index] =
// '${res['geometry']['location']['lat']},${res['geometry']['location']['lng']}';
// placeListResponseAll[index] = [];
// allTextEditingPlaces[index].clear();
// // double lat = wayPoint0[index]['geometry']['location']['lat'];
// // double lng = wayPoint0[index]['geometry']['location']['lng'];
// // newPointLocation0 = LatLng(lat, lng);
// update();
// Get.back();
// }
// }
// void convertHintTextDestinationNewPlaces(int index) {
// if (placesDestination.isEmpty) {
// hintTextDestinationPoint = 'Search for your destination'.tr;
// update();
// } else {
// var res = placesDestination[index];
// hintTextDestinationPoint = res['displayName']?['text'] ??
// res['formattedAddress'] ??
// 'Unknown Place';
// double? lat = res['location']?['latitude'];
// double? lng = res['location']?['longitude'];
// if (lat != null && lng != null) {
// newMyLocation = LatLng(lat, lng);
// // 🔥 الحل: تحريك الكاميرا فوراً للهدف حتى لا يتم مسحه عند إغلاق الكيبورد 🔥
// mapController
// ?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16));
// }
// update();
// }
// }
// void convertHintTextDestinationNewPlacesFromRecent(
// List recentLocations, int index) {
// hintTextDestinationPoint = recentLocations[index]['name'];
// double lat = recentLocations[index]['latitude'];
// double lng = recentLocations[index]['longitude'];
// newMyLocation = LatLng(lat, lng);
// // 🔥 تحريك الكاميرا فوراً 🔥
// mapController?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16));
// update();
// }
// // final mainBottomMenuMap = GlobalKey<AnimatedContainer>();
// void changeBottomSheetShown({bool? forceValue}) {
// if (forceValue != null) {
// isBottomSheetShown = forceValue;
// } else {
// isBottomSheetShown = !isBottomSheetShown;
// }
// heightBottomSheetShown = isBottomSheetShown == true ? 250 : 0;
// update();
// }
// void changeCashConfirmPageShown() {
// isCashConfirmPageShown = !isCashConfirmPageShown;
// isCashSelectedBeforeConfirmRide = true;
// cashConfirmPageShown = isCashConfirmPageShown == true ? 250 : 0;
// // to get or sure picker point for origin //todo
// // isPickerShown = true;
// // clickPointPosition();
// update();
// }
// void changePaymentMethodPageShown() {
// isPaymentMethodPageShown = !isPaymentMethodPageShown;
// paymentPageShown = isPaymentMethodPageShown == true ? Get.height * .6 : 0;
// update();
// }
// void changeMapType() {
// mapType = !mapType;
// // heightButtomSheetShown = isButtomSheetShown == true ? 240 : 0;
// update();
// }
// void changeMapTraffic() {
// mapTrafficON = !mapTrafficON;
// update();
// }
// void changeisAnotherOreder(bool val) {
// isAnotherOreder = val;
// update();
// }
// void changeIsWhatsAppOrder(bool val) {
// isWhatsAppOrder = val;
// update();
// }
// void sendSMS(String to) async {
// // Get the driver's phone number.
// String driverPhone =
// (dataCarsLocationByPassenger['message'][carsOrder]['phone'].toString());
// // Format the message.
// String message =
// 'Hi! This is ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n I am using ${box.read(AppInformation.appName)} to ride with $passengerName as the driver. $passengerName \nis driving a $model\n with license plate $licensePlate.\n I am currently located at $passengerLocation.\n If you need to reach me, please contact the driver directly at\n\n $driverPhone.';
// // Launch the URL to send the SMS.
// launchCommunication('sms', to, message);
// }
// String formatSyrianPhone(String phone) {
// // Remove spaces and +
// phone = phone.replaceAll(' ', '').replaceAll('+', '');
// // If starts with 00963 → remove 00 → 963
// if (phone.startsWith('00963')) {
// phone = phone.replaceFirst('00963', '963');
// }
// // If starts with 0963 (common mistake) → fix it
// if (phone.startsWith('0963')) {
// phone = phone.replaceFirst('0963', '963');
// }
// // If starts with 963 (already correct)
// if (phone.startsWith('963')) {
// return phone; // nothing to do
// }
// // If starts with 09 → remove leading 0 → add 963
// if (phone.startsWith('09')) {
// return '963' + phone.substring(1); // 9xxxxxxxxx
// }
// // If starts with 9xxxxxxxxx (no country code)
// if (phone.startsWith('9') && phone.length == 9) {
// return '963' + phone;
// }
// // Otherwise return raw phone
// return phone;
// }
// void sendWhatsapp(String to) async {
// // Normalize phone number before sending
// String formattedPhone = formatSyrianPhone(to);
// // Message body
// String message =
// '${'${'Hi! This is'.tr} ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n${' I am using'.tr}'} ${AppInformation.appName}${' to ride with'.tr} $passengerName${' as the driver.'.tr} $passengerName \n${'is driving a '.tr}$model\n${' with license plate '.tr}$licensePlate.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n $driverPhone.';
// // Send WhatsApp message
// launchCommunication('whatsapp', formattedPhone, message);
// }
// void changeCancelRidePageShow() {
// showCancelRideBottomSheet();
// isCancelRidePageShown = !isCancelRidePageShown;
// // : cancelRide();
// update();
// }
// void getDrawerMenu() {
// heightMenuBool = !heightMenuBool;
// widthMapTypeAndTraffic = heightMenuBool == true ? 0 : 50;
// heightMenu = heightMenuBool == true ? 80 : 0;
// widthMenu = heightMenuBool == true ? 110 : 0;
// update();
// }
// calcualateDistsanceInMetet(LatLng prev, current) async {
// double distance2 = Geolocator.distanceBetween(
// prev.latitude,
// prev.longitude,
// current.latitude,
// current.longitude,
// );
// return distance2;
// }
// StreamController<int> _timerStreamController = StreamController<int>();
// Stream<int> get timerStream => _timerStreamController.stream;
// bool isTimerFromDriverToPassengerAfterAppliedRunning = true;
// bool isTimerRunning = false; // Flag to track if the timer is running
// int beginRideInterval = 10; // Interval in seconds for getBeginRideFromDriver
// void startTimerFromDriverToPassengerAfterApplied() {
// stopTimerFromDriverToPassengerAfterApplied();
// if (isTimerRunning) return;
// isTimerRunning = true;
// isTimerFromDriverToPassengerAfterAppliedRunning = true;
// int secondsElapsed = 0;
// // استدعاء فوري لأول مرة
// // getDriverCarsLocationToPassengerAfterApplied();
// Timer.periodic(const Duration(seconds: 1), (timer) {
// // --- التغيير الجوهري هنا ---
// // شرط الإيقاف: نتوقف فقط إذا انتهت الرحلة أو ألغيت، أو تم إيقاف التايمر يدوياً
// // لم نعد نعتمد على تجاوز الوقت المقدر (timeToPassenger) كشرط للإيقاف
// bool isRideActive = (statusRide == 'Apply' ||
// statusRide == 'Arrived' ||
// statusRide == 'Begin' ||
// currentRideState.value == RideState.driverApplied ||
// currentRideState.value == RideState.driverArrived ||
// currentRideState.value == RideState.inProgress);
// if (!isRideActive || !isTimerFromDriverToPassengerAfterAppliedRunning) {
// timer.cancel();
// isTimerRunning = false;
// if (!_timerStreamController.isClosed) {
// _timerStreamController.close();
// }
// return;
// }
// secondsElapsed++;
// if (!_timerStreamController.isClosed) {
// _timerStreamController.add(secondsElapsed);
// }
// // تحديث الواجهة للوقت المتبقي (شكلياً فقط للراكب)
// // حتى لو أصبح الوقت سالباً (تأخر السائق)، سنظهره كـ 00:00 أو نتركه سالباً
// remainingTimeToPassengerFromDriverAfterApplied =
// timeToPassengerFromDriverAfterApplied - secondsElapsed;
// if (remainingTimeToPassengerFromDriverAfterApplied < 0) {
// remainingTimeToPassengerFromDriverAfterApplied = 0;
// }
// int minutes =
// (remainingTimeToPassengerFromDriverAfterApplied / 60).floor();
// int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60;
// stringRemainingTimeToPassenger =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// // تحويل الوقت أو المسافة إلى نسبة من 0.0 إلى 1.0
// double currentProgress = 1 -
// (remainingTimeToPassengerFromDriverAfterApplied /
// timeToPassengerFromDriverAfterApplied);
// // 🔴 التعديل هنا: نحدث الآيفون كل 5 ثواني فقط للحفاظ على البطارية وتجنب حظر أبل
// if (secondsElapsed % 5 == 0) {
// double currentProgress = 1 -
// (remainingTimeToPassengerFromDriverAfterApplied /
// (timeToPassengerFromDriverAfterApplied == 0
// ? 1
// : timeToPassengerFromDriverAfterApplied));
// IosLiveActivityService.updateRideActivity(
// status: 'waiting',
// driverName: driverName ?? 'السائق',
// carDetails:
// '$make • $model • $carColor', // من الأفضل إظهار اللون أيضاً
// etaText: stringRemainingTimeToPassenger,
// progress: currentProgress.clamp(0.0, 1.0),
// );
// }
// // جلب موقع السائق كل 4 ثواني (Polling) ما دامت الرحلة نشطة
// if (secondsElapsed % beginRideInterval == 0) {
// // 2. تحديث موقع الراكب للسائق
// uploadPassengerLocation();
// } else {
// update();
// }
// });
// }
// // Function to stop the timer
// void stopTimerFromDriverToPassengerAfterApplied() {
// isTimerFromDriverToPassengerAfterAppliedRunning = false;
// update();
// }
// void startTimerDriverWaitPassenger5Minute() {
// // لا تبدأ إلا إذا فعلاً وصلنا
// if (currentRideState.value != RideState.driverArrived) return;
// // 1) أوقف أي عداد سابق (تتبع وصول السائق)
// stopTimerFromDriverToPassengerAfterApplied();
// isTimerRunning = false;
// // 2) أوقف عداد الانتظار إن كان شغال من قبل (منع تكرار)
// _stopWaitPassengerTimer();
// // 3) جهّز UI الانتظار
// isDriverArrivePassenger = true;
// isDriverInPassengerWay = false;
// timeToPassengerFromDriverAfterApplied = 0;
// _waitPassengerElapsedSeconds = 0;
// remainingTimeDriverWaitPassenger5Minute = _waitPassengerTotalSeconds;
// progressTimerDriverWaitPassenger5Minute = 0;
// int m = (remainingTimeDriverWaitPassenger5Minute / 60).floor();
// int s = remainingTimeDriverWaitPassenger5Minute % 60;
// stringRemainingTimeDriverWaitPassenger5Minute =
// '$m:${s.toString().padLeft(2, '0')}';
// update();
// // 4) ابدأ Timer.periodic (يمكن إلغاؤه فوراً)
// _waitPassengerTimer = Timer.periodic(const Duration(seconds: 1), (t) {
// // أول ما تتحول إلى inProgress (أو أي حالة غير arrived) أوقف فوراً
// if (currentRideState.value != RideState.driverArrived) {
// _stopWaitPassengerTimer(resetUI: true);
// // إخفاء واجهة "السائق وصل" إذا بدأت الرحلة
// if (currentRideState.value == RideState.inProgress) {
// isDriverArrivePassenger = false;
// }
// update();
// return;
// }
// _waitPassengerElapsedSeconds++;
// int remaining = _waitPassengerTotalSeconds - _waitPassengerElapsedSeconds;
// if (remaining < 0) remaining = 0;
// remainingTimeDriverWaitPassenger5Minute = remaining;
// progressTimerDriverWaitPassenger5Minute =
// _waitPassengerElapsedSeconds / _waitPassengerTotalSeconds;
// int minutes = (remaining / 60).floor();
// int seconds = remaining % 60;
// stringRemainingTimeDriverWaitPassenger5Minute =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// update();
// if (remaining == 0) {
// _stopWaitPassengerTimer();
// // هنا إذا بدك: طبّق غرامة انتظار / اعرض رسالة / إلخ
// }
// });
// }
// // Create a StreamController to manage the timer values
// final timerController = StreamController<int>();
// // Start the timer when the ride begins
// void beginRideTimer() {
// // Set up the timer to run every second
// Timer.periodic(const Duration(seconds: 1), (timer) {
// // Update the timer value and notify listeners
// timerController.add(timer.tick);
// update();
// });
// }
// // Stop the timer when the ride ends
// void stopRideTimer() {
// timerController.close();
// update();
// }
// Timer? _rideProgressTimer;
// bool _hasShownSpeedWarning = false; // متغير لحالة التنبيه
// /// **بدء مؤقت الرحلة للراكب (Passenger Ride Timer)**
// ///
// /// تقوم هذه الدالة بإدارة العداد الزمني للرحلة بمجرد بدئها (حالة [RideState.inProgress]).
// ///
// /// **المهام الرئيسية:**
// /// 1. **دقة التوقيت:** تعتمد على فرق الوقت الحقيقي (`DateTime.difference`) لضمان دقة العداد حتى لو خرج المستخدم من التطبيق وعاد.
// /// 2. **مراقبة السرعة:** تفحص سرعة المركبة كل ثانية، وتطلق تحذيراً [_triggerSpeedWarning] إذا تجاوزت 100 كم/س.
// /// 3. **تحديث الواجهة:** تقوم بتحديث شريط التقدم والوقت المتبقي لحظياً.
// /// 4. **الإيقاف التلقائي:** تتوقف تلقائياً عند انتهاء الوقت أو تغير حالة الرحلة.
// void rideIsBeginPassengerTimer() {
// // 1. تنظيف أي تايمر سابق
// _rideProgressTimer?.cancel();
// _hasShownSpeedWarning = false; // تصفير حالة التنبيه
// // 2. تحديد وقت الوصول المتوقع بدقة
// DateTime now = DateTime.now();
// DateTime expectedArrivalTime = now.add(Duration(seconds: durationToRide));
// // تنسيق وقت الوصول للعرض
// var arrivalTime = DateFormat('hh:mm a').format(expectedArrivalTime);
// box.write(BoxName.arrivalTime, arrivalTime);
// Log.print("⏳ Ride Timer Started. Duration: $durationToRide sec");
// // 3. بدء التايمر الدوري
// _rideProgressTimer =
// Timer.periodic(const Duration(seconds: 1), (timer) async {
// // أ) شرط الإيقاف الحاسم: إذا انتهت الرحلة أو ألغيت
// if (currentRideState.value != RideState.inProgress) {
// timer.cancel();
// return;
// }
// // ب) حساب الوقت المتبقي بناءً على الساعة الحالية (أدق من العد)
// DateTime currentNow = DateTime.now();
// int remainingSeconds =
// expectedArrivalTime.difference(currentNow).inSeconds;
// if (remainingSeconds < 0) remainingSeconds = 0;
// // تحديث المتغيرات
// remainingTimeTimerRideBegin = remainingSeconds;
// // حساب النسبة المئوية (حماية من القسمة على صفر)
// progressTimerRideBegin =
// durationToRide > 0 ? 1 - (remainingSeconds / durationToRide) : 1.0;
// // ج) تنسيق الوقت للعرض
// int minutes = (remainingSeconds / 60).floor();
// int seconds = remainingSeconds % 60;
// stringRemainingTimeRideBegin =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// // نحول progressTimerRideBegin (0..1) إلى نسبة (0..100)
// final percent = (progressTimerRideBegin * 100).clamp(0, 100).toInt();
// // ==============================================================
// // 🔔 د) تحديث الإشعارات (هنا تم حل مشكلة الإزعاج)
// // ==============================================================
// // 1. تحديث الآيفون (Live Activity): يمكن تحديثه كل 5 ثواني لأنه "تحديث صامت" للشاشة فقط ولا يصدر صوتاً
// if (remainingSeconds % 5 == 0 || remainingSeconds == 0) {
// IosLiveActivityService.updateRideActivity(
// status: 'ongoing', // ['waiting', 'ongoing']
// driverName: driverName ?? '',
// carDetails: '$make • $model • $carColor',
// etaText: stringRemainingTimeRideBegin,
// progress: progressTimerRideBegin.clamp(0.0, 1.0),
// );
// }
// // 2. تحديث إشعار الهاتف العادي (RideLiveNotification):
// // نحدثه كل دقيقة (60 ثانية) بدلاً من 5 ثواني حتى لا يزعج الراكب بالرنين المستمر!
// if (remainingSeconds % 60 == 0 || remainingSeconds == 0) {
// await RideLiveNotification.showTripInProgress(
// percentage: percent,
// etaText: stringRemainingTimeRideBegin,
// );
// }
// // ==============================================================
// // هـ) منطق الإشعارات لمنتصف الرحلة (يصدر تنبيه مرة واحدة فقط)
// if (progressTimerRideBegin >= 0.25 &&
// progressTimerRideBegin < 0.26 &&
// !_hasShownSpeedWarning) {
// // يمكن إضافة منطق إشعار منتصف الرحلة هنا
// }
// // و) مراقبة السرعة (Speed Check)
// if (speed > 100 && !_hasShownSpeedWarning) {
// _hasShownSpeedWarning = true; // ✅ قفل التنبيه حتى لا يتكرر
// _triggerSpeedWarning();
// }
// // إعادة تفعيل التنبيه إذا انخفضت السرعة (إعادة ضبط الأمان)
// if (speed < 80 && _hasShownSpeedWarning) {
// _hasShownSpeedWarning = false;
// }
// // ز) إنهاء التايمر إذا انتهى الوقت
// if (remainingSeconds <= 0) {
// timer.cancel();
// }
// update();
// });
// }
// /// **عرض تحذير السرعة الزائدة (Speed Warning Trigger)**
// ///
// /// تظهر نافذة منبثقة (Dialog) وإشعاراً محلياً لتحذير الراكب عند اكتشاف سرعة عالية (> 100 كم/س).
// ///
// /// **الخيارات المتاحة للمستخدم:**
// /// * **مشاركة التفاصيل:** لإرسال رسالة استغاثة عبر واتساب.
// /// * **أنا بخير:** لإغلاق التنبيه والاستمرار في الرحلة.
// void _triggerSpeedWarning() {
// NotificationController().showNotification("Warning: Speeding detected!".tr,
// 'You can call or record audio of this trip'.tr, 'tone1');
// Get.defaultDialog(
// barrierDismissible: false,
// title: "Warning: Speeding detected!".tr,
// titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
// content: Column(
// children: [
// Icon(Icons.speed, size: 50, color: AppColor.redColor),
// const SizedBox(height: 10),
// Text(
// "We noticed the speed is exceeding 100 km/h. Please slow down for your safety..."
// .tr,
// textAlign: TextAlign.center,
// style: AppStyle.title,
// ),
// ],
// ),
// confirm: MyElevatedButton(
// title: "Share Trip Details".tr,
// kolor: AppColor.redColor,
// onPressed: () {
// Get.back();
// _shareTripDetailsSOS();
// },
// ),
// cancel: MyElevatedButton(
// title: "I'm Safe".tr,
// kolor: AppColor.greenColor,
// onPressed: () {
// Get.back();
// },
// ),
// );
// }
// /// **تفعيل وضع الطوارئ للمركبة (sosPassenger)**
// ///
// /// تقوم بإظهار حوار تأكيدي للمستخدم لسؤاله عما إذا كان يرغب في إرسال
// /// إشارة استغاثة عبر واتساب.
// void sosPassenger() {
// Get.defaultDialog(
// barrierDismissible: false,
// title: "Emergency SOS".tr,
// titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
// content: Column(
// children: [
// Icon(Icons.warning_amber_rounded, size: 50, color: AppColor.redColor),
// const SizedBox(height: 10),
// Text(
// "Do you want to send an emergency message to your SOS contact?".tr,
// textAlign: TextAlign.center,
// style: AppStyle.title,
// ),
// ],
// ),
// confirm: MyElevatedButton(
// title: "Send SOS".tr,
// kolor: AppColor.redColor,
// onPressed: () {
// Get.back();
// _shareTripDetailsSOS();
// },
// ),
// cancel: MyElevatedButton(
// title: "I'm Safe".tr,
// kolor: AppColor.greenColor,
// onPressed: () {
// Get.back();
// },
// ),
// );
// }
// /// **مشاركة تفاصيل الرحلة للطوارئ (SOS Share)**
// ///
// /// تقوم بتجهيز رسالة نصية مفصلة تحتوي على بيانات الرحلة الحالية وإرسالها
// /// عبر تطبيق WhatsApp لرقم الطوارئ المحفوظ.
// ///
// /// **البيانات المرسلة:**
// /// * موقع الانطلاق والوصول.
// /// * اسم السائق، رقم الهاتف، ونوع السيارة.
// /// * رابط مباشر للموقع الحالي على خرائط جوجل.
// void _shareTripDetailsSOS() {
// String message = "**Emergency SOS from Passenger:**\n";
// String origin = startNameAddress;
// String destination = endNameAddress;
// message += "* ${'Origin'.tr}: $origin\n";
// message += "* ${'Destination'.tr}: $destination\n";
// message += "* ${'Driver Name'.tr}: $driverName\n";
// message += "* ${'Car'.tr}: $make - $model - $licensePlate\n";
// message += "* ${'Phone'.tr}: $driverPhone\n\n";
// // رابط جوجل مابس صحيح
// message +=
// "${'Location'.tr}: https://www.google.com/maps/search/?api=1&query=${passengerLocation.latitude},${passengerLocation.longitude}\n";
// message += "Please help! Contact me as soon as possible.".tr;
// launchCommunication(
// 'whatsapp', box.read(BoxName.sosPhonePassenger), message);
// }
// int progressTimerRideBeginVip = 0;
// int elapsedTimeInSeconds = 0; // Timer starts from 0
// String stringElapsedTimeRideBegin = '0:00';
// String stringElapsedTimeRideBeginVip = '0:00';
// bool rideInProgress = true; // To control when to stop the timer
// void rideIsBeginPassengerTimerVIP() async {
// rideInProgress = true; // Start the ride timer
// bool sendSOS = false;
// while (rideInProgress) {
// await Future.delayed(const Duration(seconds: 1));
// // Increment elapsed time
// elapsedTimeInSeconds++;
// // Update the time display
// int minutes = (elapsedTimeInSeconds / 60).floor();
// int seconds = elapsedTimeInSeconds % 60;
// stringElapsedTimeRideBeginVip =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// // Check for speed and SOS conditions
// if (speed > 100 && !sendSOS) {
// Get.defaultDialog(
// barrierDismissible: false,
// title: "Warning: Speeding detected!".tr,
// titleStyle: AppStyle.title,
// content: Text(
// "We noticed the speed is exceeding 100 km/h. Please slow down for your safety. If you feel unsafe, you can share your trip details with a contact or call the police using the red SOS button."
// .tr,
// style: AppStyle.title,
// ),
// confirm: MyElevatedButton(
// title: "Share Trip Details".tr,
// onPressed: () {
// Get.back();
// // Implement sharing trip details logic here
// String message = "**Emergency SOS from Passenger:**\n";
// // Get trip details from GetX or relevant provider
// String origin = passengerLocation.toString();
// String destination = myDestination.toString();
// String driverName = passengerName;
// String driverCarPlate = licensePlate;
// // Add trip details to the message
// message += "* ${'Origin'.tr}: $origin\n";
// message += "* ${'Destination'.tr}: $destination\n";
// message += "* ${'Driver Name'.tr}: $driverName\n";
// message += "* ${'Driver Car Plate'.tr}: $driverCarPlate\n\n";
// message += "* ${'Driver Phone'.tr}: $driverPhone\n\n";
// // Add current location
// message +=
// "${'Current Location'.tr}:https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude} \n";
// // Append a call to action
// message += "Please help! Contact me as soon as possible.".tr;
// // Launch WhatsApp communication
// launchCommunication(
// 'whatsapp', box.read(BoxName.sosPhonePassenger), message);
// sendSOS = true;
// },
// kolor: AppColor.redColor,
// ),
// cancel: MyElevatedButton(
// title: "Cancel".tr,
// onPressed: () {
// Get.back();
// },
// kolor: AppColor.greenColor,
// ),
// );
// }
// // Update the UI
// update();
// }
// }
// Future<void> tripFinishedFromDriver() async {
// Log.print('🧹 Cleaning UI for Finish');
// // إغلاق أي ديالوج مفتوح
// if (Get.isDialogOpen == true) Get.back();
// if (Get.isBottomSheetOpen == true) Get.back();
// statusRide = 'Finished';
// currentRideState.value = RideState.finished; // تثبيت الحالة
// // إيقاف البحث والعدادات
// isSearchingWindow = false;
// rideTimerBegin = false;
// shouldFetch = false;
// // إيقاف التايمرات
// stopAllTimers();
// resetAllMapStates();
// clearPolyline();
// clearMarkersExceptStartEnd();
// markers.clear();
// update();
// }
// StreamController<String> _beginRideStreamController =
// StreamController<String>.broadcast();
// Stream<String> get beginRideStream => _beginRideStreamController.stream;
// bool isBeginRideFromDriverRunning = false;
// // Call this method to listen to the stream
// void listenToBeginRideStream() {
// beginRideStream.listen((status) {
// Log.print("Ride status: $status");
// // Perform additional actions based on the status
// }, onError: (error) {
// Log.print("Error in Begin Ride Stream: $error");
// });
// }
// begiVIPTripFromPassenger() async {
// timeToPassengerFromDriverAfterApplied = 0;
// remainingTime = 0;
// isBottomSheetShown = false;
// remainingTimeToPassengerFromDriverAfterApplied = 0;
// remainingTimeDriverWaitPassenger5Minute = 0;
// rideTimerBegin = true;
// statusRideVip = 'Begin';
// isDriverInPassengerWay = false;
// isDriverArrivePassenger = false;
// update();
// // isCancelRidePageShown = true;
// rideIsBeginPassengerTimerVIP();
// runWhenRideIsBegin();
// }
// Map rideStatusFromStartApp = {};
// bool isStartAppHasRide = false;
// getRideStatusFromStartApp() async {
// try {
// var res = await CRUD().get(
// link: AppLink.getRideStatusFromStartApp,
// payload: {'passenger_id': box.read(BoxName.passengerID)});
// // Log.print(res);
// Log.print('rideStatusFromStartApp: $res');
// // Log.print('1070');
// if (res == 'failure') {
// rideStatusFromStartApp = {
// 'data': {'status': 'NoRide', 'needsReview': false}
// };
// isStartAppHasRide = false;
// Log.print(
// "No rides found for the given passenger ID within the last hour.");
// } else {
// var decoded = jsonDecode(res);
// if (decoded['status'] == 'failure') {
// rideStatusFromStartApp = {
// 'data': {'status': 'NoRide', 'needsReview': false}
// };
// isStartAppHasRide = false;
// } else {
// rideStatusFromStartApp = decoded;
// }
// }
// if (rideStatusFromStartApp['data']['status'] == 'Begin' ||
// rideStatusFromStartApp['data']['status'] == 'Apply' ||
// rideStatusFromStartApp['data']['status'] == 'Applied') {
// statusRide = rideStatusFromStartApp['data']['status'];
// isStartAppHasRide = true;
// RideState.inProgress;
// driverId = rideStatusFromStartApp['data']['driver_id'];
// passengerName = rideStatusFromStartApp['data']['driverName'];
// driverRate = rideStatusFromStartApp['data']['rateDriver'].toString();
// statusRideFromStart = true;
// update();
// Map<String, dynamic> tripData =
// box.read(BoxName.tripData) as Map<String, dynamic>;
// final String pointsString = tripData['polyline'];
// List<LatLng> decodedPoints =
// await compute(decodePolylineIsolate, pointsString);
// // decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
// for (int i = 0; i < decodedPoints.length; i++) {
// polylineCoordinates.add(decodedPoints[i]);
// }
// var polyline = Polyline(
// polylineId: const PolylineId('main_route'),
// points: polylineCoordinates,
// width: 6,
// color: const Color(0xFF2196F3),
// );
// polyLines = {...polyLines, polyline};
// timeToPassengerFromDriverAfterApplied = 0;
// remainingTime = 0;
// remainingTimeToPassengerFromDriverAfterApplied = 0;
// remainingTimeDriverWaitPassenger5Minute = 0;
// rideTimerBegin = true;
// isDriverInPassengerWay = false;
// isDriverArrivePassenger = false;
// // update();
// // isCancelRidePageShown = true;
// durationToAdd = tripData['distance_m'];
// rideIsBeginPassengerTimer();
// runWhenRideIsBegin();
// update();
// }
// } catch (e) {
// // Handle the error or perform any necessary actions
// }
// }
// void driverArrivePassenger() {
// timeToPassengerFromDriverAfterApplied = 0;
// remainingTime = 0;
// // isCancelRidePageShown = true;
// update();
// rideIsBeginPassengerTimer();
// // runWhenRideIsBegin();
// }
// void cancelTimerToPassengerFromDriverAfterApplied() {
// timerToPassengerFromDriverAfterApplied?.cancel();
// }
// void clearPlacesDestination() {
// placesDestination = [];
// hintTextDestinationPoint = 'Search for your destination'.tr;
// update();
// }
// void clearPlacesStart() {
// placesStart = [];
// hintTextStartPoint = 'Search for your Start point'.tr;
// update();
// }
// void clearPlaces(int index) {
// placeListResponseAll[index] = [];
// hintTextwayPointStringAll[index] = 'Search for waypoint'.tr;
// update();
// }
// int selectedReason = -1;
// String? cancelNote;
// void selectReason0(int index, String note) {
// selectedReason = index;
// cancelNote = note;
// update();
// }
// void getDialog(String title, String? midTitle, VoidCallback onPressed) {
// final textToSpeechController = Get.find<TextToSpeechController>();
// Get.defaultDialog(
// title: title,
// titleStyle: AppStyle.title,
// middleTextStyle: AppStyle.title,
// content: Column(
// children: [
// IconButton(
// onPressed: () async {
// await textToSpeechController.speakText(title ?? midTitle!);
// },
// icon: const Icon(Icons.headphones)),
// Text(
// midTitle!,
// style: AppStyle.title,
// )
// ],
// ),
// confirm: MyElevatedButton(
// title: 'Ok'.tr,
// onPressed: onPressed,
// kolor: AppColor.greenColor,
// ),
// cancel: MyElevatedButton(
// title: 'Cancel',
// kolor: AppColor.redColor,
// onPressed: () {
// Get.back();
// }));
// }
// Future<Map<String, double>?> extractCoordinatesFromLinkAsync(
// String link) async {
// try {
// // 1. معالجة روابط الخرائط المباشرة (geo: و google.navigation:)
// if (link.startsWith('geo:') || link.startsWith('google.navigation:')) {
// RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~=](-?\d+\.\d+)');
// var match = regex.firstMatch(link);
// if (match != null) {
// double lat = double.parse(match.group(1)!);
// double lng = double.parse(match.group(2)!);
// if (lat > 40 && lat > lng) {
// double temp = lat;
// lat = lng;
// lng = temp;
// }
// return {'latitude': lat, 'longitude': lng};
// }
// }
// // 2. معالجة الروابط العادية (http/https)
// int urlStartIndex = link.indexOf(RegExp(r'https?://'));
// if (urlStartIndex == -1) return null;
// String cleanLink = link.substring(urlStartIndex).trim();
// Uri uri = Uri.parse(cleanLink);
// String finalUrl = cleanLink;
// // فك التوجيه للروابط المختصرة
// if (cleanLink.contains('goo.gl') ||
// cleanLink.contains('maps.google.com')) {
// try {
// var response =
// await http.get(uri).timeout(const Duration(seconds: 5));
// finalUrl = response.request?.url.toString() ?? cleanLink;
// } catch (e) {
// Log.print('Redirect logic failed, using original: $e');
// }
// }
// // الأنماط المشتركة لخرائط جوجل (تكون دائماً Lat ثم Lng)
// RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~](-?\d+\.\d+)');
// var match = regex.firstMatch(finalUrl);
// if (match != null) {
// double lat = double.parse(match.group(1)!);
// double lng = double.parse(match.group(2)!);
// // 🔥 منطق التصحيح الذاتي (Smart Swap) للمنطقة (سوريا/الأردن/مصر)
// // إذا كان الرقم الأول أكبر من الرقم الثاني بشكل واضح، فهذا يعني أن الرابط مقلوب أو أننا نحتاج للتأكد
// // في منطقتنا Latitude حوالي 30-35 و Longitude حوالي 36-44
// if (lat > 40 && lat > lng) {
// Log.print("⚠️ Detected Swapped Coordinates in Link. Correcting...");
// double temp = lat;
// lat = lng;
// lng = temp;
// }
// return {
// 'latitude': lat,
// 'longitude': lng,
// };
// }
// } catch (e) {
// Log.print('Error parsing location link: $e');
// }
// return null;
// }
// double latitudeWhatsApp = 0;
// double longitudeWhatsApp = 0;
// void handleWhatsAppLink(String link) async {
// Map<String, double>? coordinates =
// await extractCoordinatesFromLinkAsync(link);
// if (coordinates != null) {
// latitudeWhatsApp = coordinates['latitude']!;
// longitudeWhatsApp = coordinates['longitude']!;
// Log.print(
// 'Extracted coordinates: Lat: $latitudeWhatsApp, Long: $longitudeWhatsApp');
// // Use these coordinates in your app as needed
// } else {
// Log.print('Failed to extract coordinates from the link');
// }
// }
// void goToWhatappLocation() async {
// if (sosFormKey.currentState!.validate()) {
// // 1. استخراج الإحداثيات أولاً بشكل محلي لضمان عدم حدوث سباق بيانات (Race Condition)
// Map<String, double>? coordinates =
// await extractCoordinatesFromLinkAsync(whatsAppLocationText.text);
// if (coordinates != null) {
// latitudeWhatsApp = coordinates['latitude']!;
// longitudeWhatsApp = coordinates['longitude']!;
// Log.print(
// '📍 Final Coordinates for OSM: Lat: $latitudeWhatsApp, Lng: $longitudeWhatsApp');
// changeIsWhatsAppOrder(true);
// Get.back();
// // إعداد الوجهة
// myDestination = LatLng(latitudeWhatsApp, longitudeWhatsApp);
// // تحريك الكاميرا لموقع الراكب (البداية) وليس الوجهة فوراً لضمان تحميل الخريطة
// if (passengerLocation != null) {
// await mapController?.animateCamera(CameraUpdate.newLatLng(
// LatLng(passengerLocation.latitude, passengerLocation.longitude)));
// }
// changeMainBottomMenuMap();
// passengerStartLocationFromMap = true;
// isPickerShown = true;
// update();
// } else {
// mySnackbarWarning('لم نتمكن من استخراج الموقع من الرابط');
// }
// }
// }
// int currentTimeSearchingCaptainWindow = 0;
// late String driverPhone = '';
// late String driverRate = '';
// late String passengerName = '';
// late String carColor = '';
// late String colorHex = '';
// late String carYear = '';
// late String model = '';
// late String make = '';
// late String licensePlate = '';
// String driverOrderStatus = 'yet';
// bool isDriversTokensSend = false;
// Set<String> notifiedDrivers = {};
// /// [إضافة جديدة]
// /// دالة مخصصة لإضافة الرحلة إلى جدول الانتظار (waiting_ride)
// Future<void> _addRideToWaitingTable() async {
// try {
// await CRUD().post(link: AppLink.addWaitingRide, payload: {
// 'id': rideId.toString(),
// "start_location":
// '${startLocation.latitude},${startLocation.longitude}',
// "end_location": '${endLocation.latitude},${endLocation.longitude}',
// "date": DateTime.now().toString(),
// "time": DateTime.now().toString(),
// "price": totalPassenger.toStringAsFixed(2),
// 'passenger_id': box.read(BoxName.passengerID).toString(),
// 'status': 'waiting', // الحالة الرئيسية لجدول الانتظار
// 'carType': box.read(BoxName.carType),
// 'passengerRate': passengerRate.toStringAsFixed(2),
// 'price_for_passenger': totalME.toStringAsFixed(2),
// 'distance': distance.toStringAsFixed(1),
// 'duration': duration.toStringAsFixed(1),
// 'payment_method':
// Get.find<PaymentController>().isWalletChecked ? 'wallet' : 'cash',
// "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
// });
// Log.print('[WaitingTable] Ride $rideId added to waiting_ride table.');
// } catch (e) {
// Log.print('Error adding ride to waiting_ride table: $e');
// }
// }
// String driversStatusForSearchWindow = '';
// bool isDriversDataValid() {
// return dataCarsLocationByPassenger != 'failure' &&
// dataCarsLocationByPassenger != null &&
// dataCarsLocationByPassenger.containsKey('message') &&
// dataCarsLocationByPassenger['message'] != null;
// }
// void showNoDriversDialog() {
// Get.dialog(
// BackdropFilter(
// filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
// child: CupertinoAlertDialog(
// title: Text("No Car or Driver Found in your area.".tr,
// style: AppStyle.title
// .copyWith(fontSize: 20, fontWeight: FontWeight.bold)),
// content: Text("No Car or Driver Found in your area.".tr,
// style: AppStyle.title.copyWith(fontSize: 16)),
// actions: [
// CupertinoDialogAction(
// onPressed: () {
// Get.back();
// Get.offAll(() => const MapPagePassenger());
// },
// child:
// Text('OK'.tr, style: TextStyle(color: AppColor.greenColor)),
// ),
// ],
// ),
// ),
// barrierDismissible: false,
// );
// }
// Future<bool> postRideDetailsToServer() async {
// // التأكد من وجود مسار
// if (polylineCoordinates.isEmpty) return false;
// startLocation = polylineCoordinates.first;
// endLocation = polylineCoordinates.last;
// // تجهيز البيانات الكاملة (Data Enrichment) لإرسالها للـ PHP
// Map<String, dynamic> payload = {
// // 1. البيانات الأساسية
// "start_location": '${startLocation.latitude},${startLocation.longitude}',
// "end_location": '${endLocation.latitude},${endLocation.longitude}',
// "date": DateTime.now().toString(),
// "time": DateTime.now().toString(),
// "endtime": "00:00:00", // أو حسب حساباتك
// "price": totalPassenger.toStringAsFixed(2),
// "passenger_id": box.read(BoxName.passengerID).toString(),
// "driver_id": "0", // لم يحدد بعد
// "status": "waiting",
// "carType": box.read(BoxName.carType),
// "price_for_driver": totalPassenger.toString(), // أو المعادلة الخاصة بك
// "price_for_passenger": totalME.toString(),
// "distance": distance.toString(),
// // 2. بيانات الراكب (ليستخدمها PHP لبناء الـ Payload دون استعلام)
// "passenger_name": box.read(BoxName.name).toString(),
// "passenger_phone": box.read(BoxName.phone).toString(),
// "passenger_token": box.read(BoxName.tokenFCM).toString(),
// "passenger_email": box.read(BoxName.email).toString(),
// "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
// "passenger_rating": (passengerRate ?? 5.0).toString(),
// // 3. بيانات الواجهة الإضافية
// "start_name": startNameAddress,
// "end_name": endNameAddress,
// "duration_text": "${(durationToRide / 60).floor()}", // نص الوقت
// "distance_text": "$distance", // نص المسافة
// "is_wallet": Get.find<PaymentController>().isWalletChecked.toString(),
// "has_steps": Get.find<WayPointController>().wayPoints.length > 1
// ? 'true'
// : 'false',
// // نقاط التوقف (إذا وجدت)
// "step0": placesCoordinate.length > 0 ? placesCoordinate[0] : "",
// "step1": placesCoordinate.length > 1 ? placesCoordinate[1] : "",
// "step2": placesCoordinate.length > 2 ? placesCoordinate[2] : "",
// "step3": placesCoordinate.length > 3 ? placesCoordinate[3] : "",
// "step4": placesCoordinate.length > 4 ? placesCoordinate[4] : "",
// };
// Log.print(
// '🏁 Ride Registration Detail: $startNameAddress -> $endNameAddress');
// Log.print(' 📦 Payload: $payload');
// try {
// // الاتصال بـ add_ride.php
// var response = await CRUD().post(
// link: "${AppLink.server}/ride/rides/add_ride.php", // تأكد من المسار
// payload: payload);
// var jsonResponse = (response is String) ? jsonDecode(response) : response;
// if (jsonResponse['status'] == 'success') {
// rideId = jsonResponse['message'].toString(); // حفظ ID الرحلة
// Log.print("✅ Ride Created ID: $rideId");
// return true;
// } else {
// Log.print("❌ Ride Creation Failed: $response");
// return false;
// }
// } catch (e) {
// Log.print("❌ Exception in postRide: $e");
// return false;
// }
// }
// late LatLng endLocation;
// late LatLng startLocation;
// StreamController<String> _rideStatusStreamController =
// StreamController<String>.broadcast();
// Stream<String> get rideStatusStream => _rideStatusStreamController.stream;
// int maxAttempts = 28;
// Future<void> rideAppliedFromDriver(bool isApplied) async {
// Log.print('[rideAppliedFromDriver] 🚀 Starting logic...');
// // 1. جلب بيانات السائق والسيارة المحدثة من السيرفر
// await getUpdatedRideForDriverApply(rideId);
// // تنبيهات الأسعار حسب نوع السيارة
// if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) {
// NotificationController().showNotification('Fixed Price'.tr,
// 'The captain is responsible for the route.'.tr, 'ding');
// } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) {
// NotificationController().showNotification('Attention'.tr,
// 'The price may increase if the route changes.'.tr, 'ding');
// }
// isApplied = true;
// statusRide = 'Apply';
// rideConfirm = false;
// isSearchingWindow = false;
// _isDriverAppliedLogicExecuted = true; // ضمان عدم التكرار
// update(); // تحديث أولي
// // 2. جلب موقع السائق الأولي فوراً (Blocking await)
// await getDriverCarsLocationToPassengerAfterApplied();
// // 3. إذا توفر الموقع: حساب المسافة/الزمن ورسم المسار
// if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
// LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last;
// Log.print(
// '[rideAppliedFromDriver] 📍 Driver at: $driverPos, Passenger at: $passengerLocation');
// // أ) استدعاء API لحساب المسافة والزمن الدقيق (بدون رسم)
// await getInitialDriverDistanceAndDuration(driverPos, passengerLocation);
// // ب) رسم خط المسار (Visual only)
// await drawDriverPathOnly(driverPos, passengerLocation);
// // ج) ضبط الكاميرا لتشمل السائق والراكب
// _fitCameraToPoints(driverPos, passengerLocation);
// } else {
// Log.print(
// '[rideAppliedFromDriver] ⚠️ Warning: Driver location not found yet.');
// }
// // 4. تشغيل تايمر التتبع المستمر (الذي سيقوم بتناقص الوقت الذي جلبناه من API)
// startTimerFromDriverToPassengerAfterApplied();
// // إغلاق الستريم القديم
// if (!_rideStatusStreamController.isClosed)
// _rideStatusStreamController.close();
// }
// /// دالة لجلب المسافة والزمن بين السائق والراكب عند قبول الطلب
// /// تستخدم API سريع (overview=false)
// Future<void> getInitialDriverDistanceAndDuration(
// LatLng driverPos, LatLng passengerPos) async {
// final String apiUrl = 'https://routec.intaleq.xyz/route';
// final String apiKey = Env.mapKeyOsm;
// final String origin = '${driverPos.latitude},${driverPos.longitude}';
// final String dest = '${passengerPos.latitude},${passengerPos.longitude}';
// // الرابط المطلوب: steps=false&overview=false (سريع جداً للبيانات فقط)
// final Uri uri = Uri.parse(
// '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=false');
// try {
// Log.print('[InitialCalc] Fetching distance/duration from: $uri');
// final response = await http.get(uri, headers: {'X-API-KEY': apiKey});
// if (response.statusCode == 200) {
// final data = jsonDecode(response.body);
// if (data['status'] == 'ok') {
// // 1. استخراج الزمن (بالثواني)
// // نستخدم المعامل 1.5348 أو 1.4 حسب منطقك السابق لتقدير الوقت الواقعي
// double durationSecondsRaw = (data['duration_s'] as num).toDouble();
// int finalDurationSeconds = (durationSecondsRaw * kDurationScalar)
// .toInt(); // kDurationScalar = 1.5348
// // 2. استخراج المسافة (بالأمتار)
// double distanceMeters = (data['distance_m'] as num).toDouble();
// // 3. تحديث المتغيرات في الكنترولر
// timeToPassengerFromDriverAfterApplied = finalDurationSeconds;
// remainingTimeToPassengerFromDriverAfterApplied = finalDurationSeconds;
// distanceByPassenger =
// (distanceMeters).toStringAsFixed(0); // المسافة نصاً
// // يمكنك أيضاً تحديث durationToPassenger إذا كنت تستخدمها
// durationToPassenger = finalDurationSeconds;
// Log.print(
// '[InitialCalc] ✅ Success: Duration=${finalDurationSeconds}s, Distance=${distanceMeters}m');
// update(); // تحديث الواجهة لعرض الوقت الجديد فوراً
// }
// } else {
// Log.print('[InitialCalc] ❌ API Error: ${response.statusCode}');
// }
// } catch (e) {
// Log.print('[InitialCalc] 💥 Exception: $e');
// }
// }
// // دالة خفيفة وسريعة لرسم خط المسار فقط (بدون أسعار أو خطوات)
// Future<void> drawDriverPathOnly(LatLng driverPos, LatLng passengerPos) async {
// final String apiUrl = 'https://routec.intaleq.xyz/route';
// final String apiKey = Env.mapKeyOsm;
// final String origin = '${driverPos.latitude},${driverPos.longitude}';
// final String dest = '${passengerPos.latitude},${passengerPos.longitude}';
// // استخدام overview=full للدقة، و steps=false للسرعة
// final Uri uri = Uri.parse(
// '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=full');
// try {
// final response = await http.get(uri, headers: {'X-API-KEY': apiKey});
// if (response.statusCode == 200) {
// final data = jsonDecode(response.body);
// if (data['status'] == 'ok' && data['polyline'] != null) {
// final String pointsString = data['polyline'];
// // فك التشفير
// List<LatLng> decodedPoints =
// await compute(decodePolylineIsolate, pointsString);
// // إزالة خط مسار السائق القديم فقط
// polyLines = polyLines
// .where((p) => p.polylineId.value != 'driver_route')
// .toSet();
// // إضافة الخط الجديد
// polyLines = {
// ...polyLines,
// Polyline(
// polylineId: const PolylineId('driver_route'),
// points: decodedPoints,
// color: const Color(0xFF333333), // لون مميز لمسار السائق
// width: 5,
// )
// };
// // لا تستدعي update هنا، سيتم استدعاؤها في الدالة الأب (getDriverCars...) لتقليل عدد التحديثات
// }
// }
// } catch (e) {
// Log.print('Error drawing driver path: $e');
// }
// }
// // دالة مساعدة لضبط الكاميرا
// void _fitCameraToPoints(LatLng p1, LatLng p2) async {
// if (mapController == null) return;
// // 1. معالجة حالة النقاط المتطابقة (تمنع الكراش في Android)
// if (p1.latitude == p2.latitude && p1.longitude == p2.longitude) {
// try {
// mapController?.animateCamera(CameraUpdate.newLatLngZoom(p1, 17));
// } catch (e) {
// Log.print("Error animating to single point: $e");
// }
// return;
// }
// // 2. حساب الحدود
// double minLat = min(p1.latitude, p2.latitude);
// double maxLat = max(p1.latitude, p2.latitude);
// double minLng = min(p1.longitude, p2.longitude);
// double maxLng = max(p1.longitude, p2.longitude);
// // 3. تقليل الهوامش لتجنب خطأ "View size too small"
// // نستخدم 50 بدلاً من 100 ليكون آمناً مع الخرائط الصغيرة
// double padding = 50.0;
// try {
// await mapController?.animateCamera(
// CameraUpdate.newLatLngBounds(
// LatLngBounds(
// southwest: LatLng(minLat, minLng),
// northeast: LatLng(maxLat, maxLng),
// ),
// left: padding,
// top: padding,
// right: padding,
// bottom: padding,
// ),
// );
// } catch (e) {
// Log.print("Error animating bounds (Map might be resizing): $e");
// // محاولة بديلة آمنة: تحريك الكاميرا للمنتصف فقط دون Bounds
// try {
// LatLng center = LatLng((minLat + maxLat) / 2, (minLng + maxLng) / 2);
// mapController?.animateCamera(CameraUpdate.newLatLngZoom(center, 14));
// } catch (e) {
// Log.print("Error: $e");
// }
// }
// }
// // Listening to the Stream
// void listenToRideStatusStream() {
// rideStatusStream.listen((rideStatus) {
// Log.print("Ride Status: $rideStatus");
// // Handle updates based on the ride status
// }, onError: (error) {
// Log.print("Error in Ride Status Stream: $error");
// // Handle stream errors
// }, onDone: () {
// Log.print("Ride status stream closed.");
// });
// }
// void start15SecondTimer(String rideId) {
// Timer(const Duration(seconds: 15), () {
// // delayAndFetchRideStatusForAllDriverAvailable(rideId);
// });
// }
// // Replaces void startTimer()
// Timer?
// _uiCountdownTimer; // Add this variable to your class to manage lifecycle
// void startUiCountdown() {
// // Cancel any existing timer to avoid duplicates
// _uiCountdownTimer?.cancel();
// // Reset variables
// progress = 0;
// remainingTime = durationTimer;
// _uiCountdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
// // Logic from your loop, but non-blocking
// int i = timer.tick; // current tick
// progress = i / durationTimer;
// remainingTime = durationTimer - i;
// if (remainingTime <= 0) {
// timer.cancel(); // Stop this specific timer
// rideConfirm = false;
// // Add the duration to the tracking time logic
// timeToPassengerFromDriverAfterApplied += durationToPassenger;
// // Note: We do NOT call startTimerFromDriverToPassengerAfterApplied() here
// // because we already started it in rideAppliedFromDriver!
// timerEnded(); // Call your existing completion logic
// }
// update(); // Update the UI progress bar
// });
// }
// void timerEnded() async {
// runEvery30SecondsUntilConditionMet();
// isCancelRidePageShown = false;
// Log.print('isCancelRidePageShown: $isCancelRidePageShown');
// update();
// }
// Future<String> getRideStatus(String rideId) async {
// final response = await CRUD().get(
// link: "${AppLink.rideServerSide}/ride/rides/getRideStatus.php",
// payload: {'id': rideId});
// Log.print(response);
// Log.print('2176');
// return jsonDecode(response)['data'];
// }
// late String driverCarModel,
// driverCarMake,
// driverLicensePlate,
// driverName = '';
// Future<void> getUpdatedRideForDriverApply(String rideId) async {
// // حماية مبدئية: إذا كان المعرف غير صالح لا تكمل
// if (rideId == 'yet' || rideId.isEmpty) return;
// try {
// final res = await CRUD().get(
// link: "${AppLink.server}/ride/rides/getRideOrderID.php",
// payload: {'passengerID': box.read(BoxName.passengerID).toString()});
// if (res != 'failure') {
// var response = jsonDecode(res);
// Log.print('getUpdatedRideForDriverApply Response: $response');
// // [هام] التحقق من أن data عبارة عن Map وليست false أو null
// // هذا يمنع الخطأ: Class 'bool' has no instance method '[]'
// if (response['status'] == 'success' &&
// response['data'] != null &&
// response['data'] is Map) {
// var data = response['data'];
// // استخدام ?.toString() ?? '' للحماية من القيم الفارغة (Null Safety)
// driverId = data['driver_id']?.toString() ?? '';
// driverPhone = data['phone']?.toString() ?? '';
// driverCarMake = data['make']?.toString() ?? '';
// model = data['model']?.toString() ?? '';
// colorHex = data['color_hex']?.toString() ?? '';
// carColor = data['color']?.toString() ?? '';
// make = data['make']?.toString() ?? '';
// licensePlate = data['car_plate']?.toString() ?? '';
// // دمج الاسم الأول والأخير للراكب
// String firstName = data['passengerName']?.toString() ?? '';
// String lastName = data['last_name']?.toString() ?? '';
// passengerName =
// lastName.isNotEmpty ? "$firstName $lastName" : firstName;
// driverName = data['driverName']?.toString() ?? '';
// // [هام] التوكن ضروري للإشعارات
// driverToken = data['token']?.toString() ?? '';
// carYear = data['year']?.toString() ?? '';
// driverRate = data['ratingDriver']?.toString() ?? '5.0';
// update(); // تحديث الواجهة بالبيانات الجديدة
// } else {
// Log.print(
// "Warning: Ride data not found or invalid (data is false/null)");
// // اختياري: يمكنك هنا التعامل مع حالة عدم العثور على السائق بعد
// }
// }
// } catch (e) {
// Log.print("Error in getUpdatedRideForDriverApply: $e");
// }
// }
// late LatLng currentDriverLocation;
// late double headingList;
// Map<String, Timer> _animationTimers = {};
// final int updateIntervalMs = 100; // Update every 100ms
// final double minMovementThreshold =
// 10; // Minimum movement in meters to trigger update
// Future getCarForFirstConfirm(String carType) async {
// bool foundCars = false;
// int attempt = 0;
// // Set up the periodic timer
// Timer? timer = Timer.periodic(const Duration(seconds: 4), (Timer t) async {
// // Attempt to get car location
// foundCars = await getCarsLocationByPassengerAndReloadMarker();
// Log.print('foundCars: $foundCars');
// if (foundCars) {
// // If cars are found, cancel the timer and exit the search
// t.cancel();
// } else if (attempt >= 4) {
// // After 4 attempts, stop the search
// t.cancel();
// if (!foundCars) {
// noCarString = true;
// dataCarsLocationByPassenger = 'failure';
// }
// update();
// }
// attempt++; // Increment attempt
// });
// }
// void startCarLocationSearch(String carType) {
// int searchInterval = 5; // Interval in seconds
// Log.print('searchInterval: $searchInterval');
// int boundIncreaseStep = 2500; // Initial bounds in meters
// Log.print('boundIncreaseStep: $boundIncreaseStep');
// int maxAttempts = 3; // Maximum attempts to increase bounds
// int maxBoundIncreaseStep = 6000; // Maximum bounds increase step
// int attempt = 0; // Current attempt
// Log.print('initial attempt: $attempt');
// Timer.periodic(Duration(seconds: searchInterval), (Timer timer) async {
// Log.print('Current attempt: $attempt'); // Log current attempt
// bool foundCars = false;
// if (attempt >= maxAttempts) {
// timer.cancel();
// if (foundCars == false) {
// noCarString = true;
// // dataCarsLocationByPassenger = 'failure';
// update();
// }
// // return;
// } else if (reloadStartApp == true) {
// Log.print('reloadStartApp: $reloadStartApp');
// foundCars = await getCarsLocationByPassengerAndReloadMarker();
// Log.print('foundCars: $foundCars');
// if (foundCars) {
// timer.cancel();
// } else {
// attempt++;
// Log.print(
// 'Incrementing attempt to: $attempt'); // Log incremented attempt
// if (boundIncreaseStep < maxBoundIncreaseStep) {
// boundIncreaseStep += 1500; // Increase bounds
// if (boundIncreaseStep > maxBoundIncreaseStep) {
// boundIncreaseStep =
// maxBoundIncreaseStep; // Ensure it does not exceed the maximum
// }
// Log.print(
// 'New boundIncreaseStep: $boundIncreaseStep'); // Log new bounds
// }
// }
// }
// });
// }
// String getLocationArea(double latitude, double longitude) {
// LatLng passengerPoint = LatLng(latitude, longitude);
// // 1. فحص الأردن
// if (isPointInPolygon(passengerPoint, CountryPolygons.jordanBoundary)) {
// box.write(BoxName.countryCode, 'Jordan');
// // يمكنك تعيين AppLink.endPoint هنا إذا كان منطقك الداخلي لا يزال يعتمد عليه
// box.write(BoxName.serverChosen,
// AppLink.server); // مثال: اختر سيرفر سوريا للبيانات
// return 'Jordan';
// }
// // 2. فحص سوريا
// if (isPointInPolygon(passengerPoint, CountryPolygons.syriaBoundary)) {
// box.write(BoxName.countryCode, 'Syria');
// box.write(BoxName.serverChosen, AppLink.server);
// return 'Syria';
// }
// // 3. فحص مصر
// if (isPointInPolygon(passengerPoint, CountryPolygons.egyptBoundary)) {
// box.write(BoxName.countryCode, 'Egypt');
// box.write(BoxName.serverChosen, AppLink.server);
// return 'Egypt';
// }
// // 4. الافتراضي (إذا كان خارج المناطق المخدومة)
// box.write(BoxName.countryCode, 'Jordan');
// box.write(BoxName.serverChosen, AppLink.server);
// return 'Unknown Location (Defaulting to Jordan)';
// }
// Future<bool> getCarsLocationByPassengerAndReloadMarker() async {
// // 1. تنظيف القائمة والماركرز
// carsLocationByPassenger = [];
// if (passengerLocation.latitude == 0 && passengerLocation.longitude == 0) {
// return false; // لا يوجد موقع للراكب
// }
// // 2. طلب بسيط ومباشر (أنا هنا، أعطني السائقين حولي)
// var res = await CRUD().get(
// link: AppLink.getCarsLocationByPassenger,
// payload: {
// 'lat': passengerLocation.latitude.toString(),
// 'lng': passengerLocation.longitude.toString(),
// 'radius': '5', // نصف القطر ثابت (مثلاً 5 كم) أو يمكنك جعله ديناميكياً
// 'limit': '50', // أقصى عدد سيارات للعرض
// },
// );
// if (res == 'failure') {
// noCarString = true;
// update();
// return false;
// }
// // 3. معالجة البيانات
// noCarString = false;
// var responseData = jsonDecode(res);
// // دعم التنسيقين (data أو message) لضمان عدم حدوث كراش
// List driversList = [];
// if (responseData['status'] == true && responseData['data'] != null) {
// driversList = responseData['data'];
// } else if (responseData['message'] != null) {
// driversList = responseData['message']; // للكود القديم احتياطاً
// }
// if (driversList.isEmpty) {
// carsLocationByPassenger.clear();
// update();
// return false;
// }
// carsLocationByPassenger.clear(); // تنظيف الماركرز القديمة
// // 4. رسم السيارات على الخريطة
// for (var i = 0; i < driversList.length; i++) {
// var carData = driversList[i];
// // التحقق من الإحداثيات لضمان عدم رسم سيارة في المحيط
// double lat = double.tryParse(carData['latitude'].toString()) ?? 0.0;
// double lng = double.tryParse(carData['longitude'].toString()) ?? 0.0;
// double heading = double.tryParse(carData['heading'].toString()) ?? 0.0;
// if (lat == 0.0 || lng == 0.0) continue;
// _updateOrCreateMarker(
// carData['id'].toString(),
// LatLng(lat, lng),
// heading,
// // الدالة هذه تقرر شكل الأيقونة بناءً على نوع السيارة القادم من السيرفر
// _getIconForCar(carData),
// );
// }
// update();
// return true;
// }
// final List<Map<String, dynamic>> fakeCarData = [];
// void _addFakeCarMarkers(LatLng center, int count) {
// if (fakeCarData.isEmpty) {
// Random random = Random();
// double radiusInKm = 2.5; // 3 km diameter, so 1.5 km radius
// for (int i = 0; i < count; i++) {
// // Generate a random angle and distance within the circle
// double angle = random.nextDouble() * 2 * pi;
// double distance = sqrt(random.nextDouble()) * radiusInKm;
// // Convert distance to latitude and longitude offsets
// double latOffset = (distance / 111.32); // 1 degree lat ≈ 111.32 km
// double lonOffset =
// (distance / (111.32 * cos(radians(center.latitude))));
// // Calculate new position
// double lat = center.latitude + (latOffset * cos(angle));
// double lon = center.longitude + (lonOffset * sin(angle));
// double heading = random.nextDouble() * 360;
// fakeCarData.add({
// 'id': 'fake_$i',
// 'latitude': lat,
// 'longitude': lon,
// 'heading': heading,
// 'gender': 'Male', // Randomize gender
// });
// }
// }
// for (var carData in fakeCarData) {
// _updateOrCreateMarker(
// carData['id'].toString(),
// LatLng(carData['latitude'], carData['longitude']),
// carData['heading'],
// _getIconForCar(carData),
// );
// }
// }
// String _getIconForCar(Map<String, dynamic> carData) {
// if (carData['model'].toString().contains('دراجة')) {
// return motoIcon;
// } else if (carData['gender'] == 'Female') {
// return ladyIcon;
// } else {
// return carIcon;
// }
// }
// void _updateOrCreateMarker(
// String markerId, LatLng newPosition, double newHeading, String icon) {
// final mId = MarkerId(markerId);
// final existingMarker = markers.cast<Marker?>().firstWhere(
// (m) => m?.markerId == mId,
// orElse: () => null,
// );
// if (existingMarker == null) {
// markers = {
// ...markers,
// Marker(
// markerId: mId,
// position: newPosition,
// rotation: newHeading,
// icon: InlqBitmap.fromStyleImage(icon),
// anchor: const Offset(0.5, 0.5),
// ),
// };
// update();
// } else {
// double distance =
// _calculateDistance(existingMarker.position, newPosition);
// if (distance >= minMovementThreshold) {
// _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
// }
// }
// }
// double _calculateDistance(LatLng start, LatLng end) {
// // Implement distance calculation (e.g., Haversine formula)
// // For simplicity, this is a placeholder. Replace with actual implementation.
// return 1000 *
// sqrt(pow(start.latitude - end.latitude, 2) +
// pow(start.longitude - end.longitude, 2));
// }
// String formatSyrianPhoneNumber(String phoneNumber) {
// // Trim any whitespace from the input.
// String trimmedPhone = phoneNumber.trim();
// // If the number starts with '09', remove the leading '0' and prepend '963'.
// if (trimmedPhone.startsWith('09')) {
// return '963${trimmedPhone.substring(1)}';
// }
// // If the number already starts with '963', return it as is to avoid duplication.
// if (trimmedPhone.startsWith('963')) {
// return trimmedPhone;
// }
// // For any other case (e.g., number starts with '9' without a '0'),
// // prepend '963' to ensure the correct format.
// return '963$trimmedPhone';
// }
// String generateTrackingLink(String rideId, String driverId) {
// String cleanRideId = rideId.toString().trim();
// String cleanDriverId = driverId.toString().trim();
// // الكلمة السرية للمطابقة مع السيرفر
// const String secretSalt = "Intaleq_Secure_Track_2025";
// // الدمج والتشفير
// String rawString = "$cleanRideId$cleanDriverId$secretSalt";
// var bytes = utf8.encode(rawString);
// var digest = md5.convert(bytes);
// String token = digest.toString();
// // الرابط المباشر لصفحة التتبع
// return "https://intaleqapp.com/track/index.php?id=$cleanRideId&token=$token";
// }
// // 2. الدالة الرئيسية (تم تعديلها لإرسال واتساب بدلاً من الإشعارات)
// Future shareTripWithFamily() async {
// // التحقق أولاً: هل الرقم موجود؟
// String? storedPhone = box.read(BoxName.sosPhonePassenger);
// if (storedPhone == null) {
// // --- (نفس المنطق القديم: فتح ديالوج لإضافة الرقم) ---
// Get.defaultDialog(
// title: 'Add SOS Phone'.tr,
// titleStyle: AppStyle.title,
// content: Form(
// key: sosFormKey,
// child: MyTextForm(
// controller: sosPhonePassengerProfile,
// label: 'insert sos phone'.tr,
// hint: 'e.g. 0912345678'.tr,
// type: TextInputType.phone,
// ),
// ),
// confirm: MyElevatedButton(
// title: 'Add SOS Phone'.tr,
// onPressed: () async {
// if (sosFormKey.currentState!.validate()) {
// Get.back();
// // تنسيق الرقم
// var numberPhone =
// formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
// // حفظ في السيرفر
// await CRUD().post(
// link: AppLink.updateprofile,
// payload: {
// 'id': box.read(BoxName.passengerID),
// 'sosPhone': numberPhone,
// },
// );
// // حفظ محلياً
// box.write(BoxName.sosPhonePassenger, numberPhone);
// // استدعاء الدالة مرة أخرى للمتابعة
// shareTripWithFamily();
// }
// }));
// return;
// }
// // --- (المنطق الجديد: إرسال واتساب مباشرة) ---
// // 1. التأكد من وجود بيانات للرحلة
// if (rideId == 'yet' || driverId.isEmpty) {
// Get.snackbar("Alert".tr, "Wait for the trip to start first".tr);
// return;
// }
// // 2. تنسيق الرقم
// var numberPhone = formatSyrianPhoneNumber(storedPhone);
// // 3. توليد الرابط
// String trackingLink = generateTrackingLink(rideId, driverId);
// // 4. تجهيز الرسالة (بالإنجليزية وجاهزة للترجمة)
// // لاحظ: استخدمت المتغيرات الموجودة في الكنترولر (passengerName هنا عادة يحمل اسم السائق في الكنترولر الخاص بك حسب الكود السابق)
// String message = """
// مرحباً، تابع رحلتي مباشرة على تطبيق انطلق 🚗
// يمكنك تتبع مسار الرحلة من هنا:
// $trackingLink
// السائق: $passengerName
// السيارة: $model - $licensePlate
// شكراً لاستخدامك انطلق!
// """
// .tr;
// String messageEn = """Hello, follow my trip live on Intaleq 🚗
// Track my ride here:
// $trackingLink
// Driver: $passengerName
// Car: $model - $licensePlate
// Thank you for using Intaleq!
// """;
// // اختر الرسالة بناءً على اللغة المفضلة (مثال بسيط)
// String userLanguage = box.read(BoxName.lang) ?? 'ar';
// message = (userLanguage == 'ar') ? message : messageEn;
// // وضعنا .tr لكي تتمكن من ترجمتها للعربية في ملفات اللغة إذا أردت، أو تركها إنجليزية
// Log.print("Sending WhatsApp to: $numberPhone");
// // 5. فتح واتساب
// launchCommunication('whatsapp', numberPhone, message);
// // (اختياري) حفظ أن التتبع مفعل لتغيير حالة الأيقونة في الواجهة
// box.write(BoxName.parentTripSelected, true);
// update();
// }
// Future getTokenForParent() async {
// // 1. التحقق أولاً: هل الرقم موجود؟
// String? storedPhone = box.read(BoxName.sosPhonePassenger);
// if (storedPhone == null) {
// // --- حالة الرقم غير موجود: نفتح الديالوج فقط ---
// Get.defaultDialog(
// title: 'Add SOS Phone'.tr,
// titleStyle: AppStyle.title,
// content: Form(
// key: sosFormKey,
// child: MyTextForm(
// controller: sosPhonePassengerProfile,
// label: 'insert sos phone'.tr,
// hint: 'e.g. 0912345678'.tr,
// type: TextInputType.phone,
// ),
// ),
// confirm: MyElevatedButton(
// title: 'Add SOS Phone'.tr,
// onPressed: () async {
// if (sosFormKey.currentState!.validate()) {
// // إغلاق الديالوج الحالي
// Get.back();
// // تنسيق الرقم (تأكد أن هذا التنسيق يطابق ما تم تخزينه عند تسجيل الراكب)
// var numberPhone =
// formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
// // حفظ الرقم في السيرفر (تحديث البروفايل)
// await CRUD().post(
// link: AppLink.updateprofile,
// payload: {
// 'id': box.read(BoxName.passengerID),
// 'sosPhone': numberPhone,
// },
// );
// // حفظ الرقم محلياً
// box.write(BoxName.sosPhonePassenger, numberPhone);
// // استدعاء الدالة مرة أخرى
// getTokenForParent();
// }
// }));
// return;
// }
// generateTrackingLink(rideId, driverId);
// // --- حالة الرقم موجود: نكمل التنفيذ ---
// var numberPhone = formatSyrianPhoneNumber(storedPhone);
// Log.print("Searching for Parent Token with Phone: $numberPhone");
// // استدعاء السكريبت (استخدم POST بدلاً من GET)
// var res = await CRUD()
// .post(link: AppLink.getTokenParent, payload: {'phone': numberPhone});
// // التعامل مع الاستجابة
// if (res is Map<String, dynamic>) {
// handleResponse(res);
// } else {
// try {
// // var jsonRes = jsonDecode(res);
// handleResponse(res);
// } catch (e) {
// Log.print("Error parsing response: $res");
// }
// }
// }
// void handleResponse(Map<String, dynamic> res) {
// Log.print("Handle Response: $res"); // للتأكد من دخول الدالة
// // الحالة 1: الرقم غير مسجل (Failure)
// if (res['status'] == 'failure') {
// // إذا كان هناك أي ديالوج تحميل مفتوح، نغلقه أولاً، لكن بحذر
// if (Get.isDialogOpen ?? false) Get.back();
// Get.defaultDialog(
// title: "No user found".tr, // اختصرت العنوان ليظهر بشكل أفضل
// titleStyle: AppStyle.title,
// content: Column(
// children: [
// Text(
// "No passenger found for the given phone number".tr,
// style: AppStyle.title, // غيرت الستايل ليكون أصغر قليلاً
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 10),
// Text(
// "Send Intaleq app to him".tr,
// style: AppStyle.title
// .copyWith(color: AppColor.greenColor, fontSize: 14),
// textAlign: TextAlign.center,
// )
// ],
// ),
// confirm: MyElevatedButton(
// title: 'Send Invite'.tr,
// onPressed: () {
// Get.back(); // إغلاق الديالوج
// var rawPhone = box.read(BoxName.sosPhonePassenger);
// // تأكد أن rawPhone ليس null
// if (rawPhone == null) return;
// var phone = formatSyrianPhoneNumber(rawPhone);
// // تصحيح نص الرسالة
// var message = '''Dear Friend,
// 🚀 I have just started an exciting trip on Intaleq!
// Download the app to track my ride:
// 👉 Android: https://play.google.com/store/apps/details?id=com.Intaleq.intaleq&hl=en-US
// 👉 iOS: https://apps.apple.com/st/app/intaleq-rider/id6748075179
// See you there!
// Intaleq Team''';
// launchCommunication('whatsapp', phone, message);
// }),
// cancel: MyElevatedButton(
// title: 'Cancel'.tr,
// onPressed: () {
// Get.back();
// }));
// }
// // الحالة 2: نجاح (Success)
// else if (res['status'] == 'success') {
// // إغلاق أي ديالوج سابق (مثل Loading)
// if (Get.isDialogOpen ?? false) Get.back();
// Get.snackbar("Success".tr, "The invitation was sent successfully".tr,
// backgroundColor: AppColor.greenColor, colorText: Colors.white);
// List tokensData = res['data'];
// for (var device in tokensData) {
// String tokenParent = device['token'];
// NotificationService.sendNotification(
// category: "Trip Monitoring",
// target: tokenParent,
// title: "Trip Monitoring".tr,
// body: "Click to track the trip".tr,
// isTopic: false,
// tone: 'tone1',
// driverList: [rideId, driverId],
// );
// // حفظ آخر توكن
// box.write(BoxName.tokenParent, tokenParent);
// }
// box.write(BoxName.parentTripSelected, true);
// }
// }
// // Function to check if the point is inside the polygon
// bool isPointInPolygon(LatLng point, List<LatLng> polygon) {
// int intersections = 0;
// for (int i = 0; i < polygon.length; i++) {
// LatLng vertex1 = polygon[i];
// LatLng vertex2 =
// polygon[(i + 1) % polygon.length]; // Loop back to the start
// if (_rayIntersectsSegment(point, vertex1, vertex2)) {
// intersections++;
// }
// }
// // If the number of intersections is odd, the point is inside
// return intersections % 2 != 0;
// }
// // Helper function to check if a ray from the point intersects with a polygon segment
// bool _rayIntersectsSegment(LatLng point, LatLng vertex1, LatLng vertex2) {
// double px = point.longitude;
// double py = point.latitude;
// double v1x = vertex1.longitude;
// double v1y = vertex1.latitude;
// double v2x = vertex2.longitude;
// double v2y = vertex2.latitude;
// // Check if the point is outside the vertical bounds of the segment
// if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) {
// return false;
// }
// // Calculate the intersection of the ray and the segment
// double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y);
// // Check if the intersection is to the right of the point
// return intersectX > px;
// }
// bool isInUniversity = false;
// // Function to check if the passenger is in any university polygon
// // Function to check if the passenger is in any university polygon and return the university name
// String checkPassengerLocation(LatLng passengerLocation,
// List<List<LatLng>> universityPolygons, List<String> universityNames) {
// for (int i = 0; i < universityPolygons.length; i++) {
// if (isPointInPolygon(passengerLocation, universityPolygons[i])) {
// isInUniversity = true;
// return "Passenger is in ${universityNames[i]}";
// }
// }
// return "Passenger is not in any university";
// }
// String passengerLocationStringUnvirsity = 'unKnown';
// void getPassengerLocationUniversity() {
// // Check if the passenger is inside any of the university polygons and get the university name
// passengerLocationStringUnvirsity = checkPassengerLocation(
// passengerLocation,
// UniversitiesPolygons.universityPolygons,
// UniversitiesPolygons.universityNames,
// );
// if (passengerLocationStringUnvirsity != 'unKnown') {
// // Get.snackbar('you are in $passengerLocationStringUnvirsity', "");
// }
// Log.print(passengerLocationStringUnvirsity);
// }
// // Initialize polygons from UniversitiesPolygons
// void _initializePolygons() {
// List<List<LatLng>> universityPolygons =
// UniversitiesPolygons.universityPolygons;
// for (int i = 0; i < universityPolygons.length; i++) {
// Polygon polygon = Polygon(
// polygonId: PolygonId('univ_$i'),
// points: universityPolygons[i],
// fillColor: Colors.blueAccent.withOpacity(0.2),
// strokeColor: Colors.blueAccent,
// strokeWidth: 2,
// );
// polygons.add(polygon);
// }
// update();
// }
// LatLng driverLocationToPassenger = const LatLng(32, 35);
// Future getDriverCarsLocationToPassengerAfterApplied() async {
// // driverCarsLocationToPassengerAfterApplied
// // 1. الشرط الأمني: تتبع فقط إذا كانت الرحلة نشطة
// bool isRideActive = (statusRide == 'Apply' ||
// statusRide == 'Arrived' ||
// statusRide == 'Begin' ||
// currentRideState.value == RideState.driverApplied ||
// currentRideState.value == RideState.driverArrived ||
// currentRideState.value == RideState.inProgress);
// if (!isRideActive ||
// statusRide == 'Finished' ||
// statusRide == 'Cancel' ||
// currentRideState.value == RideState.finished ||
// currentRideState.value == RideState.noRide ||
// currentRideState.value == RideState.preCheckReview) {
// return;
// }
// // 2. منع التداخل (Blocking)
// if (_isFetchingDriverLocation) return;
// _isFetchingDriverLocation = true;
// try {
// var res = await CRUD().get(
// link: AppLink.getDriverCarsLocationToPassengerAfterApplied,
// payload: {'driver_id': driverId});
// if (res != 'failure') {
// datadriverCarsLocationToPassengerAfterApplied = jsonDecode(res);
// if (datadriverCarsLocationToPassengerAfterApplied['message'] != null &&
// datadriverCarsLocationToPassengerAfterApplied['message']
// .isNotEmpty) {
// var _data =
// datadriverCarsLocationToPassengerAfterApplied['message'][0];
// LatLng newDriverPos = LatLng(
// double.parse(_data['latitude'].toString()),
// double.parse(_data['longitude'].toString()));
// // أضف هذا السطر لتقليل استهلاك الذاكرة
// if (driverCarsLocationToPassengerAfterApplied.length > 10) {
// driverCarsLocationToPassengerAfterApplied.removeAt(0);
// }
// driverLocationToPassenger = newDriverPos;
// driverCarsLocationToPassengerAfterApplied.add(newDriverPos);
// // 🔥 الإضافة هنا أيضاً 🔥
// // 🔥 تحديث التوقيت حتى لو جاءت من API لكي يهدأ الحارس قليلاً
// _lastSocketLocationTime = DateTime.now();
// _checkAndRecalculateIfDeviated(newDriverPos);
// // [تعديل هام] تنظيف آمن: لا نحذف ماركر السائق الحالي
// clearMarkersExceptStartEndAndDriver();
// // تحريك الماركر
// reloadMarkerDriverCarsLocationToPassengerAfterApplied();
// }
// }
// update();
// } catch (e) {
// Log.print('Error fetching driver location: $e');
// } finally {
// _isFetchingDriverLocation = false;
// }
// }
// Future runEvery30SecondsUntilConditionMet() async {
// // Calculate the duration of the trip in minutes.
// double tripDurationInMinutes = durationToPassenger / 5;
// int loopCount = tripDurationInMinutes.ceil();
// // If the trip duration is less than or equal to 50 minutes, then break the loop.
// for (var i = 0; i < loopCount; i++) {
// // Wait for 50 seconds.
// await Future.delayed(const Duration(seconds: 5));
// if (rideTimerBegin == true || statusRide == 'Apply') {
// await getDriverCarsLocationToPassengerAfterApplied();
// reloadMarkerDriverCarsLocationToPassengerAfterApplied();
// }
// }
// }
// Future runWhenRideIsBegin() async {
// // Calculate the duration of the trip in minutes.
// double tripDurationInMinutes = durationToRide / 6;
// int loopCount = tripDurationInMinutes.ceil();
// // If the trip duration is less than or equal to 50 minutes, then break the loop.
// clearMarkersExceptStartEnd();
// for (var i = 0; i < loopCount; i++) {
// // Wait for 50 seconds.
// await Future.delayed(const Duration(seconds: 4));
// // if (rideTimerBegin == true && statusRide == 'Apply') {
// await getDriverCarsLocationToPassengerAfterApplied();
// // }
// reloadMarkerDriverCarsLocationToPassengerAfterApplied();
// }
// }
// Timer? _timer;
// // final int updateIntervalMs = 100; // Update every 100ms
// // final double minMovementThreshold =
// // 1.0; // Minimum movement in meters to trigger update
// void clearMarkersExceptStartEndAndDriver() {
// markers.removeWhere((marker) {
// String id = marker.markerId.value;
// // لا تحذف نقطة البداية
// if (id == 'start') return false;
// // لا تحذف نقطة النهاية
// if (id == 'end') return false;
// // لا تحذف السائق الحالي
// if (id == currentDriverMarkerId) return false;
// // احذف أي شيء آخر (مثل السيارات التي ظهرت وقت البحث)
// return true;
// });
// // ملاحظة: لا نستدعي update() هنا لأننا سنستدعيها في نهاية الدالة الرئيسية
// }
// void clearMarkersExceptStartEnd() {
// markers.removeWhere((marker) {
// String id = marker.markerId.value;
// return id != 'start' && id != 'end';
// });
// update();
// }
// // 1. تعريف ID ثابت للسائق طوال الرحلة
// String get currentDriverMarkerId => 'driver_marker_$driverId';
// void reloadMarkerDriverCarsLocationToPassengerAfterApplied() {
// if (datadriverCarsLocationToPassengerAfterApplied == null ||
// datadriverCarsLocationToPassengerAfterApplied['message'] == null ||
// datadriverCarsLocationToPassengerAfterApplied['message'].isEmpty) {
// return;
// }
// var driverData =
// datadriverCarsLocationToPassengerAfterApplied['message'][0];
// // جلب الإحداثيات الجديدة
// LatLng newPosition = LatLng(double.parse(driverData['latitude'].toString()),
// double.parse(driverData['longitude'].toString()));
// double newHeading =
// double.tryParse(driverData['heading'].toString()) ?? 0.0;
// // تحديد الأيقونة
// String icon;
// if (driverData['model'].toString().contains('دراجة') ||
// driverData['make'].toString().contains('دراجة')) {
// icon = motoIcon;
// } else if (driverData['gender'] == 'Female') {
// icon = ladyIcon;
// } else {
// icon = carIcon;
// }
// // 2. البحث عن الماركر الجديد وتحديثه أو إنشاء جديد
// final String markerId = currentDriverMarkerId;
// final mId = MarkerId(markerId);
// final existingMarker = markers.cast<Marker?>().firstWhere(
// (m) => m?.markerId == mId,
// orElse: () => null,
// );
// if (existingMarker != null) {
// _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
// } else {
// markers = {
// ...markers,
// Marker(
// markerId: mId,
// position: newPosition,
// rotation: newHeading,
// icon: InlqBitmap.fromStyleImage(icon),
// anchor: const Offset(0.5, 0.5),
// ),
// };
// update();
// }
// }
// // التأكد من دالة التحريك السلس
// void _smoothlyUpdateMarker(
// Marker oldMarker, LatLng newPosition, double newHeading, String icon) {
// double distance = Geolocator.distanceBetween(
// oldMarker.position.latitude,
// oldMarker.position.longitude,
// newPosition.latitude,
// newPosition.longitude);
// if (distance < 2.0) return;
// final MarkerId markerIdKey = oldMarker.markerId;
// _animationTimers[markerIdKey.value]?.cancel();
// int ticks = 0;
// const int totalSteps = 20;
// const int stepDuration = 50;
// double latStep =
// (newPosition.latitude - oldMarker.position.latitude) / totalSteps;
// double lngStep =
// (newPosition.longitude - oldMarker.position.longitude) / totalSteps;
// double headingStep = (newHeading - oldMarker.rotation) / totalSteps;
// LatLng currentPos = oldMarker.position;
// double currentHeading = oldMarker.rotation;
// _animationTimers[markerIdKey.value] =
// Timer.periodic(const Duration(milliseconds: stepDuration), (timer) {
// ticks++;
// currentPos =
// LatLng(currentPos.latitude + latStep, currentPos.longitude + lngStep);
// currentHeading += headingStep;
// // Update the marker in the set
// final updatedMarker = oldMarker.copyWith(
// position: currentPos,
// rotation: currentHeading,
// icon: InlqBitmap.fromStyleImage(icon),
// );
// markers = {
// ...markers.where((m) => m.markerId != markerIdKey),
// updatedMarker,
// };
// // Native update through controller to avoid UI rebuild
// if (mapController != null) {
// mapController!.animateCamera(CameraUpdate.newLatLng(
// currentPos)); // Optional: Follow car if needed
// // Note: IntaleqMapController doesn't expose raw symbol update yet for Marker object,
// // but declarative update via GetBuilder is fast.
// }
// update();
// if (ticks >= totalSteps) {
// timer.cancel();
// _animationTimers.remove(markerIdKey.value);
// }
// });
// }
// void _updateMarkerPosition(
// LatLng newPosition, double newHeading, String icon) {
// const String markerId = 'driverToPassengers';
// final mId = MarkerId(markerId);
// final existingMarker = markers.cast<Marker?>().firstWhere(
// (m) => m?.markerId == mId,
// orElse: () => null,
// );
// if (existingMarker != null) {
// _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
// } else {
// markers = {
// ...markers,
// Marker(
// markerId: mId,
// position: newPosition,
// rotation: newHeading,
// icon: InlqBitmap.fromStyleImage(icon),
// anchor: const Offset(0.5, 0.5),
// ),
// };
// update();
// }
// mapController?.animateCamera(CameraUpdate.newLatLng(newPosition));
// }
// @override
// void onClose() {
// Log.print(
// "--- MapPassengerController: Closing and cleaning up all resources. ---");
// // 1. إلغاء المؤقتات الفردية (باستخدام ?. الآمن)
// timerToPassengerFromDriverAfterApplied?.cancel();
// _timer?.cancel();
// _masterTimer?.cancel(); // (أضف المؤقت الرئيسي)
// _camThrottle?.cancel(); // (أضف مؤقت الكاميرا)
// _heartbeatTimer?.cancel();
// EmergencySignalService.instance.stopListening();
// if (isSocketConnected) {
// socket.emit('unsubscribe_all',
// {'passenger_id': box.read(BoxName.passengerID).toString()});
// socket.disconnect();
// socket.dispose();
// }
// // 2. إلغاء جميع المؤقتات في الخريطة (للتحريكات السلسة)
// _animationTimers.forEach((key, timer) {
// timer.cancel();
// });
// _animationTimers.clear();
// // 3. إغلاق متحكمات البث (StreamControllers) لمنع تسريب الذاكرة
// if (!_timerStreamController.isClosed) {
// _timerStreamController.close();
// }
// if (!_beginRideStreamController.isClosed) {
// _beginRideStreamController.close();
// }
// if (!_rideStatusStreamController.isClosed) {
// _rideStatusStreamController.close();
// }
// if (!timerController.isClosed) {
// timerController.close();
// }
// // 4. التخلص من متحكم الخريطة (ممارسة جيدة)
// mapController = null;
// Log.print("--- Cleanup complete. ---");
// super.onClose();
// }
// restCounter() {
// clearPlacesDestination();
// clearPolyline();
// data = [];
// rideConfirm = false;
// shouldFetch = false;
// timeToPassengerFromDriverAfterApplied = 0;
// update();
// }
// //driver behaviour
// double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
// double deltaLon = lon2 - lon1;
// double y = sin(deltaLon) * cos(lat2);
// double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaLon);
// double bearing = atan2(y, x);
// return (bearing * 180 / pi + 360) % 360; // تحويل إلى درجات
// }
// void analyzeBehavior(Position currentPosition, List<LatLng> routePoints) {
// double actualBearing = currentPosition.heading; // الاتجاه الفعلي من GPS
// double expectedBearing = calculateBearing(
// routePoints[0].latitude,
// routePoints[0].longitude,
// routePoints[1].latitude,
// routePoints[1].longitude,
// );
// double bearingDifference = (expectedBearing - actualBearing).abs();
// if (bearingDifference > 30) {
// Log.print("⚠️ السائق انحرف عن المسار!");
// }
// }
// void detectStops(Position currentPosition) {
// if (currentPosition.speed < 0.5) {
// Log.print("🚦 السائق توقف في موقع غير متوقع!");
// }
// }
// Future<void> cancelRideAfterRejectFromAll() async {
// clearPlacesDestination();
// clearPolyline();
// data = [];
// await CRUD().post(
// link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php",
// payload: {
// "ride_id": rideId.toString(), // Convert to String
// "reason": 'notApplyFromAnyDriver'
// });
// rideConfirm = false;
// statusRide == 'Cancel';
// isSearchingWindow = false;
// shouldFetch = false;
// isPassengerChosen = false;
// isCashConfirmPageShown = false;
// // totalStepDurations = 0;
// isCashSelectedBeforeConfirmRide = false;
// timeToPassengerFromDriverAfterApplied = 0;
// changeCancelRidePageShow();
// remainingTime = 0;
// update();
// }
// // متغيرات أسباب الإلغاء
// int selectedReasonIndex = -1;
// String selectedReasonText = "";
// TextEditingController otherReasonController = TextEditingController();
// /// تحديث السبب المختار
// void selectReason(int index, String reason) {
// selectedReasonIndex = index;
// selectedReasonText = reason;
// update();
// }
// /// **دالة إلغاء الرحلة (النهائية)**
// Future<void> cancelRide() async {
// // 1. التحقق من اختيار سبب
// if (selectedReasonIndex == -1) {
// Get.snackbar(
// 'Attention'.tr,
// 'Please select a reason first'.tr,
// snackPosition: SnackPosition.BOTTOM,
// backgroundColor: Colors.orange,
// colorText: Colors.white,
// );
// return;
// }
// // 2. تجهيز نص السبب النهائي
// String finalReason = selectedReasonText;
// if (finalReason == "Other".tr) {
// if (otherReasonController.text.trim().isEmpty) {
// Get.snackbar("Attention".tr, "Please write the reason...".tr,
// backgroundColor: Colors.red, colorText: Colors.white);
// return;
// }
// finalReason = otherReasonController.text.trim();
// }
// // 3. التنظيف المحلي الفوري (UX Optimization)
// Get.back(); // إغلاق الـ BottomSheet
// if (isCancelRidePageShown)
// changeCancelRidePageShow(); // إخفاء زر الإلغاء إن وجد
// // 🔥 استدعاء دالة التنظيف الشاملة هنا 🔥
// resetAllMapStates();
// // إيقاف جميع التايمرات
// // إيقاف جميع التايمرات
// stopAllTimers();
// currentRideState.value = RideState.cancelled;
// await RideLiveNotification.cancel(); // إغلاق أندرويد
// IosLiveActivityService.endRideActivity(); // ✅ إغلاق iOS
// PipService.disablePip(); // ✅ إيقاف PiP عند الإلغاء
// // 4. الاتصال بالسيرفر لإلغاء الرحلة وإبلاغ السائق
// if (rideId != 'yet' && rideId != null) {
// Log.print(
// '📡 Sending Cancel Request to Server with Reason: $finalReason');
// try {
// await CRUD().post(
// link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php",
// payload: {
// "ride_id": rideId.toString(),
// "reason": finalReason // ✅ إرسال السبب للسيرفر
// },
// );
// // لا داعي لإرسال FCM أو Socket يدوياً من هنا، PHP يقوم بذلك
// } catch (e) {
// Log.print("Error cancelling on server: $e");
// }
// }
// // 5. العودة للصفحة الرئيسية
// Get.offAll(() => const MapPagePassenger());
// }
// void changePickerShown() {
// isPickerShown = !isPickerShown;
// heightPickerContainer = isPickerShown == true ? 150 : 90;
// update();
// }
// // ── Multi-Waypoint Methods ──────────────────────────────────────────────────
// void addMenuWaypoint() {
// if (activeMenuWaypointCount >= 2) return;
// activeMenuWaypointCount++;
// // Increase expanded bottom menu height to accommodate new waypoint row
// mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56);
// update();
// }
// void removeMenuWaypoint(int index) {
// if (index < 0 || index >= 2) return;
// // Shift items if removing first waypoint while second exists
// if (index == 0 && activeMenuWaypointCount == 2) {
// menuWaypoints[0] = menuWaypoints[1];
// menuWaypointNames[0] = menuWaypointNames[1];
// }
// menuWaypoints[activeMenuWaypointCount - 1] = null;
// menuWaypointNames[activeMenuWaypointCount - 1] = '';
// activeMenuWaypointCount--;
// mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56);
// update();
// }
// void clearAllMenuWaypoints() {
// menuWaypoints = [null, null];
// menuWaypointNames = ['', ''];
// activeMenuWaypointCount = 0;
// isPickingWaypoint = false;
// pickingWaypointIndex = -1;
// update();
// }
// void startPickingWaypointOnMap(int index) {
// pickingWaypointIndex = index;
// isPickingWaypoint = true;
// isPickerShown = true;
// heightPickerContainer = 150;
// // Close the expanded menu to show the map picker
// isMainBottomMenuMap = true;
// mainBottomMenuMapHeight = Get.height * .22;
// update();
// }
// void setMenuWaypointFromMap(int index, LatLng position) {
// Log.print('📍 setMenuWaypointFromMap called: index=$index, pos=$position');
// if (index < 0 || index >= 2) return;
// menuWaypoints[index] = position;
// menuWaypointNames[index] =
// '${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}';
// isPickingWaypoint = false;
// pickingWaypointIndex = -1;
// isPickerShown = false;
// // Re-open expanded menu
// isMainBottomMenuMap = false;
// mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56);
// update();
// }
// void setMenuWaypointFromSearch(int index, LatLng pos, String name) {
// if (index < 0 || index >= 2) return;
// menuWaypoints[index] = pos;
// menuWaypointNames[index] = name;
// update();
// }
// /// Build OSRM waypoint coordinate string for the route URL
// String _buildOsrmWaypointCoords() {
// String coords = '';
// for (int i = 0; i < activeMenuWaypointCount; i++) {
// final wp = menuWaypoints[i];
// if (wp != null) {
// coords += ';${wp.longitude},${wp.latitude}';
// }
// }
// return coords;
// }
// void changeHeightPointsPageForRider() {
// isPointsPageForRider = !isPointsPageForRider;
// heightPointsPageForRider = isPointsPageForRider == true ? Get.height : 0;
// update();
// }
// getCoordinateFromMapWayPoints(int index) {
// placesCoordinate[index] = newStartPointLocation.toString();
// update();
// }
// // --- ابدأ الإضافة هنا ---
// // 1. قائمة لتخزين نقاط التوقف
// List<Map<String, dynamic>> waypoints = [];
// // 2. دالة لإضافة نقطة توقف جديدة
// void addWaypoint(Map<String, dynamic> placeDetails) {
// // يمكنك إضافة منطق للتحقق من عدد نقاط التوقف المسموح بها هنا
// waypoints.add(placeDetails);
// update(); // لتحديث الواجهة
// // TODO: أضف هنا استدعاء دالة إعادة رسم المسار مع نقاط التوقف الجديدة
// // getDirectionMapWithWaypoints();
// }
// // 3. دالة لحذف نقطة توقف
// void removeWaypoint(int index) {
// if (index >= 0 && index < waypoints.length) {
// waypoints.removeAt(index);
// update(); // لتحديث الواجهة
// // TODO: أضف هنا استدعاء دالة إعادة رسم المسار بعد حذف النقطة
// // getDirectionMapWithWaypoints();
// }
// }
// // --- انتهى ---
// void changeMainBottomMenuMap() {
// if (isWayPointStopsSheetUtilGetMap == true) {
// changeWayPointSheet();
// } else {
// isMainBottomMenuMap = !isMainBottomMenuMap;
// mainBottomMenuMapHeight =
// isMainBottomMenuMap == true ? Get.height * .22 : Get.height * .6;
// isWayPointSheet = false;
// if (heightMenuBool == true) {
// getDrawerMenu();
// }
// initilizeGetStorage();
// update();
// }
// }
// void downPoints() {
// if (Get.find<WayPointController>().wayPoints.length < 2) {
// isWayPointStopsSheetUtilGetMap = false;
// isWayPointSheet = false;
// wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
// // changeWayPointStopsSheet();
// update();
// }
// // changeWayPointStopsSheet();
// // isWayPointSheet = false;
// update();
// }
// void changeWayPointSheet() {
// isWayPointSheet = !isWayPointSheet;
// wayPointSheetHeight = isWayPointSheet == false ? 0 : Get.height * .45;
// // if (heightMenuBool == true) {
// // getDrawerMenu();
// // }
// update();
// }
// void changeWayPointStopsSheet() {
// // int waypointsLength = Get.find<WayPointController>().wayPoints.length;
// if (wayPointIndex > -1) {
// isWayPointStopsSheet = true;
// isWayPointStopsSheetUtilGetMap = true;
// }
// isWayPointStopsSheet = !isWayPointStopsSheet;
// wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
// // if (heightMenuBool == true) {
// // getDrawerMenu();
// // }
// update();
// }
// changeHeightPlaces() {
// if (placesDestination.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightStartPlaces() {
// if (placesStart.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightPlacesAll(int index) {
// if (placeListResponseAll[index].isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightPlaces1() {
// if (wayPoint1.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightPlaces2() {
// if (wayPoint2.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightPlaces3() {
// if (wayPoint3.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// changeHeightPlaces4() {
// if (wayPoint4.isEmpty) {
// height = 0;
// update();
// }
// height = 150;
// update();
// }
// hidePlaces() {
// height = 0;
// update();
// }
// /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
// // double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
// // const R = 6371.0; // km
// // final dLat = (lat2 - lat1) * math.pi / 180.0;
// // final dLon = (lon2 - lon1) * math.pi / 180.0;
// // final a = math.sin(dLat / 2) * math.sin(dLat / 2) +
// // math.cos(lat1 * math.pi / 180.0) *
// // math.cos(lat2 * math.pi / 180.0) *
// // math.sin(dLon / 2) *
// // math.sin(dLon / 2);
// // final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
// // return R * c;
// // }
// /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
// // double _kmToLatDelta(double km) => km / 111.0;
// // /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض)
// // double _kmToLngDelta(double km, double atLat) =>
// // km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9);
// /// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة)
// // double _relevanceScore(String name, String query) {
// // final n = name.toLowerCase();
// // final parts =
// // query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2);
// // double s = 0.0;
// // for (final p in parts) {
// // if (n.startsWith(p)) {
// // s += 2.0;
// // } else if (n.contains(p)) {
// // s += 1.0;
// // }
// // }
// // return s;
// // }
// // الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها
// // انسخ هذه الدوال والصقها داخل كلاس الكنترولر الخاص بك
// // -----------------------------------------------------------------
// // --== الدالة الرئيسية للبحث ==--
// // -----------------------------------------------------------------
// /// الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها
// // انسخ هذه الدوال والصقها داخل كلاس الكنترولر الخاص بك
// // -----------------------------------------------------------------
// // --== الدالة الرئيسية للبحث ==--
// // -----------------------------------------------------------------
// /// الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها
// Future<void> getPlaces() async {
// final q = placeDestinationController.text.trim();
// if (q.isEmpty || q.length < 3) {
// placesDestination = [];
// update();
// return;
// }
// final lat = passengerLocation.latitude;
// final lng = passengerLocation.longitude;
// final country = CountryPolygons.getCountryName(passengerLocation);
// try {
// final url =
// '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
// final response = await CRUD().getMapSaas(link: url);
// if (response != null && response['results'] is List) {
// List results = List.from(response['results']);
// final List filteredResults = [];
// final Set<String> seenPlaces = {};
// for (final p in results) {
// final name = p['name_ar'] ?? p['name'] ?? '';
// final district = p['district'] ?? '';
// final plat = p['latitude']?.toString() ?? '0';
// final plng = p['longitude']?.toString() ?? '0';
// final dedupeKey =
// "${name.trim().toLowerCase()}_${district.trim().toLowerCase()}";
// if (!seenPlaces.contains(dedupeKey)) {
// seenPlaces.add(dedupeKey);
// p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
// p['latitude'] = plat;
// p['longitude'] = plng;
// p['name'] = name;
// p['address'] = p['full_address'] ??
// (district.isNotEmpty
// ? "$district، ${p['governorate'] ?? ''}"
// : (p['governorate'] ?? ''));
// filteredResults.add(p);
// }
// }
// placesDestination = filteredResults;
// update();
// }
// } catch (e) {
// Log.print('Exception in getPlaces: $e');
// }
// }
// // -----------------------------------------------------------------
// // --== دوال مساعدة ==--
// // -----------------------------------------------------------------
// /// تحسب المسافة بين نقطتين بالكيلومتر (معادلة هافرساين)
// double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
// const R = 6371.0; // نصف قطر الأرض بالكيلومتر
// final dLat = (lat2 - lat1) * (pi / 180.0);
// final dLon = (lon2 - lon1) * (pi / 180.0);
// final rLat1 = lat1 * (pi / 180.0);
// final rLat2 = lat2 * (pi / 180.0);
// final a = sin(dLat / 2) * sin(dLat / 2) +
// cos(rLat1) * cos(rLat2) * sin(dLon / 2) * sin(dLon / 2);
// final c = 2 * atan2(sqrt(a), sqrt(1 - a));
// return R * c;
// }
// /// تحسب درجة تطابق بسيطة بين اسم المكان وكلمة البحث
// double _relevanceScore(String placeName, String query) {
// if (placeName.isEmpty || query.isEmpty) return 0.0;
// final pLower = placeName.toLowerCase();
// final qLower = query.toLowerCase();
// if (pLower.startsWith(qLower)) return 1.0; // تطابق كامل في البداية
// if (pLower.contains(qLower)) return 0.5; // تحتوي على الكلمة
// return 0.0;
// }
// /// تحويل كيلومتر إلى فرق درجات لخط العرض
// double _kmToLatDelta(double km) {
// const kmInDegree = 111.32;
// return km / kmInDegree;
// }
// /// تحويل كيلومتر إلى فرق درجات لخط الطول (يعتمد على خط العرض الحالي)
// double _kmToLngDelta(double km, double latitude) {
// const kmInDegree = 111.32;
// return km / (kmInDegree * cos(latitude * (pi / 180.0)));
// }
// // var languageCode;
// // // تحديد اللغة حسب الإدخال
// // if (RegExp(r'[a-zA-Z]').hasMatch(placeDestinationController.text)) {
// // languageCode = 'en';
// // } else {
// // languageCode = 'ar';
// // }
// // final bool isTextEmpty = placeDestinationController.text.trim().isEmpty;
// // var key = Platform.isAndroid ? AK.mapAPIKEY : AK.mapAPIKEYIOS;
// // final Uri url = Uri.parse(
// // isTextEmpty
// // ? 'https://places.googleapis.com/v1/places:searchNearby?key=$key'
// // : 'https://places.googleapis.com/v1/places:searchText?key=$key',
// // );
// // Log.print('url: $url');
// // // بناء الجسم حسب نوع الطلب
// // final body = isTextEmpty
// // ? jsonEncode({
// // "languageCode": languageCode,
// // "locationRestriction": {
// // "circle": {
// // "center": {
// // "latitude": passengerLocation.latitude,
// // "longitude": passengerLocation.longitude
// // },
// // "radius": 40000 // 40 كم
// // }
// // },
// // "maxResultCount": 10
// // })
// // : jsonEncode({
// // "textQuery": placeDestinationController.text,
// // "languageCode": languageCode,
// // "maxResultCount": 10,
// // "locationBias": {
// // "circle": {
// // "center": {
// // "latitude": passengerLocation.latitude,
// // "longitude": passengerLocation.longitude
// // },
// // "radius": 40000
// // }
// // }
// // });
// // final headers = {
// // 'Content-Type': 'application/json',
// // 'X-Goog-Api-Key': AK.mapAPIKEY,
// // 'X-Goog-FieldMask':
// // 'places.displayName,places.formattedAddress,places.location'
// // };
// // try {
// // final response = await http.post(url, headers: headers, body: body);
// // Log.print('response: ${response.statusCode} - ${response.body}');
// // if (response.statusCode == 200) {
// // final data = jsonDecode(response.body);
// // placesDestination = data['places'] ?? [];
// // update();
// // } else {
// // Log.print('Error: ${response.statusCode} - ${response.reasonPhrase}');
// // }
// // } catch (e) {
// // Log.print('Exception: $e');
// // }
// // }
// getAIKey(String key) async {
// var res =
// await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key});
// if (res != 'failure') {
// var d = jsonDecode(res)['message'];
// return d[key].toString();
// } else {}
// }
// Future<void> getPlacesStart() async {
// final q = placeStartController.text.trim();
// if (q.isEmpty || q.length < 3) {
// placesStart = [];
// update();
// return;
// }
// final lat = passengerLocation.latitude;
// final lng = passengerLocation.longitude;
// final country = CountryPolygons.getCountryName(passengerLocation);
// try {
// final url =
// '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
// final response = await CRUD().getMapSaas(link: url);
// if (response != null && response['results'] is List) {
// List list = List.from(response['results']);
// for (final p in list) {
// p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
// p['latitude'] = p['latitude'].toString();
// p['longitude'] = p['longitude'].toString();
// p['name'] = p['name_ar'] ?? p['name'] ?? '';
// p['address'] = p['full_address'] ??
// (p['district'] != null
// ? "${p['district']}، ${p['governorate'] ?? ''}"
// : (p['governorate'] ?? ''));
// }
// placesStart = list;
// update();
// }
// } catch (e) {
// Log.print('Exception in getPlacesStart: $e');
// }
// }
// Future<void> getPlacesListsWayPoint(int index) async {
// final q = wayPoint0Controller.text.trim();
// if (q.length < 3) return;
// final lat = passengerLocation.latitude;
// final lng = passengerLocation.longitude;
// final country = CountryPolygons.getCountryName(passengerLocation);
// try {
// final url =
// '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
// final response = await CRUD().getMapSaas(link: url);
// if (response != null && response['results'] is List) {
// List list = List.from(response['results']);
// for (final p in list) {
// p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
// p['latitude'] = p['latitude'].toString();
// p['longitude'] = p['longitude'].toString();
// p['name'] = p['name_ar'] ?? p['name'] ?? '';
// p['address'] = p['full_address'] ??
// (p['district'] != null
// ? "${p['district']}، ${p['governorate'] ?? ''}"
// : (p['governorate'] ?? ''));
// }
// wayPoint0 = list;
// placeListResponseAll[index] = list;
// update();
// }
// } catch (e) {
// Log.print('Error fetching places in WayPoint: $e');
// }
// }
// // داخل MapPassengerController
// bool lowPerf = false;
// Timer? _camThrottle;
// DateTime _lastUiUpdate = DateTime.fromMillisecondsSinceEpoch(0);
// Future<void> detectPerfMode() async {
// try {
// if (GetPlatform.isAndroid) {
// final info = await DeviceInfoPlugin().androidInfo;
// final sdk = info.version.sdkInt ?? 0;
// final ram = info.availableRamSize ?? 0;
// lowPerf = (sdk < 28) || (ram > 0 && ram < 3 * 1024 * 1024 * 1024);
// } else {
// lowPerf = false;
// }
// } catch (_) {
// lowPerf = false;
// }
// update();
// }
// // تحديث الكاميرا بثروتل
// void onCameraMoveThrottled(CameraPosition pos) {
// _camThrottle?.cancel();
// _camThrottle = Timer(const Duration(milliseconds: 160), () {
// Log.print('📸 onCameraMoveThrottled: ${pos.target}');
// // ضع فقط المنطق الضروري هنا لتقليل الحمل
// int waypointsLength = Get.find<WayPointController>().wayPoints.length;
// int index = wayPointIndex;
// if (waypointsLength > 0) {
// placesCoordinate[index] =
// '${pos.target.latitude},${pos.target.longitude}';
// }
// newMyLocation = pos.target;
// });
// }
// // Removed legacy light polylines since MapLibre vectors handle high-point geometries natively.
// Future<void> savePlaceToServer(
// String latitude, String longitude, String name, String rate) async {
// var data = {
// 'latitude': latitude,
// 'longitude': longitude,
// 'name': name,
// 'rate': rate,
// };
// try {
// CRUD().post(
// link: AppLink.savePlacesServer,
// payload: data,
// );
// } catch (e) {
// Log.print('Error: $e');
// }
// }
// Future<void> getLocation() async {
// Log.print('🛰️ getLocation() called');
// // Check if the app has permission to access location
// permissionGranted = await location.hasPermission();
// if (permissionGranted == PermissionStatus.denied) {
// permissionGranted = await location.requestPermission();
// if (permissionGranted != PermissionStatus.granted) {
// // Location permission is still not granted, handle the error
// return;
// }
// }
// // Configure location accuracy
// // LocationAccuracy desiredAccuracy = LocationAccuracy.high;
// // Get the current location with a timeout to prevent hanging UI
// LocationData? _locationData;
// try {
// _locationData = await location.getLocation().timeout(
// const Duration(seconds: 5),
// onTimeout: () {
// Log.print("⚠️ Location fetch timed out after 5s.");
// return LocationData.fromMap({
// "latitude": passengerLocation.latitude,
// "longitude": passengerLocation.longitude,
// "speed": 0.0
// });
// },
// );
// } catch (e) {
// Log.print("⚠️ Error fetching location: $e");
// }
// if (_locationData == null) {
// isLoading = false;
// update();
// return;
// }
// passengerLocation =
// (_locationData.latitude != null && _locationData.longitude != null
// ? LatLng(_locationData.latitude!, _locationData.longitude!)
// : null)!;
// // getLocationArea(passengerLocation.latitude, passengerLocation.longitude);
// // Log.print('AppLink.endPoint: ${AppLink.endPoint}');
// // Log.print('BoxName.serverChosen: ${box.read(BoxName.serverChosen)}');
// newStartPointLocation = passengerLocation;
// newMyLocation = passengerLocation;
// // Resolve current location address
// try {
// getReverseGeocoding(passengerLocation).then((address) {
// currentLocationString = address;
// update();
// });
// } catch (e) {
// Log.print('Error resolving current location: $e');
// }
// // Trigger offline map caching for a 10km radius
// OfflineMapService.instance
// .downloadRegion(passengerLocation, radiusKm: 10.0);
// speed = _locationData.speed!;
// // //print location details
// isLoading = false;
// update();
// }
// void clearPolyline() {
// polyLines.clear();
// update();
// }
// LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
// const double earthRadius = 6378137.0; // Earth's radius in meters
// double latDelta = (radiusInMeters / earthRadius) * (180 / pi);
// double lngDelta =
// (radiusInMeters / (earthRadius * cos(pi * lat / 180))) * (180 / pi);
// double minLat = lat - latDelta;
// double maxLat = lat + latDelta;
// double minLng = lng - lngDelta;
// double maxLng = lng + lngDelta;
// // Ensure the latitude is between -90 and 90
// minLat = max(-90.0, minLat);
// maxLat = min(90.0, maxLat);
// // Ensure the longitude is between -180 and 180
// minLng = (minLng + 180) % 360 - 180;
// maxLng = (maxLng + 180) % 360 - 180;
// // Ensure the bounds are in the correct order
// if (minLng > maxLng) {
// double temp = minLng;
// minLng = maxLng;
// maxLng = temp;
// }
// return LatLngBounds(
// southwest: LatLng(minLat, minLng),
// northeast: LatLng(maxLat, maxLng),
// );
// }
// void onMapCreated(IntaleqMapController controller) {
// mapController = controller;
// update();
// }
// void onStyleLoaded() async {
// Log.print('🗺️ Intaleq Map Style Loaded. Initializing...');
// isStyleLoaded = true;
// _loadMapIcons();
// // Smart Camera Reset logic:
// if (mapController != null) {
// if (markers.isNotEmpty && lastComputedBounds != null) {
// await _safeAnimateCameraBounds(lastComputedBounds);
// } else {
// mapController!.animateCamera(
// CameraUpdate.newLatLng(passengerLocation),
// );
// }
// }
// update();
// }
// /// Safe wrapper for animateCamera Bounds to prevent native std::domain_error crash on iOS.
// Future<void> _safeAnimateCameraBounds(LatLngBounds? bounds,
// {double left = 60,
// double top = 60,
// double right = 60,
// double bottom = 60}) async {
// if (bounds == null || mapController == null) return;
// try {
// // Ensure the coordinates are valid
// if (bounds.northeast.latitude == bounds.southwest.latitude &&
// bounds.northeast.longitude == bounds.southwest.longitude) {
// Log.print(
// '⚠️ _safeAnimateCameraBounds: Bounds are a single point, zooming to point instead.');
// await mapController
// ?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 15));
// return;
// }
// // Small delay to ensure iOS view layout is fully ready
// await Future.delayed(const Duration(milliseconds: 200));
// await mapController?.animateCamera(
// CameraUpdate.newLatLngBounds(
// bounds,
// left: left,
// top: top,
// right: right,
// bottom: bottom,
// ),
// );
// } catch (e) {
// Log.print('❌ _safeAnimateCameraBounds CRASH PREVENTED: $e');
// // Final fallback to prevent device freeze
// try {
// await mapController
// ?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 14));
// } catch (_) {}
// }
// }
// Future<void> _loadMapIcons() async {
// // Wait up to 3 seconds for the map style to finish loading
// for (int i = 0; i < 15; i++) {
// if (mapController != null && isStyleLoaded) break;
// await Future.delayed(const Duration(milliseconds: 200));
// }
// if (mapController == null || !isStyleLoaded) {
// Log.print(
// '⚠️ _loadMapIcons: mapController or style not ready. Icons may not load.');
// }
// await _addMapImage(startIcon, 'assets/images/A.png');
// await _addMapImage(endIcon, 'assets/images/b.png');
// await _addMapImage(carIcon, 'assets/images/car.png');
// await _addMapImage(motoIcon, 'assets/images/moto.png');
// await _addMapImage(ladyIcon, 'assets/images/lady.png');
// await _addMapImage('picker_icon', 'assets/images/picker.png');
// // Waypoint markers - use moto1 & lady1 as colored waypoint icons
// await _addMapImage('orange_marker', 'assets/images/moto1.png');
// await _addMapImage('violet_marker', 'assets/images/lady1.png');
// }
// Future<void> _addMapImage(String id, String path) async {
// try {
// final ByteData bytes = await rootBundle.load(path);
// // Resize car icons for better visibility on map (e.g. 120px)
// final size = _getImageSize(id);
// if (size != null && (id == carIcon || id == motoIcon || id == ladyIcon)) {
// final resized = await _resizeImage(bytes.buffer.asUint8List(), size);
// await mapController?.addImage(id, resized);
// Log.print(
// '✅ Successfully added resized map image: $id (${size}x${size})');
// } else {
// await mapController?.addImage(id, bytes.buffer.asUint8List());
// Log.print('✅ Successfully added map image: $id');
// }
// } catch (e) {
// Log.print('❌ Error loading map icon $id: $e');
// }
// }
// int? _getImageSize(String id) {
// if (id == carIcon || id == motoIcon || id == ladyIcon) return 120;
// return null;
// }
// Future<Uint8List> _resizeImage(Uint8List bytes, int size) async {
// return await compute((Uint8List data) {
// final image = img.decodeImage(data);
// if (image == null) return data;
// final resized = img.copyResize(image, width: size, height: size);
// return Uint8List.fromList(img.encodePng(resized));
// }, bytes);
// }
// // Wait up to 3 seconds for the map style to finish loading
// void updateCurrentLocationFromCamera(LatLng target) {
// Log.print('📍 updateCurrentLocationFromCamera: $target');
// newMyLocation = target;
// if (startLocationFromMap == true) {
// Log.print('📍 Updating startLocationFromMap to $target');
// newStartPointLocation = target;
// } else if (passengerStartLocationFromMap == true) {
// Log.print('📍 Updating passengerStartLocationFromMap to $target');
// newStartPointLocation = target;
// }
// int waypointsLength = Get.find<WayPointController>().wayPoints.length;
// if (waypointsLength > 0 &&
// wayPointIndex >= 0 &&
// wayPointIndex < placesCoordinate.length) {
// Log.print('📍 Updating wayPointIndex $wayPointIndex to $target');
// placesCoordinate[wayPointIndex] =
// '${target.latitude},${target.longitude}';
// }
// update();
// }
// String durationByPassenger = '';
// late DateTime newTime1 = DateTime.now();
// late DateTime timeFromDriverToPassenger = DateTime.now();
// String distanceByPassenger = '';
// late Duration durationFromDriverToPassenger;
// double nearestDistance = double.infinity;
// Future<CarLocation?> getNearestDriverByPassengerLocation() async {
// if (!rideConfirm) {
// if (dataCarsLocationByPassenger != 'failure' &&
// dataCarsLocationByPassenger != null &&
// dataCarsLocationByPassenger['message'] != null &&
// dataCarsLocationByPassenger['message'].length > 0) {
// double nearestDistance = double.infinity; // Initialize nearest distance
// CarLocation? nearestCar;
// for (var i = 0;
// i < dataCarsLocationByPassenger['message'].length;
// i++) {
// var carLocation = dataCarsLocationByPassenger['message'][i];
// // Log.print('carLocation: $carLocation');
// try {
// // Calculate distance between passenger's location and current driver's location
// final distance = Geolocator.distanceBetween(
// passengerLocation.latitude,
// passengerLocation.longitude,
// double.parse(carLocation['latitude']),
// double.parse(carLocation['longitude']),
// );
// // Calculate duration assuming an average speed of 25 km/h (adjust as needed)
// int durationToPassenger = (distance / 1000 / 25 * 3600).round();
// // Log.print('distance: $distance');
// // Log.print('durationToPassenger: $durationToPassenger');
// // Log.print('passengerLocation: $passengerLocation');
// // Log.print('carLocation: $carLocation');
// // Log.print('distance: $distance meters');
// // Log.print('durationToPassenger: $durationToPassenger seconds');
// // Update the UI with the distance and duration for each car
// update();
// // If this distance is smaller than the nearest distance found so far, update nearestCar
// if (distance < nearestDistance) {
// nearestDistance = distance;
// nearestCar = CarLocation(
// distance: distance,
// duration: durationToPassenger.toDouble(),
// id: carLocation['driver_id'],
// latitude: double.parse(carLocation['latitude']),
// longitude: double.parse(carLocation['longitude']),
// );
// // Log.print('nearestCar: $nearestCar');
// // Update the UI with the nearest driver
// update();
// }
// } catch (e) {
// Log.print('Error calculating distance/duration: $e');
// }
// }
// // Return the nearest car found
// return nearestCar;
// }
// }
// // Return null if no drivers are found or if ride is confirmed
// return null;
// }
// getNearestDriverByPassengerLocationAPIGOOGLE() async {
// if (polyLines.isEmpty || data.isEmpty) {
// return null; // Early return if data is empty
// }
// if (!rideConfirm) {
// double nearestDistance = double.infinity;
// if (dataCarsLocationByPassenger != 'failure') {
// if (dataCarsLocationByPassenger['message'].length > 0) {
// for (var i = 0;
// i < dataCarsLocationByPassenger['message'].length;
// i++) {
// var carLocation = dataCarsLocationByPassenger['message'][i];
// // }
// // isloading = true;
// update();
// // Make API request to get exact distance and duration
// String apiUrl =
// '${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${passengerLocation.latitude},${passengerLocation.longitude}&units=metric&key=${AK.mapAPIKEY}';
// var response = await CRUD().getGoogleApi(link: apiUrl, payload: {});
// if (response != null && response['status'] == "OK") {
// var data = response;
// // Extract distance and duration from the response and handle accordingly
// int distance1 =
// data['rows'][0]['elements'][0]['distance']['value'];
// distanceByPassenger =
// data['rows'][0]['elements'][0]['distance']['text'];
// durationToPassenger =
// data['rows'][0]['elements'][0]['duration']['value'];
// durationFromDriverToPassenger =
// Duration(seconds: durationToPassenger.toInt());
// newTime1 = currentTime.add(durationFromDriverToPassenger);
// timeFromDriverToPassenger =
// newTime1.add(Duration(minutes: 2.toInt()));
// durationByPassenger =
// data['rows'][0]['elements'][0]['duration']['text'];
// update();
// if (distance1 < nearestDistance) {
// nearestDistance = distance1.toDouble();
// nearestCar = CarLocation(
// distance: distance1.toDouble(),
// duration: durationToPassenger.toDouble(),
// id: carLocation['driver_id'],
// latitude: double.parse(carLocation['latitude']),
// longitude: double.parse(carLocation['longitude']),
// );
// // isloading = false;
// update();
// }
// }
// // Handle the distance and duration as needed
// else {
// // 'Failed to retrieve distance and duration: ${response['status']}');
// Log.print('${response['status']}: ${response['status']}}');
// // Handle the failure case
// }
// }
// }
// }
// }
// }
// calculateDistanceBetweenPassengerAndDriverBeforeCancelRide() async {
// await getDriverCarsLocationToPassengerAfterApplied();
// double distance = Geolocator.distanceBetween(
// passengerLocation.latitude,
// passengerLocation.longitude,
// driverCarsLocationToPassengerAfterApplied.last.latitude,
// driverCarsLocationToPassengerAfterApplied.last.longitude,
// );
// if (distance > 500) {
// isCancelRidePageShown = true;
// update();
// } else {
// Get.defaultDialog(
// barrierDismissible: false,
// title: 'The Driver Will be in your location soon .'.tr,
// middleText: 'The distance less than 500 meter.'.tr,
// confirm: Column(
// children: [
// MyElevatedButton(
// kolor: AppColor.greenColor,
// title: 'Ok'.tr,
// onPressed: () {
// Get.back();
// },
// ),
// MyElevatedButton(
// kolor: AppColor.redColor,
// title: 'No, I want to cancel this trip'.tr,
// onPressed: () {
// Get.back();
// MyDialog().getDialog(
// 'Attention'.tr,
// 'You will be charged for the cost of the driver coming to your location.'
// .tr,
// () async {
// Get.back();
// Get.find<PaymentController>()
// .payToDriverForCancelAfterAppliedAndHeNearYou(rideId);
// // isCancelRidePageShown = true;
// // update();
// },
// );
// },
// ),
// ],
// ),
// );
// // cancel: MyElevatedButton(
// // title: 'No.Iwant Cancel Trip.'.tr, onPressed: () {}));
// }
// }
// List<double> headingAngles = [];
// double calculateAngleBetweenLocations(LatLng start, LatLng end) {
// double startLat = start.latitude * math.pi / 180;
// double startLon = start.longitude * math.pi / 180;
// double endLat = end.latitude * math.pi / 180;
// double endLon = end.longitude * math.pi / 180;
// double dLon = endLon - startLon;
// double y = math.sin(dLon) * cos(endLat);
// double x = cos(startLat) * math.sin(endLat) -
// math.sin(startLat) * cos(endLat) * cos(dLon);
// double angle = math.atan2(y, x);
// double angleDegrees = angle * 180 / math.pi;
// return angleDegrees;
// }
// late LatLngBounds boundsData;
// late String startNameAddress = '';
// late String endNameAddress = '';
// List<Map<String, dynamic>> stopPoints = [];
// void removeStop(Map<String, dynamic> stop) {
// stopPoints.remove(stop);
// update(); // Trigger a rebuild of the UI
// }
// Future<String> getReverseGeocoding(LatLng location) async {
// final lat = location.latitude;
// final lng = location.longitude;
// final url = '${AppLink.reverseGeocoding}?lat=$lat&lng=$lng';
// try {
// final response = await CRUD().getMapSaas(link: url);
// if (response != null && response is List && response.isNotEmpty) {
// final data = response[0];
// String name = data['name_ar'] ?? data['name'] ?? 'Unknown Location'.tr;
// return name;
// }
// return 'Unknown Location'.tr;
// } catch (e) {
// Log.print('ReverseGeocoding Exception: $e');
// return 'Unknown Location'.tr;
// }
// }
// bool isDrawingRoute = false;
// void showDrawingBottomSheet() {
// Log.print(
// '🔔 showDrawingBottomSheet called. isDrawingRoute: $isDrawingRoute');
// final context = Get.context;
// if (context == null) return;
// WidgetsBinding.instance.addPostFrameCallback((_) {
// // Close any existing open dialogs first
// if (Get.isDialogOpen == true) {
// Get.back();
// }
// Get.dialog(
// Dialog(
// backgroundColor: Colors.transparent,
// elevation: 0,
// child: Container(
// padding: const EdgeInsets.all(24),
// width: 180,
// decoration: BoxDecoration(
// color: Colors.white.withOpacity(0.95),
// borderRadius: BorderRadius.circular(24),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.15),
// blurRadius: 20,
// spreadRadius: 5,
// )
// ],
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// // App Logo
// Image.asset(
// 'assets/images/logo.gif',
// height: 64,
// errorBuilder: (context, error, stackTrace) => const Icon(
// Icons.map,
// size: 64,
// color: AppColor.primaryColor,
// ),
// ),
// const SizedBox(height: 16),
// const SizedBox(
// width: 24,
// height: 24,
// child: MyCircularProgressIndicator(),
// ),
// const SizedBox(height: 16),
// Text(
// 'Drawing route on map...'.tr,
// style: const TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 14,
// color: AppColor.primaryColor,
// ),
// textAlign: TextAlign.center,
// ),
// ],
// ),
// ),
// ),
// barrierDismissible: false,
// );
// // Auto-dismiss after exactly 2 seconds
// Future.delayed(const Duration(seconds: 2), () {
// if (Get.isDialogOpen == true) {
// Get.back();
// }
// });
// });
// }
// String dynamicApiUrl = 'https://routec.intaleq.xyz/route';
// Future<void> getDistanceFromDriverAfterAcceptedRide(
// String origin, String destination) async {
// String apiKey = Env.mapKeyOsm; // مفتاح API الخاص بك
// if (origin.isEmpty) {
// origin = '${passengerLocation.latitude},${passengerLocation.longitude}';
// }
// // 2. بناء الرابط (URI)
// // Waypoints غير مدعومة حالياً في OSRM، لذلك تم تجاهلها
// var uri = Uri.parse(
// '$dynamicApiUrl?origin=$origin&destination=$destination&steps=false&overview=false');
// Log.print('uri: $uri');
// // 3. إرسال الطلب مع الهيدر
// http.Response response;
// Map<String, dynamic> responseData;
// try {
// response = await http.get(
// uri,
// headers: {
// 'X-API-KEY': apiKey,
// },
// ).timeout(const Duration(seconds: 20)); // تايم آوت 20 ثانية
// if (response.statusCode != 200) {
// Log.print('Error from API: ${response.statusCode}');
// isLoading = false;
// update();
// return; // خروج في حالة الخطأ
// }
// if (Get.isBottomSheetOpen ?? false) {
// Get.back(); // لإغلاق شاشة "جاري الرسم"
// }
// isDrawingRoute = false; // Reset state
// responseData = json.decode(response.body);
// Log.print('responseData: $responseData');
// if (responseData['status'] != 'ok') {
// Log.print('API returned an error: ${responseData['message']}');
// isLoading = false;
// update();
// return; // خروج في حالة خطأ منطقي (مثل "no path")
// }
// } catch (e) {
// Log.print('Failed to get directions: $e');
// isLoading = false;
// update();
// return; // خروج عند فشل الاتصال
// }
// }
// // (b = 1.5348) هو المعامل الذي تم حسابه من مقارنة 60 رحلة بين Google و OSRM
// double kDurationScalar =
// 1.5348; //this from colab 60 random locations from google and routec
// // -----------------------------------------------------------------------------------------
// // GET DIRECTION MAP (FULL)
// // -----------------------------------------------------------------------------------------
// // -----------------------------------------------------------------------------------------
// // GET DIRECTION MAP (With Auto-Retry Logic)
// // -----------------------------------------------------------------------------------------
// // أضفنا attemptCount لتتبع عدد المحاولات
// // -----------------------------------------------------------------------------------------
// // GET DIRECTION MAP (Retry or Fail Strict Logic)
// // -----------------------------------------------------------------------------------------
// Future<void> getDirectionMap(String origin, String destination,
// [List<String> waypoints = const [], int attemptCount = 0]) async {
// // 1. إظهار التحميل فقط في المحاولة الأولى
// if (attemptCount == 0) {
// // NOTE: Do NOT set isLoading = true here!
// // isLoading destroys the MapLibreMap widget entirely (replaced by spinner),
// // which means markers/polylines cannot be added to the new map instance
// // until its style finishes loading asynchronously — causing a race condition.
// // The showDrawingBottomSheet() overlay provides sufficient user feedback.
// isDrawingRoute = true;
// update();
// if (isDrawingRoute) showDrawingBottomSheet();
// await getCarsLocationByPassengerAndReloadMarker();
// }
// // تجهيز الإحداثيات
// if (origin.isEmpty) {
// origin = '${passengerLocation.latitude},${passengerLocation.longitude}';
// }
// var coordDestination = destination.split(',');
// double latDest = double.parse(coordDestination[0]);
// double lngDest = double.parse(coordDestination[1]);
// myDestination = LatLng(latDest, lngDest);
// // ── 2. Unified SaaS Routing Strategy ──────────────────────────
// final bool isSaaSRequest = true;
// Uri uri;
// var originCoords = origin.split(',');
// final Map<String, String> queryParams = {
// 'fromLat': originCoords[0].trim(),
// 'fromLng': originCoords[1].trim(),
// 'toLat': latDest.toString(),
// 'toLng': lngDest.toString(),
// };
// // Add multi-stop waypoints to the query parameters
// for (int i = 0; i < activeMenuWaypointCount; i++) {
// final wp = menuWaypoints[i];
// if (wp != null) {
// queryParams['stop${i + 1}Lat'] = wp.latitude.toString();
// queryParams['stop${i + 1}Lng'] = wp.longitude.toString();
// }
// }
// uri = Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams);
// Log.print(
// 'Requesting Route URI (${isSaaSRequest ? "SaaS" : "OSRM"}, Attempt: ${attemptCount + 1}): $uri');
// http.Response response;
// Map<String, dynamic> responseData;
// try {
// response = await http.get(uri, headers: {
// 'x-api-key': Env.mapSaasKey,
// }).timeout(const Duration(seconds: 20));
// responseData = json.decode(response.body);
// // Validation: SaaS returns 200 with data, OSRM returns code: 'Ok'
// bool isRequestValid = response.statusCode == 200 &&
// (isSaaSRequest || responseData['code'] == 'Ok');
// if (!isRequestValid) {
// if (attemptCount < 2) {
// await _retryProcess(origin, destination, waypoints, attemptCount);
// return;
// }
// _handleFatalError(
// "Server Error".tr, "Connection failed. Please try again.".tr);
// return;
// }
// // ============================================================
// // 🛑 الفحص الأمني (Sanity Check)
// // ============================================================
// double apiDistanceMeters;
// String pointsString;
// dynamic routeData;
// // SaaS parsing
// apiDistanceMeters = (responseData['distance'] as num).toDouble();
// pointsString = responseData['points'] ?? "";
// routeData = responseData; // For box storage
// var originCoords = origin.split(',');
// double startLat = double.parse(originCoords[0]);
// double startLng = double.parse(originCoords[1]);
// // المسافة الجوية
// double aerialDistance =
// Geolocator.distanceBetween(startLat, startLng, latDest, lngDest);
// if (apiDistanceMeters < 50.0 && aerialDistance > 200.0) {
// Log.print(
// "⚠️ Suspicious Route detected! Server: $apiDistanceMeters m | Aerial: $aerialDistance m");
// if (attemptCount < 2) {
// Log.print("🔄 Retrying request (Attempt ${attemptCount + 2})...");
// await Future.delayed(const Duration(seconds: 1));
// await getDirectionMap(
// origin, destination, waypoints, attemptCount + 1);
// return;
// } else {
// Log.print("❌ All retries failed. Calculating Route is impossible.");
// _handleFatalError(
// "Route Not Found".tr,
// "We couldn't find a valid route to this destination. Please try selecting a different point."
// .tr);
// return;
// }
// }
// // 3. معالجة البيانات
// box.remove(BoxName.tripData);
// box.write(BoxName.tripData, routeData);
// durationToRide =
// ((routeData['duration'] as num) * kDurationScalar).toInt();
// double distanceOfTrip = apiDistanceMeters / 1000.0;
// distance = distanceOfTrip;
// data = routeData['legs'] != null && routeData['legs'].isNotEmpty
// ? (routeData['legs'][0]['steps'] ?? [])
// : [];
// List<LatLng> decodedPoints = [];
// if (pointsString.isNotEmpty) {
// decodedPoints = await compute(decodePolylineIsolate, pointsString);
// }
// if (decodedPoints.isEmpty) {
// _handleFatalError("Map Error".tr, "Received empty route data.".tr);
// return;
// }
// polylineCoordinates.clear();
// polylineCoordinates.addAll(decodedPoints);
// final LatLng startLoc = polylineCoordinates.first;
// final LatLng endLoc = polylineCoordinates.last;
// // ── 4. العناوين والتحديثات ──────────────────────────────────
// startNameAddress = responseData['startName'] ?? 'Start Point'.tr;
// endNameAddress = responseData['endName'] ?? 'Destination'.tr;
// Log.print('📍 ROUTE START: $startNameAddress');
// Log.print('📍 ROUTE END: $endNameAddress');
// // ── 5. Bounds Calculation (SaaS bbox vs OSRM manual) ──────────
// if (isSaaSRequest && responseData['bbox'] != null) {
// List<dynamic> bbox = responseData['bbox'];
// if (bbox.length == 4) {
// // SaaS format: [minLng, minLat, maxLng, maxLat]
// lastComputedBounds = LatLngBounds(
// southwest: LatLng(bbox[1], bbox[0]),
// northeast: LatLng(bbox[3], bbox[2]),
// );
// }
// } else {
// double? minLat, maxLat, minLng, maxLng;
// for (LatLng point in polylineCoordinates) {
// minLat =
// minLat == null ? point.latitude : min(minLat, point.latitude);
// maxLat =
// maxLat == null ? point.latitude : max(maxLat, point.latitude);
// minLng =
// minLng == null ? point.longitude : min(minLng, point.longitude);
// maxLng =
// maxLng == null ? point.longitude : max(maxLng, point.longitude);
// }
// if (minLat != null) {
// lastComputedBounds = LatLngBounds(
// northeast: LatLng(maxLat!, maxLng!),
// southwest: LatLng(minLat!, minLng!));
// }
// }
// // isDrawingRoute = false;
// // 5b. Reset state when finished
// if (isDrawingRoute) {
// Log.print('🔔 Finalizing route drawing state');
// isDrawingRoute = false;
// isLoading = false;
// update();
// }
// // 6. إضافة الماركرز
// durationToAdd = Duration(seconds: durationToRide);
// hours = durationToAdd.inHours;
// minutes = (durationToAdd.inMinutes % 60).round();
// markers = {
// Marker(
// markerId: const MarkerId('start'),
// position: startLoc,
// icon: InlqBitmap.fromStyleImage('orange_marker'),
// infoWindow: const InfoWindow(title: 'A'),
// anchor: const Offset(0.5, 1.0),
// ),
// Marker(
// markerId: const MarkerId('end'),
// position: endLoc,
// icon: InlqBitmap.fromStyleImage('violet_marker'),
// infoWindow: const InfoWindow(title: 'B'),
// anchor: const Offset(0.5, 1.0),
// ),
// };
// for (int i = 0; i < activeMenuWaypointCount; i++) {
// final wp = menuWaypoints[i];
// if (wp != null) {
// final bool isFirstWaypoint = i == 0;
// markers.add(Marker(
// markerId: MarkerId('waypoint_$i'),
// position: wp,
// icon: InlqBitmap.fromStyleImage(
// isFirstWaypoint ? 'orange_marker' : 'violet_marker'),
// infoWindow:
// InfoWindow(title: isFirstWaypoint ? 'Stop 1' : 'Stop 2'),
// anchor: const Offset(0.5, 1.0),
// ));
// }
// }
// // 7. رسم الخط
// if (polyLines.isNotEmpty) clearPolyline();
// rideConfirm = false;
// isMarkersShown = true;
// update(); // تحديث أولي لإظهار الخريطة والماركرز
// // إظهار الباتم شيت للسعر
// await bottomSheet();
// // تشغيل الأنيميشن الخفيف لومضات المسار + fit camera after
// await _playRouteAnimation(polylineCoordinates, lastComputedBounds);
// } catch (e, stackTrace) {
// // 🚨 Cleanup on error to prevent UI freeze
// if (isDrawingRoute) {
// isDrawingRoute = false;
// isLoading = false;
// update();
// }
// Log.print('🚨 CRITICAL ERROR IN getDirectionMap: $e');
// Log.print('🚨 STACKTRACE: $stackTrace');
// if (attemptCount < 2) {
// await _retryProcess(origin, destination, waypoints, attemptCount);
// } else {
// _handleFatalError("Connection Error".tr,
// "Please check your internet and try again.".tr);
// }
// }
// }
// // --- رسم المسار النهائي مع تقسيم ملون حسب نقاط التوقف ---
// Future<void> _playRouteAnimation(
// List<LatLng> coords, LatLngBounds? bounds) async {
// // Segment colors matching UI dots: green → amber → purple → red
// const List<Color> segmentColors = [
// Color(0xFF109642), // Green (start → stop 1)
// Color(0xFFF59E0B), // Amber (stop 1 → stop 2)
// Color(0xFF7C3AED), // Purple (last segment → dest)
// Color(0xFFEF4444), // Red (fallback)
// ];
// // ── Build final polyline segments ───────────────────────────────────
// // Build all segments in a temporary Set first, then assign once
// Set<Polyline> newPolylines = {};
// if (activeMenuWaypointCount > 0) {
// List<int> splitIndices = [];
// for (int w = 0; w < activeMenuWaypointCount; w++) {
// final wp = menuWaypoints[w];
// if (wp == null) continue;
// int bestIdx = 0;
// double bestDist = double.infinity;
// for (int j = 0; j < coords.length; j++) {
// final dx = coords[j].latitude - wp.latitude;
// final dy = coords[j].longitude - wp.longitude;
// final d = dx * dx + dy * dy;
// if (d < bestDist) {
// bestDist = d;
// bestIdx = j;
// }
// }
// splitIndices.add(bestIdx);
// }
// splitIndices.sort();
// List<int> boundaries = [0, ...splitIndices, coords.length - 1];
// for (int s = 0; s < boundaries.length - 1; s++) {
// int from = boundaries[s];
// int to = boundaries[s + 1] + 1;
// if (to > coords.length) to = coords.length;
// if (from >= to - 1) continue;
// final segCoords = coords.sublist(from, to);
// if (segCoords.length < 2) continue;
// final color = segmentColors[s % segmentColors.length];
// newPolylines.add(Polyline(
// polylineId: PolylineId('segment_$s'),
// points: segCoords,
// color: color,
// width: 6,
// ));
// }
// } else {
// newPolylines.add(Polyline(
// polylineId: const PolylineId('route_primary'),
// points: coords,
// color: AppColor.primaryColor,
// width: 6,
// ));
// }
// polyLines = newPolylines;
// update();
// Log.print(
// '🗺️ Drawing ${markers.length} markers + ${polyLines.length} polylines on map');
// update();
// // ── Fit camera to full route bounds ────────────────────────────────
// if (bounds != null) {
// await _safeAnimateCameraBounds(bounds);
// }
// }
// // --- دالة المساعدة لإعادة المحاولة ---
// Future<void> _retryProcess(String origin, String dest, List<String> waypoints,
// int currentAttempt) async {
// Log.print(
// "🔄 Exception or Error caught. Retrying in 1s... (Attempt ${currentAttempt + 1})");
// await Future.delayed(const Duration(seconds: 1));
// getDirectionMap(origin, dest, waypoints, currentAttempt + 1);
// }
// // --- دالة جديدة لتنظيف الخريطة بالكامل ومنع تداخل الرحلات ---
// void resetAllMapStates() {
// Log.print('🧹 Resetting all map states to prevent sticky location bug');
// clearPlacesDestination();
// clearPlacesStart();
// clearPolyline();
// data = [];
// passengerStartLocationFromMap = false;
// startLocationFromMap = false;
// isPickerShown = false;
// workLocationFromMap = false;
// homeLocationFromMap = false;
// isAnotherOreder = false;
// isWhatsAppOrder = false;
// // ✅ أضف هذا: reset الوجهة لموقع الراكب حتى لا تبقى قيمة الرحلة القديمة
// myDestination = passengerLocation;
// hintTextDestinationPoint = 'Select your destination'.tr;
// placeDestinationController.clear();
// placeStartController.clear();
// rideConfirm = false;
// shouldFetch = false;
// isDrawingRoute = false;
// isLoading = false;
// update();
// }
// // -----------------------------------------------------------------------------------------
// // 🛑 دالة الخطأ القاتل (تغلق كل شيء وتعيد المستخدم للخريطة)
// // -----------------------------------------------------------------------------------------
// void _handleFatalError(String title, String message) {
// // 1. إغلاق شاشة التحميل (Drawing route...)
// if (Get.isBottomSheetOpen == true || Get.isDialogOpen == true) {
// Get.back();
// }
// if (Get.isSnackbarOpen) Get.closeCurrentSnackbar();
// // 2. تصفير المتغيرات
// isDrawingRoute = false;
// isLoading = false;
// update();
// // 3. إظهار الديالوج الإجباري
// Get.defaultDialog(
// title: title,
// titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
// middleText: message,
// middleTextStyle: AppStyle.subtitle,
// barrierDismissible: false, // لا يمكن إغلاقه بالضغط خارجاً
// confirm: MyElevatedButton(
// title: "Close".tr,
// kolor: AppColor.redColor,
// onPressed: () {
// Get.back(); // إغلاق الديالوج
// // 4. إعادة تحميل الصفحة بالكامل (تنظيف الحالة)
// // تأكد من استيراد MapPagePassenger
// Get.offAll(() => const MapPagePassenger());
// },
// ),
// );
// }
// // Legacy gradient and layered animations removed for MapLibre migration
// String shortenAddress(String fullAddress) {
// // Split the address into parts
// List<String> parts = fullAddress.split('،');
// // Remove any leading or trailing whitespace from each part
// parts = parts.map((part) => part.trim()).toList();
// // Remove any empty parts
// parts = parts.where((part) => part.isNotEmpty).toList();
// // Initialize the short address
// String shortAddress = '';
// if (parts.isNotEmpty) {
// // Add the first part (usually the most specific location)
// shortAddress += parts[0];
// }
// if (parts.length > 2) {
// // Add the district or area name (usually the third part in Arabic format)
// shortAddress += '، ${parts[2]}';
// } else if (parts.length > 1) {
// // Add the second part for English or shorter addresses
// shortAddress += '، ${parts[1]}';
// }
// // Add the country (usually the last part)
// if (parts.length > 1) {
// shortAddress += '، ${parts.last}';
// }
// // Remove any part that's just numbers (like postal codes)
// shortAddress = shortAddress
// .split('،')
// .where((part) => !RegExp(r'^[0-9 ]+$').hasMatch(part.trim()))
// .join('،');
// // Check if the address is in English
// bool isEnglish =
// RegExp(r'^[a-zA-Z0-9 ]+$').hasMatch(shortAddress.replaceAll('،', ''));
// if (isEnglish) {
// // Further processing for English addresses
// List<String> englishParts = shortAddress.split('،');
// if (englishParts.length > 2) {
// shortAddress =
// '${englishParts[0]}، ${englishParts[1]}، ${englishParts.last}';
// } else if (englishParts.length > 1) {
// shortAddress = '${englishParts[0]}، ${englishParts.last}';
// }
// }
// return shortAddress;
// }
// double distanceOfDestination = 0;
// bool haveSteps = false;
// late LatLng latestPosition;
// getMapPoints(String originSteps, String destinationSteps, int index) async {
// isWayPointStopsSheetUtilGetMap = false;
// // haveSteps = true;
// // startCarLocationSearch(box.read(BoxName.carType));
// await getCarsLocationByPassengerAndReloadMarker();
// // await getCarsLocationByPassengerAndReloadMarker();
// // isLoading = true;
// update();
// var url =
// ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destinationSteps&origin=$originSteps&key=${AK.mapAPIKEY}');
// var response = await CRUD().getGoogleApi(link: url, payload: {});
// data = response['routes'][0]['legs'];
// // isLoading = false;
// int durationToRide0 = data[0]['duration']['value'];
// durationToRide = durationToRide + durationToRide0;
// distance = distanceOfDestination + (data[0]['distance']['value']) / 1000;
// update();
// // final points =
// // decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
// final String pointsString =
// response['routes'][0]["overview_polyline"]["points"];
// List<LatLng> decodedPoints =
// await compute(decodePolylineIsolate, pointsString);
// // decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
// for (int i = 0; i < decodedPoints.length; i++) {
// polylineCoordinates.add(decodedPoints[i]);
// }
// // Define the northeast and southwest coordinates
// if (polyLines.isEmpty) {
// var polyline = Polyline(
// polylineId: PolylineId('route_$index'),
// points: polylineCoordinatesPointsAll[index],
// width: 6,
// color: const Color(0xFF2196F3),
// );
// polyLines = {...polyLines, polyline};
// rideConfirm = false;
// update();
// }
// }
// void updateCameraForDistanceAfterGetMap() {
// LatLng coord1 = LatLng(
// double.parse(coordinatesWithoutEmpty.first.split(',')[0]),
// double.parse(coordinatesWithoutEmpty.first.split(',')[1]));
// LatLng coord2 = LatLng(
// double.parse(coordinatesWithoutEmpty.last.split(',')[0]),
// double.parse(coordinatesWithoutEmpty.last.split(',')[1]));
// LatLng northeast;
// LatLng southwest;
// if (coord1.latitude > coord2.latitude) {
// northeast = coord1;
// southwest = coord2;
// } else {
// northeast = coord2;
// southwest = coord1;
// }
// // Create the LatLngBounds object
// LatLngBounds bounds =
// LatLngBounds(northeast: northeast, southwest: southwest);
// // Fit the camera to the bounds
// var cameraUpdate = CameraUpdate.newLatLngBounds(bounds,
// left: 180, top: 180, right: 180, bottom: 180);
// mapController!.animateCamera(cameraUpdate);
// update();
// }
// int selectedIndex = -1; // Initialize with no selection
// void selectCarFromList(int index) {
// selectedIndex = index; // Update selected index
// carTypes.forEach(
// (element) => element.isSelected = false); // Reset selection flags
// carTypes[index].isSelected = true;
// update();
// }
// showBottomSheet1() async {
// await bottomSheet();
// isBottomSheetShown = true;
// heightBottomSheetShown = 250;
// update();
// }
// final promo = TextEditingController();
// bool promoTaken = false;
// void applyPromoCodeToPassenger(BuildContext context) async {
// if (promoTaken == true) {
// MyDialog().getDialog(
// 'Promo Already Used'.tr,
// 'You have already used this promo code.'.tr,
// () => Get.back(),
// );
// return;
// }
// if (!promoFormKey.currentState!.validate()) return;
// // العتبات بالليرة السورية
// const double minPromoLowSYP = 172; // Speed / Balash
// const double minPromoHighSYP = 200; // Comfort / Electric / Lady
// try {
// final value = await CRUD().get(
// link: AppLink.getPassengersPromo,
// payload: {'promo_code': promo.text},
// );
// if (value == 'failure') {
// MyDialog().getDialog(
// 'Promo Ended'.tr,
// 'The promotion period has ended.'.tr,
// () => Get.back(),
// );
// return;
// }
// // هل يوجد فئة مؤهلة أصلاً قبل الخصم؟
// final bool eligibleNow = (totalPassengerSpeed >= minPromoLowSYP) ||
// (totalPassengerBalash >= minPromoLowSYP) ||
// (totalPassengerComfort >= minPromoHighSYP) ||
// (totalPassengerElectric >= minPromoHighSYP) ||
// (totalPassengerLady >= minPromoHighSYP);
// if (!eligibleNow) {
// Get.snackbar(
// 'Lowest Price Achieved'.tr,
// 'Cannot apply further discounts.'.tr,
// backgroundColor: AppColor.yellowColor,
// );
// return;
// }
// final decode = jsonDecode(value);
// if (decode["status"] != "success") {
// MyDialog().getDialog(
// 'Promo Ended'.tr,
// 'The promotion period has ended.'.tr,
// () => Get.back(),
// );
// return;
// }
// Get.snackbar('Promo Code Accepted'.tr, '',
// backgroundColor: AppColor.greenColor);
// final firstElement = decode["message"][0];
// final int discountPercentage =
// int.tryParse(firstElement['amount'].toString()) ?? 0;
// // قيمة المحفظة - قد تكون سالبة
// final double walletVal = double.tryParse(
// box.read(BoxName.passengerWalletTotal)?.toString() ?? '0') ??
// 0.0;
// final bool isWalletNegative = walletVal < 0;
// // --------------------------
// // دالة تُطبّق الخصم دون النزول تحت الحد الأدنى
// // --------------------------
// double _applyDiscountPerTier({
// required double fare,
// required double minThreshold,
// required bool isWalletNegative,
// }) {
// if (fare < minThreshold) return fare; // غير مؤهل أصلاً
// final double discount = fare * (discountPercentage / 100.0);
// double result;
// if (isWalletNegative) {
// double neg = (-1) * walletVal; // walletVal < 0 => neg positive
// result = fare + neg - discount;
// } else {
// result = fare - discount;
// }
// // لا نسمح بالنزول دون الحد الأدنى
// if (result < minThreshold) {
// result = minThreshold;
// }
// // ولا نسمح بمبلغ سالب
// return result.clamp(0.0, double.infinity);
// }
// // Comfort
// totalPassengerComfort = _applyDiscountPerTier(
// fare: totalPassengerComfort,
// minThreshold: minPromoHighSYP,
// isWalletNegative: isWalletNegative,
// );
// // Electric
// totalPassengerElectric = _applyDiscountPerTier(
// fare: totalPassengerElectric,
// minThreshold: minPromoHighSYP,
// isWalletNegative: isWalletNegative,
// );
// // Lady
// totalPassengerLady = _applyDiscountPerTier(
// fare: totalPassengerLady,
// minThreshold: minPromoHighSYP,
// isWalletNegative: isWalletNegative,
// );
// // Speed
// totalPassengerSpeed = _applyDiscountPerTier(
// fare: totalPassengerSpeed,
// minThreshold: minPromoLowSYP,
// isWalletNegative: isWalletNegative,
// );
// // Balash
// totalPassengerBalash = _applyDiscountPerTier(
// fare: totalPassengerBalash,
// minThreshold: minPromoLowSYP,
// isWalletNegative: isWalletNegative,
// );
// // تعديل دخل السائق وفق نسبة الخصم
// totalDriver = totalDriver - (totalDriver * discountPercentage / 100.0);
// promoTaken = true;
// update();
// // مؤثرات
// Confetti.launch(
// context,
// options: const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
// );
// Get.back();
// await Future.delayed(const Duration(milliseconds: 120));
// } catch (e) {
// Get.snackbar('Error'.tr, e.toString(),
// backgroundColor: AppColor.redColor);
// }
// }
// double getDistanceFromText(String distanceText) {
// // Remove any non-digit characters from the distance text
// String distanceValue = distanceText.replaceAll(RegExp(r'[^0-9.]+'), '');
// // Parse the extracted numerical value as a double
// double distance = double.parse(distanceValue);
// return distance;
// }
// double costForDriver = 0;
// double totalPassengerSpeed = 0;
// double totalPassengerBalash = 0;
// double totalPassengerElectric = 0;
// double totalPassengerLady = 0;
// double totalPassengerRayehGai = 0;
// double totalPassengerRayehGaiComfort = 0;
// double totalPassengerRayehGaiBalash = 0;
// Future bottomSheet() async {
// // if (data.isEmpty) return;
// // === إعدادات عامة ===
// const double minFareSYP = 160; // حد أدنى
// const double minBillableKm = 0.3; // حد أدنى للمسافة المفوترة
// const double ladyFlatAddon = 20; // إضافة ثابتة لـ Lady
// const double airportAddonSYP = 200; // إضافة المطار
// // --- ⬇️ الإضافة الجديدة: إضافة حدود مطار دمشق ⬇️ ---
// const double damascusAirportBoundAddon = 1400; // إضافة المطار (حدود)
// // --- ⬆️ نهاية الإضافة ⬆️ ---
// // كهرباء
// const double electricPerKmUplift = 4; // زيادة/كم
// const double electricFlatAddon = 10; // زيادة ثابتة
// // Long Speed
// const double longSpeedThresholdKm = 40.0;
// const double longSpeedPerKm = 26.0; // Speed عند >40كم
// // قواعد الرحلات البعيدة للدقائق (تعمل لكل الأوقات)
// const double mediumDistThresholdKm = 25.0; // >25كم
// const double longDistThresholdKm = 35.0; // >35كم
// const double longTripPerMin = 6.0;
// const int minuteCapMedium = 60; // سقف دقائق عند >25كم
// const int minuteCapLong = 80; // سقف دقائق عند >35كم
// const int freeMinutesLong = 10; // عفو 10 دقائق عند >35كم
// // تخفيضات المسافات الكبيرة للفئات غير Speed
// const double extraReduction100 = 0.07; // +7% فوق تخفيض >40كم للرحلات >100كم
// const double maxReductionCap = 0.35; // سقف 35% كحد أقصى
// // ====== زمن الرحلة ======
// durationToAdd = Duration(seconds: durationToRide);
// hours = durationToAdd.inHours;
// minutes = (durationToAdd.inMinutes % 60).round();
// final DateTime currentTime = DateTime.now();
// newTime = currentTime.add(durationToAdd);
// averageDuration = (durationToRide / 60) / distance;
// // +5 minutes per waypoint stop surcharge
// final int waypointSurchargeMinutes = activeMenuWaypointCount * 5;
// final int totalMinutes =
// (durationToRide / 60).floor() + waypointSurchargeMinutes;
// // ====== أدوات مساعدة ======
// bool _isAirport(String s) {
// final t = s.toLowerCase();
// return t.contains('airport') ||
// s.contains('مطار') ||
// s.contains('المطار');
// }
// bool _isClub(String s) {
// final t = s.toLowerCase();
// return t.contains('club') ||
// t.contains('nightclub') ||
// t.contains('night club') ||
// s.contains('ديسكو') ||
// s.contains('ملهى ليلي');
// }
// // --- ⬇️ الإضافة الجديدة: دالة التحقق من حدود المطار ⬇️ ---
// // (P1: 33.415313, 36.499687) (P2: 33.400265, 36.531505)
// bool _isInsideDamascusAirportBounds(double lat, double lng) {
// final double northLat = 33.415313;
// final double southLat = 33.400265;
// final double eastLng = 36.531505;
// final double westLng = 36.499687;
// // التحقق من خط العرض (بين الشمال والجنوب)
// bool isLatInside = (lat <= northLat) && (lat >= southLat);
// // التحقق من خط الطول (بين الشرق والغرب)
// bool isLngInside = (lng <= eastLng) && (lng >= westLng);
// return isLatInside && isLngInside;
// }
// // --- ⬆️ نهاية الإضافة ⬆️ ---
// // أسعار الدقيقة من السيرفر
// final double naturePerMin = naturePrice; // طبيعي
// final double latePerMin = latePrice; // ليل
// final double heavyPerMin = heavyPrice; // ذروة
// // سعر الدقيقة حسب الوقت (أساس قبل قواعد المسافة)
// double _perMinuteByTime(DateTime now, bool clubCtx) {
// final h = now.hour;
// if (h >= 21 || h < 1) return latePerMin; // ليل
// if (h >= 1 && h < 5) return clubCtx ? (latePerMin * 2) : latePerMin;
// if (h >= 14 && h <= 17) return heavyPerMin; // ذروة
// return naturePerMin; // طبيعي
// }
// // حد أدنى
// double _applyMinFare(double fare) =>
// (fare < minFareSYP) ? minFareSYP : fare;
// // عمولة الراكب (kazan من السيرفر)
// double _withCommission(double base) =>
// (base * (1 + kazan / 100)).ceilToDouble();
// // ====== سياق ======
// final bool airportCtx =
// _isAirport(startNameAddress) || _isAirport(endNameAddress);
// final bool clubCtx = _isClub(startNameAddress) || _isClub(endNameAddress);
// // --- ⬇️ الإضافة الجديدة: التحقق من سياق حدود المطار ⬇️ ---
// // !! ⚠️ تأكد من أن هذه هي المتغيرات الصحيحة لإحداثيات نقطة النهاية !!
// double destLat = 0.0;
// double destLng = 0.0;
// try {
// destLat = myDestination.latitude;
// destLng = myDestination.longitude;
// } catch (_) {
// if (coordinatesWithoutEmpty.isNotEmpty) {
// destLat =
// double.tryParse(coordinatesWithoutEmpty.last.split(',')[0]) ?? 0.0;
// destLng =
// double.tryParse(coordinatesWithoutEmpty.last.split(',')[1]) ?? 0.0;
// }
// }
// final bool damascusAirportBoundCtx = _isInsideDamascusAirportBounds(
// destLat,
// destLng,
// );
// final bool isInDamascusAirportBoundCtx = _isInsideDamascusAirportBounds(
// newMyLocation.latitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح
// newMyLocation.longitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح
// );
// // --- ⬆️ نهاية الإضافة ⬆️ ---
// // ====== مسافة مفوترة ======
// final double billableDistance =
// (distance < minBillableKm) ? minBillableKm : distance;
// // ====== Speed (قصير/طويل) ======
// final bool isLongSpeed = billableDistance > longSpeedThresholdKm;
// final double perKmSpeedBaseFromServer =
// speedPrice; // مثال: 2900 يأتي من السيرفر
// final double perKmSpeed =
// isLongSpeed ? longSpeedPerKm : perKmSpeedBaseFromServer;
// // ====== تخفيضات الفئات الأخرى حسب بُعد الرحلة ======
// // ... (الكود كما هو) ...
// double reductionPct40 = 0.0;
// if (perKmSpeedBaseFromServer > 0) {
// reductionPct40 = (1.0 - (longSpeedPerKm / perKmSpeedBaseFromServer))
// .clamp(0.0, maxReductionCap);
// }
// final double reductionPct100 =
// (reductionPct40 + extraReduction100).clamp(0.0, maxReductionCap);
// double distanceReduction = 0.0;
// if (billableDistance > 100.0) {
// distanceReduction = reductionPct100;
// } else if (billableDistance > 40.0) {
// distanceReduction = reductionPct40;
// }
// // ====== منطق الدقيقة يعمل لكل الأوقات ويتكيّف مع المسافة ======
// // ... (الكود كما هو) ...
// double effectivePerMin = _perMinuteByTime(currentTime, clubCtx);
// int billableMinutes = totalMinutes;
// if (billableDistance > longDistThresholdKm) {
// effectivePerMin = longTripPerMin;
// final int capped =
// (billableMinutes > minuteCapLong) ? minuteCapLong : billableMinutes;
// billableMinutes = capped - freeMinutesLong;
// if (billableMinutes < 0) billableMinutes = 0;
// } else if (billableDistance > mediumDistThresholdKm) {
// effectivePerMin = longTripPerMin;
// billableMinutes = (billableMinutes > minuteCapMedium)
// ? minuteCapMedium
// : billableMinutes;
// }
// // ====== أسعار/كم قبل التخفيض ======
// // ... (الكود كما هو) ...
// final double perKmComfortRaw = comfortPrice;
// final double perKmDelivery = deliveryPrice;
// final double perKmVanRaw =
// (familyPrice > 0 ? familyPrice : (speedPrice + 13));
// final double perKmElectricRaw = perKmComfortRaw + electricPerKmUplift;
// // ====== تطبيق التخفيضات على الفئات (نفس نسبة Speed للبعيد) ======
// // ... (الكود كما هو) ...
// double perKmComfort = perKmComfortRaw * (1.0 - distanceReduction);
// double perKmElectric = perKmElectricRaw * (1.0 - distanceReduction);
// double perKmVan = perKmVanRaw * (1.0 - distanceReduction);
// perKmComfort = perKmComfort.clamp(0, double.infinity);
// perKmElectric = perKmElectric.clamp(0, double.infinity);
// perKmVan = perKmVan.clamp(0, double.infinity);
// final double perKmBalash = (perKmSpeed - 5).clamp(0, double.infinity);
// // ====== دوال الاحتساب ======
// double _oneWayFare({
// required double perKm,
// required bool isLady,
// double flatAddon = 0,
// }) {
// double fare = billableDistance * perKm;
// fare +=
// billableMinutes * effectivePerMin; // دقائق بعد السقف/العفو إن وُجد
// fare += flatAddon;
// if (isLady) fare += ladyFlatAddon;
// if (airportCtx) fare += airportAddonSYP;
// // --- ⬇️ الإضافة الجديدة: تطبيق إضافة حدود المطار ⬇️ ---
// if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) {
// fare += damascusAirportBoundAddon;
// }
// // --- ⬆️ نهاية الإضافة ⬆️ ---
// return _applyMinFare(fare);
// }
// double _roundTripFare({required double perKm}) {
// // خصم 40% لمسافة إياب واحدة + زمن مضاعف (بنفس قواعد الدقيقة المعدّلة)
// double distPart =
// (billableDistance * 2 * perKm) - ((billableDistance * perKm) * 0.4);
// double timePart = (billableMinutes * 2) * effectivePerMin;
// double fare = distPart + timePart;
// if (airportCtx) fare += airportAddonSYP;
// // --- ⬇️ الإضافة الجديدة: تطبيق إضافة حدود المطار ⬇️ ---
// // تنطبق أيضاً على رحلات الذهاب والعودة لأنها "تصل" إلى الوجهة
// if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) {
// fare += damascusAirportBoundAddon;
// }
// // --- ⬆️ نهاية الإضافة ⬆️ ---
// return _applyMinFare(fare);
// }
// // ====== حساب كل الفئات (Base قبل العمولة) ======
// final double costSpeed = _oneWayFare(perKm: perKmSpeed, isLady: false);
// final double costBalash = _oneWayFare(perKm: perKmBalash, isLady: false);
// final double costComfort = _oneWayFare(perKm: perKmComfort, isLady: false);
// final double costElectric = _oneWayFare(
// perKm: perKmElectric, isLady: false, flatAddon: electricFlatAddon);
// final double costDelivery =
// _oneWayFare(perKm: perKmDelivery, isLady: false);
// final double costLady = _oneWayFare(
// perKm: perKmComfort,
// isLady: true); // Lady تعتمد Comfort بعد التخفيض + إضافة ثابتة
// final double costVan = _oneWayFare(perKm: perKmVan, isLady: false);
// final double costRayehGai = _roundTripFare(perKm: perKmSpeed);
// final double costRayehGaiComfort = _roundTripFare(perKm: perKmComfort);
// final double costRayehGaiBalash = _roundTripFare(perKm: perKmBalash);
// // ====== أسعار الراكب بعد العمولة (kazan من السيرفر) ======
// totalPassengerSpeed = _withCommission(costSpeed);
// totalPassengerBalash = _withCommission(costBalash);
// totalPassengerComfort = _withCommission(costComfort);
// totalPassengerElectric = _withCommission(costElectric);
// totalPassengerLady = _withCommission(costLady);
// totalPassengerScooter = _withCommission(costDelivery);
// totalPassengerVan = _withCommission(costVan);
// totalPassengerRayehGai = _withCommission(costRayehGai);
// totalPassengerRayehGaiComfort = _withCommission(costRayehGaiComfort);
// totalPassengerRayehGaiBalash = _withCommission(costRayehGaiBalash);
// // افتراضي للعرض
// totalPassenger = totalPassengerSpeed;
// totalCostPassenger = totalPassenger;
// // ====== دعم رصيد محفظة سلبي ======
// try {
// final walletStr = box.read(BoxName.passengerWalletTotal).toString();
// final walletVal = double.tryParse(walletStr) ?? 0.0;
// if (walletVal < 0) {
// final neg = (-1) * walletVal;
// totalPassenger += neg;
// totalPassengerComfort += neg;
// totalPassengerElectric += neg;
// totalPassengerLady += neg;
// totalPassengerBalash += neg;
// totalPassengerScooter += neg;
// totalPassengerRayehGai += neg;
// totalPassengerRayehGaiComfort += neg;
// totalPassengerRayehGaiBalash += neg;
// totalPassengerVan += neg;
// }
// } catch (e) {
// Log.print("Error: $e");
// }
// update();
// changeBottomSheetShown(forceValue: true);
// }
// List<LatLng> polylineCoordinate = [];
// String? cardNumber;
// void readyWayPoints() {
// hintTextwayPointStringAll = [
// hintTextwayPoint0,
// hintTextwayPoint1,
// hintTextwayPoint2,
// hintTextwayPoint3,
// hintTextwayPoint4,
// ];
// polylineCoordinatesPointsAll = [
// polylineCoordinates0,
// polylineCoordinates1,
// polylineCoordinates2,
// polylineCoordinates3,
// polylineCoordinates4,
// ];
// allTextEditingPlaces = [
// wayPoint0Controller,
// wayPoint1Controller,
// wayPoint2Controller,
// wayPoint3Controller,
// wayPoint4Controller,
// ];
// currentLocationToFormPlacesAll = [
// currentLocationToFormPlaces0,
// currentLocationToFormPlaces1,
// currentLocationToFormPlaces2,
// currentLocationToFormPlaces3,
// currentLocationToFormPlaces4,
// ];
// placeListResponseAll = [
// wayPoint0,
// wayPoint1,
// wayPoint2,
// wayPoint3,
// wayPoint4
// ];
// startLocationFromMapAll = [
// startLocationFromMap0,
// startLocationFromMap1,
// startLocationFromMap2,
// startLocationFromMap3,
// startLocationFromMap4,
// ];
// currentLocationStringAll = [
// currentLocationString0,
// currentLocationString1,
// currentLocationString2,
// currentLocationString3,
// currentLocationString4,
// ];
// placesCoordinate = [
// placesCoordinate0,
// placesCoordinate1,
// placesCoordinate2,
// placesCoordinate3,
// placesCoordinate4,
// ];
// update();
// }
// List driversForMishwari = [];
// Future selectDriverAndCarForMishwariTrip() async {
// // Calculate the bounds for 12km range
// double latitudeOffset = 0.1; // 20km range in latitude
// double longitudeOffset = 0.12; // 20km range in longitude
// // Calculate bounding box based on passenger's location
// double southwestLat = passengerLocation.latitude - latitudeOffset;
// double northeastLat = passengerLocation.latitude + latitudeOffset;
// double southwestLon = passengerLocation.longitude - longitudeOffset;
// double northeastLon = passengerLocation.longitude + longitudeOffset;
// // Create the payload with calculated bounds
// var payload = {
// 'southwestLat': southwestLat.toString(),
// 'northeastLat': northeastLat.toString(),
// 'southwestLon': southwestLon.toString(),
// 'northeastLon': northeastLon.toString(),
// };
// try {
// // Fetch data from the API
// var res = await CRUD().get(
// link: AppLink.selectDriverAndCarForMishwariTrip, payload: payload);
// if (res != 'failure') {
// // Check if response is valid JSON
// try {
// var d = jsonDecode(res);
// driversForMishwari = d['message'];
// Log.print('driversForMishwari: $driversForMishwari');
// update();
// } catch (e) {
// // Handle invalid JSON format
// Log.print("Error decoding JSON: $e");
// return 'Server returned invalid data. Please try again later.';
// }
// } else {
// return 'No driver available now, try again later. Thanks for using our app.'
// .tr;
// }
// } catch (e) {
// // Handle network or other exceptions
// Log.print("Error fetching data: $e");
// return 'There was an issue connecting to the server. Please try again later.'
// .tr;
// }
// }
// final Rx<DateTime> selectedDateTime = DateTime.now().obs;
// void updateDateTime(DateTime newDateTime) {
// selectedDateTime.value = newDateTime;
// }
// Future mishwariOption() async {
// isLoading = true;
// update();
// // add dialoug for select driver and car
// await selectDriverAndCarForMishwariTrip();
// Future.delayed(Duration.zero);
// isLoading = false;
// update();
// Get.to(() => CupertinoDriverListWidget());
// // changeCashConfirmPageShown();
// }
// var driverIdVip = '';
// Future<void> saveTripData(
// Map<String, dynamic> driver, DateTime tripDateTime) async {
// try {
// // Prepare trip data
// Map<String, dynamic> tripData = {
// 'id': driver['driver_id'].toString(), // Ensure the id is a string
// 'phone': driver['phone'],
// 'gender': driver['gender'],
// 'name': driver['NAME'],
// 'name_english': driver['name_english'],
// 'address': driver['address'],
// 'religion': driver['religion'] ?? 'UnKnown',
// 'age': driver['age'].toString(), // Convert age to String
// 'education': driver['education'] ?? 'UnKnown', //startlocationname
// 'license_type': driver['license_type'] ?? 'UnKnown',
// 'national_number': driver['national_number'] ?? 'UnKnown',
// 'car_plate': driver['car_plate'],
// 'make': driver['make'],
// 'model': driver['model'],
// 'year': driver['year'].toString(), // Convert year to String
// 'color': driver['color'],
// 'color_hex': driver['color_hex'],
// 'displacement': driver['displacement'],
// 'fuel': driver['fuel'],
// 'token': driver['token'],
// 'rating': driver['rating'].toString(), // Convert rating to String
// 'countRide':
// driver['ride_count'].toString(), // Convert countRide to String
// 'passengerId': box.read(BoxName.passengerID),
// 'timeSelected': tripDateTime.toIso8601String(),
// 'status': 'pending',
// 'startNameAddress': startNameAddress.toString(),
// 'locationCoordinate':
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// };
// Log.print('tripData: $tripData');
// // Send data to server
// var response =
// await CRUD().post(link: AppLink.addMishwari, payload: tripData);
// // Log.print('response: $response');
// if (response != 'failure') {
// // Trip saved successfully
// // Get.snackbar('Success'.tr, 'Trip booked successfully'.tr);
// var id = response['message']['id'].toString();
// await CRUD()
// .post(link: '${AppLink.server}/ride/rides/add.php', payload: {
// "start_location":
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// "end_location":
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// "date": DateTime.now().toString(),
// "time": DateTime.now().toString(),
// "endtime": DateTime.now().add(const Duration(hours: 2)).toString(),
// "price": '50',
// "passenger_id": box.read(BoxName.passengerID).toString(),
// "driver_id": driver['driver_id'].toString(),
// "status": "waiting",
// 'carType': 'vip',
// "price_for_driver": '50',
// "price_for_passenger": '50',
// "distance": '20',
// "paymentMethod": 'cash',
// }).then((value) {
// if (value is String) {
// final parsedValue = jsonDecode(value);
// rideId = parsedValue['message'];
// } else if (value is Map) {
// rideId = value['message'];
// } else {
// Log.print('Unexpected response type: ${value.runtimeType}');
// }
// });
// driverIdVip = driver['driver_id'].toString();
// driverId = driver['driver_id'].toString();
// DateTime timeSelected = DateTime.parse(tripDateTime.toIso8601String());
// Get.find<NotificationController>().scheduleNotificationsForTimeSelected(
// "Your trip is scheduled".tr,
// "Don't forget your ride!".tr,
// "tone1",
// timeSelected);
// await NotificationService.sendNotification(
// category: 'OrderVIP',
// target: driver['token'].toString(),
// title: 'OrderVIP'.tr,
// body: '$rideId - VIP Trip',
// isTopic: false, // Important: this is a token
// tone: 'tone1',
// driverList: [
// id,
// rideId,
// driver['id'],
// passengerLocation.latitude.toString(),
// startNameAddress.toString(),
// passengerLocation.longitude.toString(),
// (box.read(BoxName.name).toString().split(' ')[0]).toString(),
// box.read(BoxName.passengerID).toString(),
// box.read(BoxName.phone).toString(),
// box.read(BoxName.email).toString(),
// box.read(BoxName.passengerPhotoUrl).toString(),
// box.read(BoxName.tokenFCM).toString(),
// (driver['token'].toString()),
// ],
// );
// if (response['message'] == "Trip updated successfully") {
// mySnackbarSuccess("Trip updated successfully".tr);
// Log.print(
// 'previous_driver_token: ${response['previous_driver_token']}');
// await NotificationService.sendNotification(
// category: 'Order VIP Canceld',
// target: response['previous_driver_token'].toString(),
// title: 'Order VIP Canceld'.tr,
// body: 'Passenger cancel order'.tr,
// isTopic: false, // Important: this is a token
// tone: 'cancel',
// driverList: [],
// );
// }
// // data = [];
// isBottomSheetShown = false;
// update();
// Get.to(() => VipWaittingPage());
// } else {
// throw Exception('Failed to save trip');
// }
// } catch (e) {
// // Show error message with more details for debugging
// Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr,
// backgroundColor: AppColor.redColor);
// Log.print('Error: $e');
// }
// }
// Future<void> cancelVip(String token, tripId) async {
// var res = await CRUD()
// .post(link: AppLink.cancelMishwari, payload: {'id': tripId});
// if (res != 'failur') {
// Get.back();
// mySnackbarSuccess('You canceled VIP trip'.tr);
// }
// }
// void sendToDriverAgain(String token) {
// NotificationService.sendNotification(
// category: 'Order VIP Canceld',
// target: token.toString(),
// title: 'Order VIP Canceld'.tr,
// body: 'Passenger cancel order'.tr,
// isTopic: false, // Important: this is a token
// tone: 'cancel',
// driverList: [],
// );
// }
// // دالة الفحص عند بدء التطبيق
// Future<void> detectAndCacheDeviceTier() async {
// // 1. استخدام الكلاس الذي أنشأناه سابقاً للفحص
// bool isHighEnd = await DevicePerformanceManager.isHighEndDevice();
// // 2. طباعة النتيجة للتأكد
// Log.print("Device Analysis - Is Flagship/HighEnd? $isHighEnd");
// // 3. تخزين النتيجة بشكل منطقي صحيح
// // إذا كان الجهاز قوياً (true)، فإن وضع الـ LowEnd يكون (false)
// // والعكس صحيح
// box.write(BoxName.lowEndMode, !isHighEnd);
// }
// initilizeGetStorage() async {
// if (box.read(BoxName.addWork) == null) {
// box.write(BoxName.addWork, 'addWork');
// }
// if (box.read(BoxName.addHome) == null) {
// box.write(BoxName.addHome, 'addHome');
// }
// if (box.read(BoxName.lowEndMode) == null) {
// detectAndCacheDeviceTier();
// }
// }
// late List recentPlaces = [];
// getFavioratePlaces() async {
// recentPlaces = await sql.getCustomQuery(
// 'SELECT * FROM ${TableName.recentLocations} ORDER BY createdAt DESC');
// // Log.print('recentPlaces: ${recentPlaces}');
// }
// double passengerRate = 5;
// double comfortPrice = 45;
// double speedPrice = 40;
// double mashwariPrice = 40;
// double familyPrice = 55;
// double deliveryPrice = 1.2;
// double minFareSYP = 16000; // حد أدنى للأجرة (سوريا)
// double minBillableKm = 1.0; // حد أدنى للمسافة المفوترة
// double commissionPct = 15; // عمولة التطبيق % (راكب)
// getKazanPercent() async {
// var res = await CRUD().get(
// link: AppLink.getKazanPercent,
// payload: {'country': box.read(BoxName.countryCode).toString()},
// );
// if (res != 'failure') {
// var json = jsonDecode(res);
// // التحقق الديناميكي من 'data' أو 'message'
// var dataList = json['data'] ?? json['message'];
// if (dataList != null && dataList is List && dataList.isNotEmpty) {
// var firstRow = dataList[0];
// kazan = double.parse(firstRow['kazan'].toString());
// naturePrice = double.parse(firstRow['naturePrice'].toString());
// heavyPrice = double.parse(firstRow['heavyPrice'].toString());
// latePrice = double.parse(firstRow['latePrice'].toString());
// comfortPrice = double.parse(firstRow['comfortPrice'].toString());
// speedPrice = double.parse(firstRow['speedPrice'].toString());
// deliveryPrice = double.parse(firstRow['deliveryPrice'].toString());
// mashwariPrice = double.parse(firstRow['freePrice'].toString());
// familyPrice = double.parse(firstRow['familyPrice'].toString());
// fuelPrice = double.parse(firstRow['fuelPrice'].toString());
// }
// }
// }
// getPassengerRate() async {
// var res = await CRUD().get(
// link: AppLink.getPassengerRate,
// payload: {'passenger_id': box.read(BoxName.passengerID)});
// if (res != 'failure') {
// var json = jsonDecode(res);
// var message = json['data'] ?? json['message'];
// if (message['rating'] == null) {
// passengerRate = 5.0; // Default rating
// } else {
// // Safely parse the rating to double
// var rating = message['rating'];
// if (rating is String) {
// passengerRate =
// double.tryParse(rating) ?? 5.0; // Default if parsing fails
// } else if (rating is num) {
// passengerRate =
// rating.toDouble(); // Already a number, convert to double
// } else {
// passengerRate = 5.0; // Default for unexpected data types
// }
// }
// } else {
// passengerRate = 5.0; // Default rating for failure
// }
// }
// addFingerPrint() async {
// String fingerPrint = await DeviceHelper.getDeviceFingerprint();
// await CRUD().postWallet(link: AppLink.addFingerPrint, payload: {
// 'token': (box.read(BoxName.tokenFCM.toString())),
// 'passengerID': box.read(BoxName.passengerID).toString(),
// "fingerPrint": fingerPrint
// });
// }
// firstTimeRunToGetCoupon() async {
// // Check if it's the first time and the app is installed and gift token is available
// if (box.read(BoxName.isFirstTime).toString() == '0' &&
// box.read(BoxName.isInstall).toString() == '1' &&
// box.read(BoxName.isGiftToken).toString() == '0') {
// var promo, discount, validity;
// var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: {
// "passengerID": box.read(BoxName.passengerID).toString(),
// });
// if (resPromo != 'failure') {
// var d1 = jsonDecode(resPromo);
// promo = d1['message']['promo_code'];
// discount = d1['message']['amount'];
// validity = d1['message']['validity_end_date'];
// }
// box.write(BoxName.isFirstTime, '1');
// // Show a full-screen modal styled as an ad
// Get.dialog(
// AlertDialog(
// contentPadding:
// EdgeInsets.zero, // Removes the padding around the content
// content: SizedBox(
// width: 300, // Match the width of PromoBanner
// // height: 250, // Match the height of PromoBanner
// child: PromoBanner(
// promoCode: promo,
// discountPercentage: discount,
// validity: validity,
// ),
// ),
// ),
// );
// }
// }
// // --- دالة جديدة للاستماع ومعالجة الرابط ---
// void _listenForDeepLink() {
// ever(_deepLinkController.rawDeepLink, (String? link) async {
// if (link != null && link.isNotEmpty) {
// Log.print('📍 MapPassengerController processing link: $link');
// // 1. استخراج الإحداثيات باستخدام الدالة الموجودة لديك مسبقاً
// Map<String, double>? coordinates =
// await extractCoordinatesFromLinkAsync(link);
// if (coordinates != null) {
// double destLat = coordinates['latitude']!;
// double destLng = coordinates['longitude']!;
// myDestination = LatLng(destLat, destLng);
// // 2. التحقق من موقع الراكب الحالي
// if (passengerLocation == null ||
// (passengerLocation.latitude == 0 &&
// passengerLocation.longitude == 0)) {
// Log.print('⏳ Waiting for current location to calculate route...');
// await getLocation(); // جلب موقع الراكب إذا لم يكن متاحاً
// }
// if (passengerLocation != null) {
// String originStr =
// '${passengerLocation.latitude},${passengerLocation.longitude}';
// String destStr = '$destLat,$destLng';
// Log.print(
// '🚀 Drawing route from Deep Link: $originStr to $destStr');
// // 3. مسح أي مسارات ونقاط توقف سابقة
// clearPolyline();
// waypoints.clear();
// clearAllMenuWaypoints();
// // 4. استدعاء دالة رسم المسار وحساب التكلفة التي برمجتها
// await getDirectionMap(originStr, destStr);
// // 5. إظهار الواجهة السفلية للرحلة ليكون الطلب جاهزاً بنقرة واحدة
// isBottomSheetShown = true;
// heightBottomSheetShown = 250;
// update();
// Get.snackbar(
// 'Location Received'.tr,
// 'Route and prices have been calculated successfully!'.tr,
// backgroundColor: AppColor.greenColor,
// colorText: Colors.white,
// );
// }
// } else {
// Log.print('⚠️ Could not extract valid coordinates from link: $link');
// }
// // تفريغ الرابط بعد معالجته حتى لا يتم استدعاؤه مرة أخرى بالخطأ
// _deepLinkController.rawDeepLink.value = null;
// }
// });
// // معالجة الرابط إذا كان موجوداً مسبقاً (Cold Start) قبل تفعيل المستمع
// if (_deepLinkController.rawDeepLink.value != null &&
// _deepLinkController.rawDeepLink.value!.isNotEmpty) {
// String link = _deepLinkController.rawDeepLink.value!;
// _deepLinkController.rawDeepLink.value = null;
// // نؤجل التنفيذ قليلاً لضمان تحميل الخريطة
// Future.delayed(const Duration(milliseconds: 500), () async {
// Log.print(
// '📍 MapPassengerController processing link (Cold Start): $link');
// Map<String, double>? coordinates =
// await extractCoordinatesFromLinkAsync(link);
// if (coordinates != null) {
// double destLat = coordinates['latitude']!;
// double destLng = coordinates['longitude']!;
// myDestination = LatLng(destLat, destLng);
// if (passengerLocation == null ||
// (passengerLocation.latitude == 0 &&
// passengerLocation.longitude == 0)) {
// await getLocation();
// }
// if (passengerLocation != null) {
// String originStr =
// '${passengerLocation.latitude},${passengerLocation.longitude}';
// String destStr = '$destLat,$destLng';
// clearPolyline();
// waypoints.clear();
// clearAllMenuWaypoints();
// await getDirectionMap(originStr, destStr);
// isBottomSheetShown = true;
// heightBottomSheetShown = 250;
// update();
// }
// }
// });
// }
// }
// @override
// void onInit() async {
// super.onInit();
// _checkAndRefreshMapStyle(); // Verify style version and clear cache if needed
// // // --- إضافة جديدة: تهيئة وحدة التحكم في الروابط العميقة ---
// Get.put(DeepLinkController(), permanent: true);
// // // ----------------------------------------------------
// // مرحلة 0: الضروري جداً لعرض الخريطة سريعاً
// // mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
// await initilizeGetStorage(); // إعداد سريع
// await _initMinimalIcons(); // start/end فقط
// // await addToken(); // لو لازم للمصادقة
// _listenForDeepLink();
// // initSocket();
// await getLocation(); // لتحديد الكاميرا
// box.write(BoxName.carType, 'yet');
// box.write(BoxName.tipPercentage, '0');
// // await detectAndCacheDeviceTier();
// // لا تُنشئ Controllers الثقيلة الآن:
// Get.lazyPut<TextToSpeechController>(() => TextToSpeechController(),
// fenix: true);
// Get.lazyPut<FirebaseMessagesController>(() => FirebaseMessagesController(),
// fenix: true);
// Get.lazyPut<AudioRecorderController>(() => AudioRecorderController(),
// fenix: true);
// // ابدأ الخريطة الآن (الشاشة ظهرت للمستخدم)
// Future.delayed(const Duration(seconds: 4), () {
// if (isLoading) {
// isLoading = false;
// update();
// }
// });
// // مرحلة 1: مهام ضرورية للتسعير لكن غير حرجة لظهور UI
// unawaited(_stagePricingAndState());
// // مرحلة 2: تحسينات/كماليات بالخلفية
// unawaited(_stageNiceToHave());
// // ابدأ إعادة تحميل الماركر لكن بثروتل داخلي
// // startMarkerReloading(); // تأكد أنه مَخنوق التحديث (throttled)
// _startMasterTimer();
// // Start listening to emergency shake gestures
// EmergencySignalService.instance.startListening(() {
// if (statusRide == 'Begin' || statusRide == 'start') {
// Log.print("🚨 Emergency shake verified! Prompting SOS...");
// if (isBottomSheetShown) {
// sosPassenger();
// } else {
// Get.snackbar(
// 'Emergency Mode Triggered'.tr,
// 'Stay calm. We are here to help.'.tr,
// backgroundColor: AppColor.redColor,
// colorText: Colors.white,
// duration: const Duration(seconds: 4),
// );
// sosPassenger();
// }
// }
// });
// }
// // === Helpers ===
// Future<void> _initMinimalIcons() async {
// // Icons are now loaded dynamically via MapLibre's _loadMapIcons onStyleLoaded
// }
// Future<void> _stagePricingAndState() async {
// try {
// await getKazanPercent(); // أسعار السيرفر
// } catch (e) {
// Log.print("Error: $e");
// }
// try {
// _checkInitialRideStatus(); // تحقق من حالة الرحلة الحالية
// } catch (e) {
// Log.print("Error: $e");
// }
// // لو عندك ضبط “وضع خفيف” حسب الجهاز:
// _applyLowEndModeIfNeeded();
// }
// Future<void> _stageNiceToHave() async {
// Log.print('🚀 MapPassengerController: Starting _stageNiceToHave');
// // 🔥 Fix: Future.wait uses ONE argument (the list).
// await Future.wait([
// Future(() async {
// try {
// Log.print('🔍 Loading Favorites...');
// getFavioratePlaces();
// } catch (e) {
// Log.print("Error: $e");
// }
// }),
// Future(() async {
// try {
// Log.print('🔍 Loading Waypoints...');
// readyWayPoints();
// } catch (e) {
// Log.print("Error: $e");
// }
// }),
// Future(() async {
// try {
// Log.print('🔍 Loading Rate...');
// getPassengerRate();
// } catch (e) {
// Log.print("Error: $e");
// }
// }),
// Future(() async {
// try {
// Log.print('🔍 Loading Coupons...');
// firstTimeRunToGetCoupon();
// } catch (e) {
// Log.print("Error: $e");
// }
// }),
// ]);
// Log.print('✅ MapPassengerController: _stageNiceToHave complete');
// try {
// cardNumber = await SecureStorage().readData(BoxName.cardNumber);
// } catch (e) {
// Log.print("Error: $e");
// }
// }
// void _applyLowEndModeIfNeeded() {
// // مثال بسيط: يمكنك حفظ فلاج بنظامك (من السيرفر/الإعدادات/الكاش) لتفعيل وضع خفيف
// // لاحقاً فعّل: map.trafficEnabled=false, buildingsEnabled=false, تبسيط polylines...
// // controller.lowEndMode = true;
// }
// uploadPassengerLocation() async {
// await CRUD().post(link: AppLink.addpassengerLocation, payload: {
// "passengerId": box.read(BoxName.passengerID),
// "lat": passengerLocation.latitude.toString(),
// "lng": passengerLocation.longitude.toString(),
// "rideId": rideId.toString()
// });
// }
// void _showRideStartNotifications() {
// // تنبيهات الأسعار حسب نوع السيارة
// if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) {
// NotificationController().showNotification('Fixed Price'.tr,
// 'The captain is responsible for the route.'.tr, 'ding');
// } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) {
// NotificationController().showNotification('Attention'.tr,
// 'The price may increase if the route changes.'.tr, 'ding');
// }
// }
// /// Checks the current version of assets/style.json and purges the map cache if it has changed.
// Future<void> _checkAndRefreshMapStyle() async {
// try {
// final String styleJson = await rootBundle.loadString('assets/style.json');
// final Map<String, dynamic> decoded = json.decode(styleJson);
// final String? currentVersion =
// decoded['metadata'] != null ? decoded['metadata']['version'] : null;
// if (currentVersion == null) return;
// final String lastVersion = box.read(BoxName.styleVersion) ?? "0.0.0";
// if (currentVersion != lastVersion) {
// Log.print(
// "♻️ Map Style Version mismatch ($lastVersion -> $currentVersion). Purging offline cache...");
// await OfflineMapService.instance.clearCache();
// // Final verification check: give native engine time to flush
// await Future.delayed(const Duration(milliseconds: 500));
// box.write(BoxName.styleVersion, currentVersion);
// Log.print("✅ Style Version updated to $currentVersion");
// }
// } catch (e) {
// Log.print("⚠️ Style version check failed: $e");
// }
// }
// }
// class CarLocation {
// final String id;
// final double latitude;
// final double longitude;
// final double distance;
// final double duration;
// CarLocation({
// required this.id,
// required this.latitude,
// required this.longitude,
// this.distance = 10000,
// this.duration = 10000,
// });
// }