import time
import random
import win32com.client as com
import pythoncom
from UCwinRoadCOM import UCwinRoadComProxy
from LoggerProxy import LoggerProxy
from UCwinRoadUtils import SetCallbackHandlers, CloseCallbackEvent, SetF8COMdVec3, AsF8COMdVec3
from .config import SimulationPlan, RoadConfig

class RushHourScenario:
    def __init__(self, winRoadProxy, logProxy: LoggerProxy):
        self.winRoadProxy = winRoadProxy
        self.logProxy = logProxy
        self.is_running = False
        self.vehicles = []

        # Accident state per road
        self.accidents = {}  # name -> dict(pos, lane, distance, blocker, objects)

        self.selected_road_names = []
        self.plan: SimulationPlan | None = None

        self._ensure_default_plan()

        # Batch mode: generate all first, then start simulation
        self.batch_mode = True

        # Emergency vehicle tracking
        self.emergency_car = None
        self.emergency_target_distance: float | None = None
        self.emergency_target_lane: int | None = None
        self.emergency_target_road = None
        self.emergency_active = False
        # Emergency game state
        self.emg_car = None
        self.emg_target_pos = None
        self.emg_active = False
        self.emg_started_at = None
        self.emg_lane = 1
        self.emg_road = None
        self.emg_player_name = None
        self.emg_game_mode = False  
        self.emg_rankings = []  

        self.preferred_models = [
            "4WD Blue", "Compact Car - Yellow", "Coupe",
            "Motorcycle", "Sedan Red"
        ]
        self._model_cache = {}
        self._cone_model = None  # cache cone model for accident scene
        self._accident_blocker_model = None  # cache a blocker vehicle model
        self._vehicle_model_indices: list[int] | None = None
        self._selected_model_indices: list[int] | None = None
        self._emergency_model_indices: list[int] | None = None
        self._cone_pool: list = []  # preallocated cone instances for fast accident spawn
        self._blocker_pool: list = []  # preallocated blocker vehicles for accident scenes
        self.emergency_yield_enabled = True

        # Simulation completion tracking
        self._simulation_completed = False
        self._simulation_success = False
        self._current_progress_popup = None
        self._current_progress_var = None


    def _ensure_default_plan(self):
        try:
            if self.plan is not None:
                return  # Plan already exists

            prj = self.winRoadProxy.Project
            if prj.RoadsCount == 0:
                self.logProxy.logger.warning("No roads found in project, cannot create default plan")
                return

            configs = []
            for i in range(min(prj.RoadsCount, 5)): 
                road = prj.Road(i)
                road_name = road.Name

                config = RoadConfig(
                    name=road_name,
                    enabled=True,  
                    vehicles=30,   
                    min_speed=20.0,
                    max_speed=60.0,
                    forward_lanes=[1],  
                    backward_lanes=[2]  
                )
                configs.append(config)

            if configs:
                self.plan = SimulationPlan(configs=configs)
                self.selected_road_names = [c.name for c in configs if c.enabled]
                self.logProxy.logger.info(f"Created default simulation plan with {len(configs)} roads: {self.selected_road_names}")
            else:
                self.logProxy.logger.warning("Failed to create default simulation plan - no valid road configurations")

        except Exception as e:
            self.logProxy.logger.error(f"Failed to create default simulation plan: {e}")

    def warm_cache_models(self):
        """Populate model indices once at script init to avoid repeated COM scans."""
        # Show progress popup for model loading
        popup, progress_var = self.show_progress_popup(
            "🔧 Loading Models",
            "Scanning and caching 3D models...",
            duration_ms=5000
        )

        try:
            prj = self.winRoadProxy.Project
            const = self.winRoadProxy.const

            self.update_progress_popup(progress_var, "Scanning vehicle models...")
            veh_indices = []
            total_models = prj.ThreeDModelsCount
            for i in range(total_models):
                try:
                    if prj.ThreeDModel(i).ModelType == const._VehicleModel:
                        veh_indices.append(i)
                except Exception:
                    pass

            self.update_progress_popup(progress_var, "Categorizing models...")
            self._vehicle_model_indices = veh_indices
            # Select preferred models by index, fallback to all vehicles
            sel = []
            names = set(self.preferred_models)
            for i in veh_indices:
                try:
                    m = prj.ThreeDModel(i)
                    if m.Name in names:
                        sel.append(i)
                except Exception:
                    pass
            self._selected_model_indices = sel if sel else veh_indices

            # Emergency-preferred indices
            self.update_progress_popup(progress_var, "Finding emergency vehicles...")
            prefer = ["Ambulance", "Police", "Fire", "Emergency"]
            emg_idx = []
            for i in veh_indices:
                try:
                    m = prj.ThreeDModel(i)
                    if any(p.lower() in m.Name.lower() for p in prefer):
                        emg_idx.append(i)
                except Exception:
                    pass
            self._emergency_model_indices = emg_idx

            self.logProxy.logger.info(f"Model cache: vehicles={len(veh_indices)}, selected={len(self._selected_model_indices)}, emergency_like={len(emg_idx)}")

            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Models Loaded",
                f"✅ Model cache populated successfully!\n\n"
                f"🚗 Total vehicles: {len(veh_indices)}\n"
                f"🎯 Selected models: {len(self._selected_model_indices)}\n"
                f"🚨 Emergency vehicles: {len(emg_idx)}\n\n"
                f"Ready for traffic generation.",
                success=True
            )
            return True

        except Exception as e:
            self.logProxy.logger.error(f"warm_cache_models failed: {e}")
            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Model Loading Failed",
                f"Failed to load models: {str(e)}",
                success=False
            )
            return False
    def warm_emergency_vehicle(self):
        """Pre-create one emergency vehicle off-screen for instant reuse."""
        try:
            prj = self.winRoadProxy.Project
            const = self.winRoadProxy.const
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation
            # pick emergency-like model if available
            model = None
            if self._emergency_model_indices:
                try:
                    model = prj.ThreeDModel(self._emergency_model_indices[0])
                except Exception:
                    model = None
            if model is None and self._vehicle_model_indices:
                try:
                    model = prj.ThreeDModel(self._vehicle_model_indices[0])
                except Exception:
                    model = None
            if model is None:
                # as a last resort, scan
                for i in range(prj.ThreeDModelsCount):
                    m = prj.ThreeDModel(i)
                    if m.ModelType == const._VehicleModel:
                        model = m; break
            if model is None:
                return False
            # create on first road if exists, then park off-screen
            if prj.RoadsCount <= 0:
                return False
            road0 = prj.Road(0)
            vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
            vptype.IsForward = True
            vptype.Lane = 1
            vptype.Distance = 0.0
            car = traffic.AddNewVehicle(model, road0, vptype)
            if not car:
                return False
            self._emg_warm_car = car
            return True
        except Exception as e:
            self.logProxy.logger.error(f"warm_emergency_vehicle failed: {e}")
            return False

    def warm_accident_assets(self):
        """Preload models/assets for accident scene to avoid long loads on first use."""
        try:
            prj = self.winRoadProxy.Project
            const = self.winRoadProxy.const
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation

            # Ensure cone model is cached and loaded by creating and deleting one instance
            if self._cone_model is None:
                for i in range(prj.ThreeDModelsCount):
                    try:
                        m = prj.ThreeDModel(i)
                        if 'cone' in m.Name.lower():
                            self._cone_model = m
                            break
                    except Exception:
                        pass
            if self._cone_model is not None:
                # Pre-create a small pool of cones off-screen for fast reuse
                try:
                    if not self._cone_pool:
                        for _ in range(8):
                            inst = traffic.AddNewTransient(self._cone_model)
                            if inst:
                                self._cone_pool.append({'inst': inst, 'busy': False})
                except Exception:
                    pass
            # Pre-create blocker vehicles pool (2 vehicles) to avoid first-use lag
            try:
                if prj.RoadsCount > 0 and not self._blocker_pool:
                    road0 = prj.Road(0)
                    model = None
                    if self._accident_blocker_model is not None:
                        model = self._accident_blocker_model
                    elif self._selected_model_indices:
                        try:
                            model = prj.ThreeDModel(self._selected_model_indices[0])
                        except Exception:
                            model = None
                    if model is None:
                        for i in range(prj.ThreeDModelsCount):
                            m = prj.ThreeDModel(i)
                            if m.ModelType == const._VehicleModel:
                                model = m; break
                    if model is not None:
                        vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
                        vptype.IsForward = True
                        vptype.Lane = 1
                        vptype.Distance = 0.0
                        for _ in range(2):
                            inst = traffic.AddNewVehicle(model, road0, vptype)
                            if inst:
                                self._blocker_pool.append(inst)
            except Exception:
                pass

            # Ensure at least one vehicle model is preloaded by create+delete on first use
            try:
                if prj.RoadsCount > 0:
                    road0 = prj.Road(0)
                    # pick a cached vehicle model if available
                    model = None
                    if self._accident_blocker_model is not None:
                        model = self._accident_blocker_model
                    elif self._selected_model_indices:
                        try:
                            model = prj.ThreeDModel(self._selected_model_indices[0])
                        except Exception:
                            model = None
                    if model is None:
                        # last resort scan
                        for i in range(prj.ThreeDModelsCount):
                            m = prj.ThreeDModel(i)
                            if m.ModelType == const._VehicleModel:
                                model = m; break
                    if model is not None:
                        vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
                        vptype.IsForward = True
                        vptype.Lane = 1
                        vptype.Distance = 0.0
                        car = traffic.AddNewVehicle(model, road0, vptype)
                        if car:
                            try:
                                car.Delete()
                            except Exception:
                                pass
            except Exception:
                pass
        except Exception as e:
            self.logProxy.logger.error(f"warm_accident_assets failed: {e}")

    # --- Accident cone pooling helpers ---
    def _acquire_cone(self):
        try:
            # Return a free pre-created cone if available
            for item in self._cone_pool:
                if not item.get('busy') and item.get('inst') is not None:
                    item['busy'] = True
                    return item['inst']
            # Otherwise, create a new one and add to pool
            prj = self.winRoadProxy.Project
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation
            if self._cone_model is None:
                # try to locate
                for i in range(prj.ThreeDModelsCount):
                    m = prj.ThreeDModel(i)
                    if 'cone' in m.Name.lower():
                        self._cone_model = m; break
            if self._cone_model is not None:
                inst = traffic.AddNewTransient(self._cone_model)
                if inst:
                    self._cone_pool.append({'inst': inst, 'busy': True})
                    return inst
        except Exception:
            pass
        return None

    def _release_cone(self, inst):
        try:
            for item in self._cone_pool:
                if item.get('inst') is inst:
                    item['busy'] = False
                    try:
                        inst.Position = AsF8COMdVec3(1e6, -1000.0, 1e6)
                    except Exception:
                        pass
                    return True
        except Exception:
            pass
        return False

    def set_plan(self, plan: SimulationPlan):
        self.plan = plan
        self.selected_road_names = plan.enabled_roads()
        self.logProxy.logger.info(f"Simulation plan set for roads: {self.selected_road_names}")
        # Accident state per road
        self.accidents = {}  # name -> dict(pos: IF8COMdVec3, lane: int, distance: float, blocker: IF8TransientCarInstanceProxy)


    def start_simulation(self):
        if self.is_running:
            self.logProxy.logger.info("A simulation process is already running.")
            return

        try:
            self.logProxy.logger.info("Starting simulation process...")

            # Show progress popup for traffic generation (will auto-close after duration)
            popup, progress_var = self.show_progress_popup(
                "🚗 Generating Traffic",
                "Initializing traffic simulation...",
                duration_ms=5000  # Extended duration for longer operations
            )

            # Store popup references for UI updates
            self._current_progress_popup = popup
            self._current_progress_var = progress_var

            self.is_running = True

            # Clear any previously spawned vehicles before starting a new run
            self.clear_vehicles(stop_simulation_engine=False)

            # Start vehicle spawning synchronously
            try:
                self.spawn_vehicles()
                self._simulation_completed = True
                self._simulation_success = True
            except Exception as e:
                self.logProxy.logger.error(f"Error in vehicle spawning: {e}")
                self._simulation_completed = True
                self._simulation_success = False

        except Exception as e:
            self.logProxy.logger.error(f"Failed to start simulation thread: {e}")
            self.is_running = False
            if popup:
                try:
                    popup.destroy()
                except:
                    pass

    def stop_simulation(self):
        if self.is_running:
            self.logProxy.logger.info("Stopping vehicle spawning...")
            self.is_running = False # Signal to stop (though synchronous spawning should complete quickly)

            # Clean up simulation completion tracking
            self._simulation_completed = False
            self._simulation_success = False
            # Note: Don't destroy popup here as it may be handled by the tick loop

        try:
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation
            if traffic.TrafficState == 1:
                self.logProxy.logger.info("Stopping traffic simulation engine.")
                traffic.TrafficState = 0
        except Exception as e:
            self.logProxy.logger.error(f"Could not stop traffic simulation engine: {e}")

    def clear_vehicles(self, stop_simulation_engine=True):
        # Never stop the sim engine from here; keep existing vehicles alive
        self.logProxy.logger.info(f"Clearing {len(self.vehicles)} tracked vehicles...")
        for vehicle in self.vehicles:
            try:
                vehicle.Remove()
            except Exception:
                pass 
        self.vehicles.clear()
        self.logProxy.logger.info("Tracked vehicles cleared.")

    def _iter_spawns(self, project):
        """Generator to yield spawn configurations: (road_object, config_object)"""
        if self.plan and self.plan.configs:
            # Filter to valid configs (enabled with vehicles > 0)
            valid_cfgs = [cfg for cfg in self.plan.configs if cfg.enabled and cfg.vehicles > 0]
            if not valid_cfgs:
                self.logProxy.logger.warning("Simulation plan has no enabled roads with vehicles. Falling back to default spawning.")
            else:
                self.logProxy.logger.info("Spawning vehicles based on simulation plan.")
                for cfg in valid_cfgs:
                    road_found = None
                    for i in range(project.RoadsCount):
                        r = project.Road(i)
                        if r.Name == cfg.name:
                            road_found = r
                            break
                    if not road_found:
                        self.logProxy.logger.warning(f"Plan road not found, skipping: {cfg.name}")
                        continue
                    for _ in range(cfg.vehicles):
                        yield road_found, cfg
                return
        self.logProxy.logger.warning("No valid simulation plan. Spawning on first available road.")
        if project.RoadsCount > 0:
            road = project.Road(0)
            legacy_cfg = RoadConfig(name=road.Name, vehicles=90, min_speed=20, max_speed=50,
                                    forward_lanes=[1], backward_lanes=[2])
            for _ in range(legacy_cfg.vehicles):
                yield road, legacy_cfg

    def spawn_vehicles(self):
        """Create vehicles synchronously on the main thread."""
        try:
            project = self.winRoadProxy.Project
            const = self.winRoadProxy.const
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation

            # Calculate total vehicles to spawn for progress tracking
            total_vehicles = sum(config.vehicles for _, config in self._iter_spawns(project))
            current_vehicle = 0

            # Ensure traffic is stopped while adding vehicles in batch mode
            if self.batch_mode and traffic.TrafficState != 0:
                traffic.TrafficState = 0
                time.sleep(0.5)

            # Collect vehicle models via cached indices if available
            sel_idx = self._selected_model_indices
            if not sel_idx:
                # Fallback to scan
                all_models = [project.ThreeDModel(i) for i in range(project.ThreeDModelsCount)
                              if project.ThreeDModel(i).ModelType == const._VehicleModel]
                selected_models = [m for m in all_models if m.Name in self.preferred_models] or all_models
            else:
                selected_models = [project.ThreeDModel(i) for i in sel_idx]

            for road, config in self._iter_spawns(project):
                if not self.is_running:
                    self.logProxy.logger.info("Spawning interrupted by stop signal.")
                    break

                try:
                    chosen_model = random.choice(selected_models)
                    is_forward = random.choice([True, False])
                    target_lane = random.choice(config.forward_lanes if is_forward else config.backward_lanes)

                    vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
                    vptype.IsForward = is_forward
                    vptype.Lane = target_lane
                    vptype.Distance = random.uniform(0.0, getattr(road, 'Length', 100.0))

                    car = traffic.AddNewVehicle(chosen_model, road, vptype)
                    if car:
                        car.SetSpeed(const._KiloMeterPerHour, random.uniform(config.min_speed, config.max_speed))
                        self.vehicles.append(car) # Track created vehicle
                        current_vehicle += 1

                except Exception as e:
                    self.logProxy.logger.error(f"Error during vehicle spawn loop: {e}")

            if self.is_running and self.batch_mode:
                self.logProxy.logger.info("Batch spawning complete. Starting traffic simulation...")
                traffic.TrafficState = 1

            self.logProxy.logger.info(f"Vehicle spawning completed: {current_vehicle} vehicles created")

        except Exception as e:
            self.logProxy.logger.error(f"Fatal error in spawn_vehicles: {e}")

    def start_simulation_sync(self, on_progress=None):
        """Spawn vehicles synchronously on the main thread, updating optional on_progress(cb_done, cb_total)."""
        if self.is_running:
            self.logProxy.logger.info("A simulation process is already running.")
            return
        try:
            self.is_running = True
            self.clear_vehicles(stop_simulation_engine=False)
            self._spawn_vehicles_sync_body(on_progress)
        finally:
            self.is_running = False

    def _spawn_vehicles_sync_body(self, on_progress=None):
        project = self.winRoadProxy.Project
        const = self.winRoadProxy.const
        traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation

        sel_idx = self._selected_model_indices
        if not sel_idx:
            all_models = [project.ThreeDModel(i) for i in range(project.ThreeDModelsCount)
                          if project.ThreeDModel(i).ModelType == const._VehicleModel]
            selected_models = [m for m in all_models if m.Name in self.preferred_models] or all_models
        else:
            selected_models = [project.ThreeDModel(i) for i in sel_idx]

        # Count total for progress
        total = 0
        for _ in self._iter_spawns(project):
            total += 1
        done = 0

        for road, config in self._iter_spawns(project):
            try:
                chosen_model = random.choice(selected_models)
                is_forward = random.choice([True, False])
                target_lane = random.choice(config.forward_lanes if is_forward else config.backward_lanes)

                vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
                vptype.IsForward = is_forward
                vptype.Lane = target_lane
                vptype.Distance = random.uniform(0.0, getattr(road, 'Length', 100.0))

                car = traffic.AddNewVehicle(chosen_model, road, vptype)
                if car:
                    car.SetSpeed(const._KiloMeterPerHour, random.uniform(config.min_speed, config.max_speed))
                    self.vehicles.append(car)
            except Exception as e:
                self.logProxy.logger.error(f"Error during vehicle spawn loop: {e}")
            finally:
                done += 1
                try:
                    on_progress and on_progress(done, total)
                except Exception:
                    pass
    def create_emergency_game(self, road_name: str, lane: int, percent: float):
        """Spawn emergency car at road percent, set camera, and prep game state.
        Reuses a pre-warmed emergency vehicle instance if available; otherwise creates one.
        """
        # Show progress popup (no auto-close since we manually close it)
        popup, progress_var = self.show_progress_popup(
            "🚨 Creating Emergency Game",
            f"Setting up emergency scenario on {road_name}...",
            duration_ms=None,
            auto_close=False
        )

        try:
            prj = self.winRoadProxy.Project
            const = self.winRoadProxy.const
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation

            self.update_progress_popup(progress_var, "Finding road location...")
            # find road
            road = None
            for i in range(prj.RoadsCount):
                r = prj.Road(i)
                if r.Name == road_name:
                    road = r; break
            if not road:
                return False
            road_len = float(getattr(road, 'Length', 0.0))
            distance = float(max(0.0, min(road_len, (percent/100.0)*road_len)))

            self.update_progress_popup(progress_var, "Preparing emergency vehicle...")
            # Always create a fresh vehicle (simpler and more reliable)
            # Dispose any warmed car instance to avoid confusion
            try:
                if getattr(self, '_emg_warm_car', None) is not None:
                    try:
                        self._emg_warm_car.Delete()
                    except Exception:
                        pass
                    self._emg_warm_car = None
            except Exception:
                pass

            self.update_progress_popup(progress_var, "Loading vehicle models...")
            # pick a drivable vehicle model (prioritize cars, exclude bikes/characters)
            model = None
            def _is_drivable(m):
                name = (m.Name or '').lower()
                bad = ['bicycle', 'bike', 'pedestrian', 'character', 'bus stop', 'cone']
                return m.ModelType == const._VehicleModel and not any(b in name for b in bad)
            # preference: emergency-like names
            if self._emergency_model_indices:
                for idx in self._emergency_model_indices:
                    try:
                        candidate = prj.ThreeDModel(idx)
                        if _is_drivable(candidate):
                            model = candidate; break
                    except Exception:
                        pass
            # fallback: preferred list or any drivable vehicle
            if model is None and self._selected_model_indices:
                for idx in self._selected_model_indices:
                    try:
                        candidate = prj.ThreeDModel(idx)
                        if _is_drivable(candidate):
                            model = candidate; break
                    except Exception:
                        pass
            if model is None:
                for i in range(prj.ThreeDModelsCount):
                    try:
                        m = prj.ThreeDModel(i)
                        if _is_drivable(m):
                            model = m; break
                    except Exception:
                        pass
            if model is None:
                self.logProxy.logger.error("create_emergency_game: no vehicle model found")
                return False
            vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
            vptype.IsForward = True
            vptype.Lane = int(lane)
            vptype.Distance = distance
            self.update_progress_popup(progress_var, "Spawning emergency vehicle...")
            car = traffic.AddNewVehicle(model, road, vptype)
            if not car:
                self.logProxy.logger.error("create_emergency_game: AddNewVehicle returned None")
                return False
            self.vehicles.append(car)
            self.logProxy.logger.info(f"Emergency car spawned on '{road_name}' lane {lane} at {percent:.1f}%")

            self.update_progress_popup(progress_var, "Setting up camera...")
            # camera to vehicle (robust: defer a tiny bit for position to settle)
            try:
                pos = car.Position
                dirv = getattr(car, 'BodyDirection', None) or getattr(car, 'Direction', None)
                if dirv is None:
                    dirv = AsF8COMdVec3(0,0,1)
                back = 20.0; up = 10.0
                eye = AsF8COMdVec3(pos.X - dirv.X*back, pos.Y + up, pos.Z - dirv.Z*back)
                self.winRoadProxy.MainForm.MainCamera.SetLookAt(eye, AsF8COMdVec3(pos.X, pos.Y + 2.0, pos.Z))
            except Exception:
                pass

            self.update_progress_popup(progress_var, "Preparing game state...")
            # prepare game state
            self.emg_car = car
            self.emg_target_pos = self.get_accident_pos(road_name)
            self.emg_active = True
            self.emg_started_at = time.perf_counter()
            self.emg_lane = int(lane)
            self.emg_road = road
            # schedule a deferred manual-driving attempt (avoid re-entrancy deadlocks)
            self._emg_manual_ready_at = self.emg_started_at + 0.5
            self._emg_manual_attempted = False

            # Close progress popup and show completion
            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Emergency Game Ready",
                f"🚨 Emergency vehicle spawned on {road_name}!\n\n"
                f"📍 Location: Lane {lane} at {percent:.1f}%\n"
                f"🎯 Click 'Enter Manual Driving' to start the mission!",
                success=True
            )

            return True
        except Exception as e:
            self.logProxy.logger.error(f"create_emergency_game failed: {e}")
            # Close progress popup on error
            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Emergency Game Setup Failed",
                f"Failed to create emergency game: {str(e)}",
                success=False
            )
            return False
    def enter_manual_driving(self):
        """Attempt to switch Navigation to Manual driving on the emg_car (user clicks this)."""
        try:
            if not self.emg_active or self.emg_car is None:
                self.logProxy.logger.warning("enter_manual_driving: No active emergency car")
                return False
            
            mf = self.winRoadProxy.MainForm
            const = self.winRoadProxy.const
            
            # Use InitialNavigationState instead of NavigationState
            nav = mf.InitialNavigationState
            
            # Set navigation mode to drive
            nav.Mode = const._nmDrive
            
            # Configure drive parameters for manual driving
            try:
                dp = nav.DriveParameter
                if dp is None:
                    dp = com.DispatchEx('UCwinRoad.F8COMDriveParameterType')
                    nav.DriveParameter = dp
                
                dp.drivingMode = const._dmManual
                dp.driveWithCarParam = True  # Use the car parameter
                
                # Set vehicle-specific parameters
                try:
                    dp.Vehicle = self.emg_car
                except Exception:
                    pass

            except Exception as e:
                self.logProxy.logger.error(f"Failed to set drive parameters: {e}")
                return False
            
            # Set the road and vehicle
            try:
                nav.Road = self.emg_road
            except Exception as e:
                self.logProxy.logger.warning(f"Failed to set road: {e}")
            
            try:
                nav.NewCar = self.emg_car
            except Exception as e:
                self.logProxy.logger.warning(f"Failed to set new car: {e}")
            
            # Apply the navigation state using the correct method
            try:
                mf.NavigationState = nav
            except Exception:
                # Fallback: try to apply directly
                try:
                    mf.ApplyNavigationState(nav)
                except Exception:
                    pass
            
            # Try to bring UC-win/Road to foreground so key input goes there
            try:
                if hasattr(mf, 'Focus'):
                    mf.Focus()
                elif hasattr(mf, 'SetFocus'):
                    mf.SetFocus()
            except Exception:
                pass
            
            self.logProxy.logger.info("Manual driving mode activated")
            return True
            
        except Exception as e:
            self.logProxy.logger.error(f"enter_manual_driving failed: {e}")
            return False


    def _create_topmost_popup(self):
        """Create a Tkinter root window that stays on top, or use scenario window if available."""
        import tkinter as tk

        # Try to use the scenario window as parent for better z-order
        try:
            from . import handlers
            _ribbon = handlers._ribbon
            if hasattr(_ribbon, 'scenarios_window') and _ribbon.scenarios_window and _ribbon.scenarios_window.winfo_exists():
                # Use scenario window as parent for popups to stay on top of it
                return _ribbon.scenarios_window
        except Exception as e:
            pass

        # Fallback: create a new root window - but this can cause issues
        # Let's try using the main ribbon window instead
        try:
            from . import handlers
            _ribbon = handlers._ribbon
            if hasattr(_ribbon, 'tk_root') and _ribbon.tk_root and _ribbon.tk_root.winfo_exists():
                return _ribbon.tk_root
        except Exception as e:
            pass

        # Last resort: create a minimal root window (but this may cause issues)
        root = tk.Tk()
        root.withdraw()  # Hide the main window completely
        root.attributes('-topmost', True)
        root.lift()
        root.focus_force()
        # Try to position it off-screen to avoid conflicts
        root.geometry("+0+0")
        return root

    def show_progress_popup(self, title, message, duration_ms=None, auto_close=True):
        """Show a progress/status popup that stays on top."""
        try:
            import tkinter as tk
            from tkinter import ttk

            root = self._create_topmost_popup()
            popup = tk.Toplevel(root)
            popup.title(title)
            popup.geometry("400x150")
            popup.resizable(False, False)
            popup.attributes('-topmost', True)
            popup.lift()
            popup.focus_force()

            # Center the popup on screen
            popup.transient()  # Make it modal-like
            try:
                popup.grab_set()  # May fail if another app has grab
            except tk.TclError:
                # Another application has grab, continue without modal behavior
                pass

            # Main frame
            frame = ttk.Frame(popup, padding="20")
            frame.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))
            popup.columnconfigure(0, weight=1)
            popup.rowconfigure(0, weight=1)

            # Title
            ttk.Label(frame, text=title, font=("Arial", 12, "bold")).grid(row=0, column=0, pady=(0, 10))

            # Message
            ttk.Label(frame, text=message, wraplength=350).grid(row=1, column=0, pady=(0, 20))

            # Progress bar for operations that take time
            progress_var = tk.StringVar()
            progress_label = ttk.Label(frame, textvariable=progress_var)
            progress_label.grid(row=2, column=0, pady=(0, 10))

            # Auto-close if duration specified
            if auto_close and duration_ms:
                popup.after(duration_ms, popup.destroy)

            return popup, progress_var

        except Exception as e:
            self.logProxy.logger.error(f"Failed to show progress popup: {e}")
            return None, None

    def update_progress_popup(self, progress_var, message):
        """Update the progress message in a progress popup."""
        if progress_var:
            try:
                progress_var.set(message)
            except Exception as e:
                self.logProxy.logger.error(f"Failed to update progress: {e}")

    def show_operation_complete_popup(self, title, message, success=True):
        """Show a completion popup for operations."""
        try:
            import tkinter as tk
            from tkinter import ttk, messagebox

            root = self._create_topmost_popup()
            icon = "✅" if success else "❌"
            full_title = f"{icon} {title}"

            if success:
                messagebox.showinfo(full_title, message, parent=root)
            else:
                messagebox.showerror(full_title, message, parent=root)

        except Exception as e:
            self.logProxy.logger.error(f"Failed to show completion popup: {e}")

    def show_operation_progress_popup(self, operation_name, steps):
        """Show a detailed progress popup for multi-step operations."""
        try:
            import tkinter as tk
            from tkinter import ttk

            root = self._create_topmost_popup()
            popup = tk.Toplevel(root)
            popup.title(f"🔄 {operation_name}")
            popup.geometry("450x200")
            popup.resizable(False, False)
            popup.attributes('-topmost', True)
            popup.lift()
            popup.focus_force()
            popup.transient()
            try:
                popup.grab_set()  # May fail if another app has grab
            except tk.TclError:
                # Another application has grab, continue without modal behavior
                pass

            frame = ttk.Frame(popup, padding="20")
            frame.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))
            popup.columnconfigure(0, weight=1)
            popup.rowconfigure(0, weight=1)

            ttk.Label(frame, text=f"{operation_name}", font=("Arial", 12, "bold")).grid(row=0, column=0, pady=(0, 15))

            # Progress bar
            progress_var = tk.DoubleVar()
            progress_bar = ttk.Progressbar(frame, variable=progress_var, maximum=len(steps))
            progress_bar.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

            # Status label
            status_var = tk.StringVar(value="Initializing...")
            status_label = ttk.Label(frame, textvariable=status_var)
            status_label.grid(row=2, column=0, pady=(0, 15))

            # Step counter
            step_var = tk.StringVar(value=f"Step 0 of {len(steps)}")
            step_label = ttk.Label(frame, textvariable=step_var, font=("Arial", 9))
            step_label.grid(row=3, column=0)

            return popup, progress_var, status_var, step_var

        except Exception as e:
            self.logProxy.logger.error(f"Failed to create progress popup: {e}")
            return None, None, None, None

    def maybe_enter_manual_driving(self):
        try:
            if not getattr(self, 'emg_active', False) or self.emg_car is None:
                return
            if getattr(self, '_emg_manual_attempted', False):
                return
            if time.perf_counter() < getattr(self, '_emg_manual_ready_at', 0.0):
                return
            # One-shot attempt to switch to manual driving on the spawned car
            try:
                mf = self.winRoadProxy.MainForm
                nav = mf.InitialNavigationState
                nav.Mode = self.winRoadProxy.const._nmDrive
                try:
                    dp = nav.DriveParameter
                    dp.drivingMode = self.winRoadProxy.const._dmManual
                    dp.driveWithCarParam = True
                except Exception:
                    pass
                try:
                    nav.Road = self.emg_road
                except Exception:
                    pass
                try:
                    nav.NewCar = self.emg_car
                except Exception:
                    pass
                mf.NavigationState = nav
            except Exception:
                pass
            self._emg_manual_attempted = True
        except Exception:
            pass

            return False

    def poll_emergency_game(self):
        """Return dict with status; call from UI tick. Auto-detect manual driving mode."""
        try:
            if not self.emg_active or not self.emg_game_mode:
                return None
            
            # Auto-detect if user is manually driving any vehicle
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation
            driver = traffic.Driver
            driving = False
            current_car = None
            
            if driver is not None:
                try:
                    current_car = driver.CurrentCar
                    # Check if user is driving any car
                    driving = bool(current_car)
                    if driving:
                        self.logProxy.logger.info("User is manually driving a vehicle!")
                except Exception:
                    driving = False
            
            # Calculate elapsed time only if not arrived
            if not getattr(self, 'emg_arrived', False):
                elapsed = time.perf_counter() - (self.emg_started_at or time.perf_counter())
            else:
                elapsed = getattr(self, 'emg_final_time', 0.0)
            
            # arrival detection by distance threshold to ANY accident
            arrived = getattr(self, 'emg_arrived', False)
            if not arrived and driving and current_car:
                try:
                    pos = current_car.Position
                    arrival_threshold = 15.0  # Detection radius
                    
                    # Check distance to ALL accidents, not just the target
                    for road_name, accident_data in self.accidents.items():
                        if 'pos' in accident_data:
                            tgt = accident_data['pos']
                            dx = pos.X - tgt.X; dy = pos.Y - tgt.Y; dz = pos.Z - tgt.Z
                            dist = (dx*dx + dy*dy + dz*dz) ** 0.5

                            if dist < arrival_threshold:
                                arrived = True
                                # Store which accident was reached
                                self.emg_reached_accident = road_name
                                self.logProxy.logger.info(f"Player arrived at accident on {road_name}! Distance: {dist:.1f}m, Final time: {elapsed:.1f}s")
                                break
                    
                    if arrived and not getattr(self, 'emg_arrived', False):
                        # First time arriving - stop the timer and show completion popup
                        self.emg_arrived = True
                        self.emg_final_time = elapsed

                        # Show completion popup
                        self._show_completion_popup(elapsed)
                except Exception as e:
                    self.logProxy.logger.error(f"Arrival detection failed: {e}")
                    pass
            
            return {
                'driving': driving,
                'elapsed': elapsed,
                'arrived': arrived,
                'current_car': current_car,
                'player_name': self.emg_player_name
            }
        except Exception as e:
            self.logProxy.logger.error(f"poll_emergency_game failed: {e}")
            return None

    def reset_emergency_game(self):
        """Reset the emergency game state."""
        try:
            self.emg_active = False
            self.emg_car = None
            self.emg_target_pos = None
            self.emg_started_at = None
            self.emg_lane = 1
            self.emg_road = None
            self.emg_arrived = False
            self.emg_final_time = 0.0
            self.emg_player_name = None
            self.emg_game_mode = False
            self.emg_reached_accident = None
            self._emg_manual_attempted = False
            self._emg_manual_ready_at = 0.0

            self.logProxy.logger.info("Emergency game reset")
            return True
        except Exception as e:
            self.logProxy.logger.error(f"reset_emergency_game failed: {e}")
            return False

    def start_emergency_game(self):
        """Start the emergency game - check if user is driving and guide them."""
        try:
            # Check if user is currently driving
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation
            driver = traffic.Driver
            is_driving = False

            if driver is not None:
                try:
                    current_car = driver.CurrentCar
                    is_driving = bool(current_car)
                except Exception:
                    is_driving = False

            if not is_driving:
                # User is not driving - show simple message
                try:
                    from tkinter import messagebox
                    # Try to get the scenario window for proper parenting
                    parent = None
                    try:
                        from . import handlers
                        _ribbon = handlers._ribbon
                        if hasattr(_ribbon, 'scenarios_window') and _ribbon.scenarios_window and _ribbon.scenarios_window.winfo_exists():
                            parent = _ribbon.scenarios_window
                    except:
                        pass

                    messagebox.showinfo(
                        "🚗 Emergency Game - Setup Required",
                        "To start the emergency game, you need to be driving a vehicle.\n\n"
                        "Please:\n"
                        "1. Go to the 'Home' tab in UC-win/Road\n"
                        "2. Click 'Drive' to enter driving mode\n"
                        "3. Select any vehicle to drive\n"
                        "4. Then click 'Start Emergency Game' again\n\n"
                        "Use the 2D map to navigate to the accident scene!",
                        parent=parent
                    )
                except Exception as e:
                    self.logProxy.logger.error(f"Failed to show driving guidance message: {e}")
                return False
            else:
                # User is driving - show game start popup
                return self._show_game_start_popup()

        except Exception as e:
            self.logProxy.logger.error(f"start_emergency_game failed: {e}")
            return False





    def _show_game_start_popup(self):
        """Show popup to start the emergency game with name input."""
        self.logProxy.logger.info("_show_game_start_popup called")
        try:
            import tkinter as tk
            from tkinter import ttk, messagebox

            # Create a popup that stays on top (using scenario window as parent)
            root = self._create_topmost_popup()

            # Create custom name input dialog as proper child of scenario window
            dialog = tk.Toplevel(root)
            dialog.title("🚑 Emergency Game - Player Name")
            dialog.geometry("420x180")
            dialog.resizable(False, False)
            dialog.attributes('-topmost', True)
            dialog.transient(root)
            try:
                dialog.grab_set()
            except tk.TclError:
                # Another application has grab, continue without modal behavior
                # This is normal and expected - dialog still works fine
                pass

            # Center on parent window
            try:
                dialog.geometry("+{}+{}".format(
                    root.winfo_x() + (root.winfo_width() // 2) - 210,
                    root.winfo_y() + (root.winfo_height() // 2) - 90
                ))
            except tk.TclError:
                pass  # Use default positioning if centering fails

            # Content frame
            frame = ttk.Frame(dialog, padding="20")
            frame.pack(fill=tk.BOTH, expand=True)

            # Title
            ttk.Label(frame, text="Let's drive to the accident scene!",
                     font=("Arial", 11, "bold")).pack(pady=(0, 10))

            # Name input
            ttk.Label(frame, text="Enter your name:").pack(pady=(0, 5))

            name_var = tk.StringVar(value="Player")
            name_entry = ttk.Entry(frame, textvariable=name_var, width=30, justify='center')
            name_entry.pack(pady=(0, 15))
            name_entry.select_range(0, tk.END)
            name_entry.focus()

            result = {'name': None}

            def on_ok():
                player_name = name_var.get().strip()
                if player_name:
                    # Start the game
                    result['name'] = player_name
                    dialog.destroy()

                    # Initialize game state
                    self.emg_player_name = player_name
                    self.emg_game_mode = True
                    self.emg_active = True
                    self.emg_started_at = time.perf_counter()

                    # Find the nearest accident
                    nearest_accident = self._find_nearest_accident()
                    if nearest_accident:
                        self.emg_target_pos = nearest_accident['pos']
                        self.logProxy.logger.info(f"Emergency game started for {player_name}, target: {nearest_accident['road']}")
                    else:
                        self.logProxy.logger.warning("No accidents found for emergency game")

                    # Show start confirmation
                    try:
                        messagebox.showinfo(
                            "🚑 Emergency Game Started!",
                            f"Welcome {player_name}!\n\n"
                            "Your mission: Drive to the accident scene!\n"
                            "The timer has started. Good luck!",
                            parent=root
                        )
                    except Exception as e:
                        self.logProxy.logger.warning(f"Could not show success message: {e}")

                    # Only destroy if it's not the scenario window (which we shouldn't destroy)
                    try:
                        # Check if this is the scenario window by comparing references
                        from . import handlers
                        _ribbon = handlers._ribbon
                        if not (hasattr(_ribbon, 'scenarios_window') and root is _ribbon.scenarios_window):
                            root.destroy()
                    except:
                        pass  # Safe to ignore destroy errors
                else:
                    # Empty name, show error
                    try:
                        messagebox.showerror(
                            "Invalid Name",
                            "Please enter a valid name to start the game.",
                            parent=dialog
                        )
                    except Exception as e:
                        self.logProxy.logger.warning(f"Could not show error message: {e}")

            def on_cancel():
                result['name'] = None
                dialog.destroy()
                try:
                    # Check if this is the scenario window by comparing references
                    from . import handlers
                    _ribbon = handlers._ribbon
                    if not (hasattr(_ribbon, 'scenarios_window') and root is _ribbon.scenarios_window):
                        root.destroy()
                except:
                    pass

            # Buttons
            btn_frame = ttk.Frame(frame)
            btn_frame.pack(pady=(0, 10))

            ttk.Button(btn_frame, text="Start Game", command=on_ok).pack(side=tk.LEFT, padx=5)
            ttk.Button(btn_frame, text="Cancel", command=on_cancel).pack(side=tk.LEFT, padx=5)

            # Bind Enter and Escape keys
            name_entry.bind('<Return>', lambda e: on_ok())
            dialog.bind('<Escape>', lambda e: on_cancel())

            # Force dialog to be visible and focused - try multiple approaches
            dialog.deiconify()
            dialog.lift()
            dialog.focus_force()
            dialog.attributes('-topmost', True)  # Make sure it's always on top
            dialog.grab_set()  # Try to grab focus again after making visible
            self.logProxy.logger.info("Dialog deiconified, lifted, focused, and set as topmost")

            # Additional visibility attempts
            try:
                dialog.after(100, lambda: dialog.lift())  # Lift again after a short delay
                dialog.after(200, lambda: dialog.focus_force())  # Force focus again
                self.logProxy.logger.info("Scheduled additional visibility attempts")
            except Exception as e:
                self.logProxy.logger.warning(f"Could not schedule additional visibility: {e}")

            # Don't wait for dialog - let it work asynchronously
            # The game initialization will happen in the on_ok callback
            self.logProxy.logger.info("Dialog setup complete, returning from _show_game_start_popup")
            return True

        except Exception as e:
            self.logProxy.logger.error(f"_show_game_start_popup failed: {e}")
            return False

    def _find_nearest_accident(self):
        """Find the nearest accident to the current camera position."""
        try:
            if not self.accidents:
                return None
            
            # Get current camera position
            cam = self.winRoadProxy.MainForm.MainCamera
            cam_pos = cam.MainCameraState.eye
            
            nearest = None
            min_distance = float('inf')
            
            for road_name, accident in self.accidents.items():
                if accident and 'pos' in accident:
                    pos = accident['pos']
                    dx = pos.X - cam_pos.X
                    dy = pos.Y - cam_pos.Y
                    dz = pos.Z - cam_pos.Z
                    distance = (dx*dx + dy*dy + dz*dz) ** 0.5
                    
                    if distance < min_distance:
                        min_distance = distance
                        nearest = {
                            'road': road_name,
                            'pos': pos,
                            'distance': distance
                        }
            
            return nearest
        except Exception as e:
            self.logProxy.logger.error(f"_find_nearest_accident failed: {e}")
            return None

    def _show_completion_popup(self, time_taken):
        """Show popup when player reaches the accident scene."""
        try:
            from tkinter import messagebox
            
            # Add to rankings
            if self.emg_player_name:
                self.emg_rankings.append({
                    'name': self.emg_player_name,
                    'time': time_taken,
                    'timestamp': time.time()
                })
                # Sort by time (best first)
                self.emg_rankings.sort(key=lambda x: x['time'])
            
            # Create completion message
            minutes = int(time_taken // 60)
            seconds = int(time_taken % 60)
            time_str = f"{minutes:02d}:{seconds:02d}"
            
            # Create rankings text
            rankings_text = "🏆 Rankings:\n\n"
            for i, ranking in enumerate(self.emg_rankings[:5]):  # Top 5
                medal = "🥇" if i == 0 else "🥈" if i == 1 else "🥉" if i == 2 else "🏅"
                rank_min = int(ranking['time'] // 60)
                rank_sec = int(ranking['time'] % 60)
                rank_time = f"{rank_min:02d}:{rank_sec:02d}"
                rankings_text += f"{medal} {i+1}. {ranking['name']}: {rank_time}\n"
            
            # Create a popup that stays on top
            root = self._create_topmost_popup()
            
            # Get the accident that was reached
            reached_accident = getattr(self, 'emg_reached_accident', 'Unknown')
            
            messagebox.showinfo(
                "🎯 Emergency Response Complete!",
                f"Congratulations {self.emg_player_name}!\n\n"
                f"⏱️ Your time: {time_str}\n"
                f"📍 Reached: {reached_accident}\n\n"
                f"{rankings_text}\n"
                "Click 'Reset Emergency Game' to play again!",
                parent=root
            )

            # Only destroy if it's not the scenario window (which we shouldn't destroy)
            try:
                # Check if this is the scenario window by comparing references
                from . import handlers
                _ribbon = handlers._ribbon
                if not (hasattr(_ribbon, 'scenarios_window') and root is _ribbon.scenarios_window):
                    root.destroy()
            except:
                pass  # Safe to ignore destroy errors
            
            # Reset game state
            self.emg_game_mode = False
            self.emg_active = False
            
        except Exception as e:
            self.logProxy.logger.error(f"_show_completion_popup failed: {e}")


    # Accident and emergency helpers
    def has_accident(self, road_name: str) -> bool:
        return road_name in self.accidents

    def get_accident_pos(self, road_name: str):
        acc = self.accidents.get(road_name)
        return acc.get('pos') if acc else None

    def create_accident(self, road_name: str, lane: int | None = None, distance: float | None = None, length: float = 50.0):
        popup, progress_var = self.show_progress_popup(
            "🚧 Creating Accident",
            f"Setting up accident scene on {road_name}...",
            duration_ms=None,
            auto_close=False
        )

        try:
            prj = self.winRoadProxy.Project
            const = self.winRoadProxy.const

            self.update_progress_popup(progress_var, "Finding road location...")
            # Resolve road
            road = None
            for i in range(prj.RoadsCount):
                r = prj.Road(i)
                if r.Name == road_name:
                    road = r; break
            if not road:
                self.logProxy.logger.error(f"create_accident: road not found: {road_name}")
                return

            road_len = getattr(road, 'Length', 200.0)
            # Choose defaults
            if distance is None:
                distance = float(road_len * 0.6)
            if lane is None:
                lane = 1

            self.update_progress_popup(progress_var, "Loading vehicle models...")
            # Place a stalled vehicle as visual blockage
            # Pick any vehicle model (cached)
            model = self._accident_blocker_model
            if model is None:
                for i in range(prj.ThreeDModelsCount):
                    m = prj.ThreeDModel(i)
                    if m.ModelType == const._VehicleModel:
                        model = m
                        self._accident_blocker_model = m
                        break
            if model is None:
                self.logProxy.logger.error("create_accident: no vehicle model available")
                return

            self.update_progress_popup(progress_var, "Placing accident vehicle...")
            vptype = com.DispatchEx('UCwinRoad.F8COMVehiclePlacementType')
            vptype.IsForward = True
            vptype.Lane = int(lane)
            vptype.Distance = float(distance)
            traffic = self.winRoadProxy.ApplicationServices.SimulationCore.TrafficSimulation

            blocker = traffic.AddNewVehicle(model, road, vptype)
            if blocker:
                # Keep it stopped by enabling parking brake and zero speed
                try:
                    blocker.ParkingBrake = True
                except Exception:
                    pass
                blocker.SetSpeed(const._KiloMeterPerHour, 0.0)
                self.vehicles.append(blocker)

            # Place two cones inline with the vehicle: one ahead and one behind, centered in lane
            objects = []
            try:
                # Get lane curve for exact lane-center placement
                lane_curve = road.SearchRoadLane(distance, True, int(lane))
                ld = self.winRoadProxy.const._ldRoad
                # Cache a cone model once
                if self._cone_model is None:
                    for i in range(prj.ThreeDModelsCount):
                        m = prj.ThreeDModel(i)
                        if 'cone' in m.Name.lower():
                            self._cone_model = m
                            break
                def _cone_at(offset_m: float):
                    inst = self._acquire_cone()
                    if inst:
                        d = min(road_len, max(0.0, distance + offset_m))
                        pos = lane_curve.GetPosition(d, ld)
                        dirv = lane_curve.GetDirection(d, ld)
                        inst.Position = pos
                        try:
                            import math
                            yaw = math.atan2(dirv.X, dirv.Z)
                            inst.YawAngle = yaw
                        except Exception:
                            pass
                        objects.append(inst)
                        return True
                    return False
                _cone_at(+3.0)
                _cone_at(-3.0)
            except Exception:
                pass


            # Remember position
            pos = road.GetPositionAt(distance)
            self.accidents[road_name] = {
                'pos': pos,
                'lane': lane,
                'distance': distance,
                'blocker': blocker,
                'objects': objects,
            }

            # Skip lane closure for now to avoid heavy operations during accident spawn

            self.logProxy.logger.info(f"Accident created on '{road_name}' lane {lane} at {distance:.1f} m")

            # Close progress popup and show completion
            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Accident Created",
                f"🚧 Accident scene created on {road_name}!\n\n"
                f"📍 Location: Lane {lane} at {distance:.1f}m\n"
                f"🚗 Blocked vehicle placed with warning cones",
                success=True
            )

        except Exception as e:
            self.logProxy.logger.error(f"create_accident failed: {e}")
            # Close progress popup on error
            if popup:
                popup.destroy()
            self.show_operation_complete_popup(
                "Accident Creation Failed",
                f"Failed to create accident scene: {str(e)}",
                success=False
            )

    def create_accident_at_percent(self, road_name: str, percent: float | None = None, lane: int | None = None, length: float = 50.0):
        try:
            prj = self.winRoadProxy.Project
            # Resolve road and length
            road = None
            for i in range(prj.RoadsCount):
                r = prj.Road(i)
                if r.Name == road_name:
                    road = r; break
            if not road:
                self.logProxy.logger.error(f"create_accident_at_percent: road not found: {road_name}")
                return
            road_len = max(0.0, float(getattr(road, 'Length', 0.0)))
            if road_len <= 0.0:
                self.logProxy.logger.error("create_accident_at_percent: road length is zero")
                return
            p = 60.0 if percent is None else max(0.0, min(100.0, float(percent)))
            distance = (p / 100.0) * road_len
            self.create_accident(road_name, lane=lane, distance=distance, length=length)
        except Exception as e:
            self.logProxy.logger.error(f"create_accident_at_percent failed: {e}")

