//! Client cursor surface on `Windows`: a surface a client attaches through
//! `wl_pointer.set_cursor`, adopted here and imported/composed on the pointer
//! path only (it owns no layout).

use super::*;

impl Windows {
    /// Adopt the surface a client attached through `wl_pointer.set_cursor`.
    ///
    /// The buffers are taken here as well as on commit: a client may attach and
    /// commit before it issues `set_cursor`, and that buffer would otherwise sit
    /// in the cached state with nobody to claim it.
    pub fn set_cursor_surface(&mut self, surface: Option<WlSurface>) {
        if self.cursor_surface == surface {
            return;
        }
        if let Some(surface) = &surface {
            crate::windows::window::take_surface_tree_buffers(surface);
        }
        self.cursor_surface = surface;
        self.dirty = true;
    }
}

/// Import the client cursor surface's buffers into textures.
///
/// Mirrors the window path without its texture cache, presentation feedback or
/// per-window scale: a cursor is composed straight from the per-surface
/// `texture`, and `location` carries the subsurface offsets.
pub(super) fn import_cursor_surface(renderer: &mut GlesRenderer, surface: &WlSurface) {
    compositor::with_surface_tree_upward(
        surface,
        Point::from((0, 0)),
        |_, surface_data, location| {
            let Some(data) = surface_data.data_map.get::<RefCell<ForgeSurfaceData>>() else {
                return TraversalAction::SkipChildren;
            };
            let mut data = data.borrow_mut();

            let mut location = *location;
            if surface_data.role == Some("subsurface") {
                let mut subsurface = surface_data.cached_state.get::<SubsurfaceCachedState>();
                location += subsurface.current().location;
            }
            data.location = location;

            TraversalAction::DoChildren(location)
        },
        |surface, surface_data, _| {
            let Some(data) = surface_data.data_map.get::<RefCell<ForgeSurfaceData>>() else {
                return;
            };
            let mut data = data.borrow_mut();

            if let Some(texture) = &data.texture {
                texture.set_location(data.location);
                return;
            }

            let Some(buffer) = &data.buffer else { return };
            let damage = data.damage.buffer();
            match renderer.import_buffer(buffer, Some(surface_data), damage) {
                Some(Ok(tex)) => {
                    data.buffer = None;
                    let texture = Texture::from_surface(tex, None, data.location, &data, surface);
                    data.texture = Some(RenderTexture::new(texture));
                    data.damage.clear();
                }
                _ => {
                    data.buffer = None;
                }
            }
        },
        |_, _, _| true,
    );
}

/// Push the client cursor surface tree as elements anchored at `origin`.
///
/// `Kind::Cursor` is only claimed for a lone surface: with subsurfaces the
/// hardware-plane router would take one arbitrary member and leave the rest on
/// the primary, so the whole tree stays composited.
pub(super) fn push_cursor_surface(
    textures: &mut Vec<ForgeElement>,
    surface: &WlSurface,
    origin: Point<i32, Physical>,
    scale: f64,
) {
    let mut items: Vec<(RenderTexture, Point<i32, Physical>)> = Vec::new();

    compositor::with_surface_tree_upward(
        surface,
        (),
        |_, _, _| TraversalAction::DoChildren(()),
        |_, surface_data, _| {
            let Some(data) = surface_data.data_map.get::<RefCell<ForgeSurfaceData>>() else {
                return;
            };
            let data = data.borrow();
            let Some(texture) = &data.texture else { return };
            let loc = origin + data.location.to_physical_precise_round(scale);
            items.push((texture.clone(), loc));
        },
        |_, _, _| true,
    );

    let single = items.len() == 1;
    for (texture, loc) in items {
        let mut bounds = texture.geometry(scale.into());
        bounds.loc = loc;
        if single {
            ForgeElement::add_cursor(textures, texture, loc, bounds, None, scale);
        } else {
            ForgeElement::add_element(textures, texture, loc, bounds, None, scale);
        }
    }
}
