//! App-switcher backing: the Alt+Tab / carousel surface. Offscreen thumbnail
//! snapshots, the entry list, and object-id-keyed activation. Operates on
//! `Windows`.

use super::*;

impl Windows {
    /// Capture an offscreen thumbnail snapshot for every layout window, so the
    /// switcher composites plain 2D textures instead of client dmabufs (which
    /// scale to black on some GPUs). Each call is a no-op for windows whose
    /// snapshot is still valid; it re-captures only after the window committed
    /// new content, so video playback stays live in the carousel.
    pub fn snapshot_switcher_thumbnails(&mut self, renderer: &mut GlesRenderer) {
        let scale = self.canvas.scale();
        for layout in self.layouts.layouts() {
            for window in layout.windows() {
                window.borrow_mut().snapshot_thumbnail(renderer, scale);
            }
        }
    }

    /// Build the flat window list used by the Alt+Tab switcher, sorted MRU.
    pub fn app_switcher_entries(&mut self) -> Vec<crate::app_switcher::WindowEntry> {
        let mut all: Vec<crate::app_switcher::WindowEntry> = Vec::new();
        for layout in self.layouts.layouts() {
            for window in layout.windows() {
                let w = window.borrow();
                // A client that died without destroying its toplevel leaves
                // a window nothing can activate; listing it gives the user a
                // tile that does nothing (Electron apps whose launcher exits
                // hit this).
                if !w.alive() {
                    continue;
                }
                let wl = w.surface.surface();
                let surface_id = wl.id().protocol_id();
                let app_id = w.app_id.clone().unwrap_or_default();
                let title = smithay::wayland::compositor::with_states(wl, |states| {
                    states
                        .data_map
                        .get::<smithay::wayland::shell::xdg::XdgToplevelSurfaceData>()
                        .and_then(|d| d.lock().ok())
                        .and_then(|d| d.title.clone())
                        .unwrap_or_default()
                });
                let thumbnail  = w.switcher_thumbnail();
                let thumb_size = w.switcher_thumbnail_size();
                let object_id  = Some(wl.id());
                let output = {
                    let owner = self.owner_output(w.output_rectangle());
                    (!self.is_primary(owner)).then(|| owner.smithay_output().name())
                };
                tracing::debug!(
                    "app_switcher: wayland window surface_id={} app_id={:?} title={:?} minimized={}",
                    surface_id, app_id, title, w.minimized
                );
                all.push(crate::app_switcher::WindowEntry {
                    surface_id, object_id, app_id, title, thumbnail, thumb_size, output,
                });
            }
        }

        for entry in self.x11_windows.entries() {
            if entry.surface.is_override_redirect() { continue; }
            let Some(wl) = entry.surface.wl_surface() else { continue };
            let surface_id = wl.id().protocol_id();
            let title = entry.surface.title();
            let app_id = entry.surface.class();
            let title = if title.is_empty() { app_id.clone() } else { title };
            tracing::debug!(
                "app_switcher: x11 window surface_id={} app_id={:?} title={:?}",
                surface_id, app_id, title
            );
            all.push(crate::app_switcher::WindowEntry {
                surface_id,
                object_id: Some(wl.id()),
                app_id,
                title,
                thumbnail: entry.thumbnail_texture(),
                thumb_size: entry.thumbnail_size(),
                output: {
                    let owner = self.owner_output(entry.surface.geometry());
                    (!self.is_primary(owner)).then(|| owner.smithay_output().name())
                },
            });
        }

        tracing::debug!("app_switcher: {} total entries before sort", all.len());

        // Stable alphabetical order: app_id, then title, then surface_id
        // as a final tie-break. Keeps the layout deterministic across
        // re-renders so users build muscle memory for tile positions.
        all.sort_by(|a, b| {
            a.app_id.cmp(&b.app_id)
                .then_with(|| a.title.cmp(&b.title))
                .then_with(|| a.surface_id.cmp(&b.surface_id))
        });
        all
    }
    /// Activate a window by the full `ObjectId` of its `wl_surface`.
    /// `protocol_id` is per-client and collides across clients, so two
    /// distinct windows can share the same `u32` — Alt+Tab would then
    /// activate whichever appears first in layout iteration order rather
    /// than the one the user selected; the `ObjectId` is unique.
    pub fn activate_by_object_id(
        &mut self,
        oid: &smithay::reexports::wayland_server::backend::ObjectId,
    ) {
        let target: Option<(usize, Rc<RefCell<Window>>)> = self
            .layouts
            .layouts()
            .iter()
            .enumerate()
            .find_map(|(ws_idx, layout)| {
                layout
                    .windows()
                    .find_map(|window| {
                        (&window.borrow().surface.surface().id() == oid)
                            .then(|| (ws_idx, window.clone()))
                    })
            });
        let Some((ws_idx, window_rc)) = target else { return };

        let keep_fullscreen = self.keep_or_unfullscreen(&window_rc);
        if Some(ws_idx) != self.active_workspace() {
            self.switch_workspace(ws_idx);
        }
        window_rc.borrow_mut().minimized = false;
        let surface = window_rc.borrow().surface.surface().clone();
        self.raise(&surface);
        self.set_focus(Some(FocusTarget::Layout(Rc::downgrade(&window_rc))));
        if !keep_fullscreen {
            self.set_view(View::Workspace);
        }
        self.dirty = true;
    }

    pub fn activate_x11_by_object_id(
        &mut self,
        oid: &smithay::reexports::wayland_server::backend::ObjectId,
    ) -> Option<smithay::xwayland::X11Surface> {
        let wl = self.x11_windows.entries().iter().find_map(|e| {
            let s = e.surface.wl_surface()?;
            (s.id() == *oid).then_some(s)
        })?;
        let surface = self.x11_windows.find(&wl)?.clone();
        self.unfullscreen(None);
        self.x11_windows.set_minimized(&wl, false);
        self.set_focus(Some(FocusTarget::X11(wl)));
        self.set_view(View::Workspace);
        self.dirty = true;
        Some(surface)
    }

    /// Return the switcher thumbnail for the window whose Wayland surface
    /// protocol ID matches `window_id`, or `None` if not found.
    pub fn thumbnail_for_id(&self, window_id: u64) -> Option<crate::drawing::RenderTexture> {
        for layout in self.layouts.layouts() {
            for window in layout.windows() {
                let w = window.borrow();
                if w.surface.surface().id().protocol_id() as u64 == window_id {
                    return w.switcher_thumbnail();
                }
            }
        }
        for entry in self.x11_windows.entries() {
            if let Some(wl) = entry.surface.wl_surface() {
                if wl.id().protocol_id() as u64 == window_id {
                    return entry.thumbnail_texture();
                }
            }
        }
        None
    }
}
