use {
    crate::{
        allocator::{BO_USE_RENDERING, BufferObject, BufferUsage},
        async_engine::{Phase, SpawnedFuture},
        cmm::{cmm_manager::ColorManager, cmm_render_intent::RenderIntent},
        cursor::KnownCursor,
        fixed::Fixed,
        format::ARGB8888,
        gfx_api::{
            AcquireSync, AlphaMode, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync,
            needs_render_usage,
        },
        globals::GlobalName,
        ifs::zwlr_layer_shell_v1::OVERLAY,
        portal::{
            ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
            ptl_text::{self, TextMeasurement},
        },
        renderer::renderer_base::RendererBase,
        scale::Scale,
        theme::Color,
        utils::{
            asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
            errorfmt::ErrorFmt, hash_map_ext::HashMapExt, rc_eq::rc_eq,
        },
        wire::{
            ZwpLinuxBufferParamsV1Id, wp_fractional_scale_v1::PreferredScale,
            zwlr_layer_surface_v1::Configure,
        },
        wl_usr::usr_ifs::{
            usr_linux_buffer_params::{UsrLinuxBufferParams, UsrLinuxBufferParamsOwner},
            usr_wl_buffer::{UsrWlBuffer, UsrWlBufferOwner},
            usr_wl_callback::UsrWlCallbackOwner,
            usr_wl_surface::UsrWlSurface,
            usr_wlr_layer_surface::{UsrWlrLayerSurface, UsrWlrLayerSurfaceOwner},
            usr_wp_fractional_scale::{UsrWpFractionalScale, UsrWpFractionalScaleOwner},
            usr_wp_viewport::UsrWpViewport,
        },
    },
    ahash::AHashSet,
    std::{
        cell::{Cell, RefCell},
        ops::Deref,
        rc::Rc,
        sync::Arc,
    },
};

#[derive(Default)]
pub struct GuiElementData {
    pub x: Cell<f32>,
    pub y: Cell<f32>,
    pub width: Cell<f32>,
    pub height: Cell<f32>,
}

pub trait GuiElement {
    fn data(&self) -> &GuiElementData;
    fn layout(
        &self,
        ctx: &Rc<dyn GfxContext>,
        scale: f32,
        max_width: f32,
        max_height: f32,
    ) -> (f32, f32);
    fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32);
    fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>>;

    fn hover_cursor(&self) -> KnownCursor {
        KnownCursor::Default
    }

    fn button(&self, seat: &PortalSeat, button: u32, state: u32) {
        let _ = seat;
        let _ = button;
        let _ = state;
    }

    fn hover(&self, seat: &PortalSeat, hover: bool) -> bool {
        let _ = seat;
        let _ = hover;
        false
    }

    fn destroy(&self) {}
}

#[derive(Copy, Clone, Debug, Default)]
pub struct ButtonExtents {
    pub width: f32,
    pub height: f32,
    pub tex_off_x: f32,
    pub tex_off_y: f32,
}

pub fn button_extents(
    msmt: &TextMeasurement,
    scale: f32,
    padding: f32,
    border: f32,
) -> ButtonExtents {
    let above_baseline_height =
        (msmt.baseline - msmt.ink_rect.y1().max(msmt.logical_rect.y1())) as f32 / scale;
    let height = above_baseline_height + 2.0 * (padding + border);
    let width = msmt.ink_rect.width() as f32 / scale + 2.0 * (padding + border);
    let tex_off_x = padding + border;
    // let tex_off_y = ((msmt.ink_rect.y1() - msmt.logical_rect.y1()) as f64 / scale).round() as i32 + padding + border;
    let tex_off_y = padding + border;
    ButtonExtents {
        width,
        height,
        tex_off_x,
        tex_off_y,
    }
}

pub struct Button {
    pub data: GuiElementData,
    pub tex_off_x: Cell<f32>,
    pub tex_off_y: Cell<f32>,
    pub hover: RefCell<AHashSet<GlobalName>>,
    pub padding: Cell<f32>,
    pub border: Cell<f32>,
    pub border_color: Cell<Color>,
    pub bg_color: Cell<Color>,
    pub bg_hover_color: Cell<Color>,
    pub text: RefCell<String>,
    pub font: Arc<String>,
    pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
    pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
}

pub trait ButtonOwner {
    fn button(&self, seat: &PortalSeat, button: u32, state: u32);
}

impl Default for Button {
    fn default() -> Self {
        Self {
            data: Default::default(),
            tex_off_x: Cell::new(0.0),
            tex_off_y: Cell::new(0.0),
            hover: Default::default(),
            padding: Default::default(),
            border: Default::default(),
            border_color: Cell::new(Color::from_gray_srgb(0)),
            bg_color: Cell::new(Color::from_gray_srgb(255)),
            bg_hover_color: Cell::new(Color::from_gray_srgb(255)),
            text: Default::default(),
            font: Arc::new(DEFAULT_FONT.to_string()),
            tex: Default::default(),
            owner: Default::default(),
        }
    }
}

impl GuiElement for Button {
    fn hover_cursor(&self) -> KnownCursor {
        KnownCursor::Pointer
    }

    fn data(&self) -> &GuiElementData {
        &self.data
    }

    fn layout(
        &self,
        ctx: &Rc<dyn GfxContext>,
        scale: f32,
        _max_width: f32,
        _max_height: f32,
    ) -> (f32, f32) {
        let text = self.text.borrow_mut();
        let tex = ptl_text::render(
            ctx,
            None,
            &self.font,
            &text,
            Color::from_gray_srgb(0),
            Some(scale as _),
            true,
        );
        let (tex, msmt) = match tex {
            Some((a, b)) => (Some(a), Some(b)),
            _ => (None, None),
        };
        let extents = match msmt {
            Some(m) => button_extents(&m, scale, self.padding.get(), self.border.get()),
            _ => Default::default(),
        };
        self.tex.set(tex);
        self.tex_off_x.set(extents.tex_off_x);
        self.tex_off_y.set(extents.tex_off_y);
        (extents.width, extents.height)
    }

    fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x1: f32, y1: f32) {
        let srgb_srgb = color_manager.srgb_gamma22();
        let srgb = &srgb_srgb.linear;
        let x2 = x1 + self.data.width.get();
        let y2 = y1 + self.data.height.get();
        let border = self.border.get();
        {
            let rects = [
                (x1, y1, x2, y1 + border),
                (x1, y2 - border, x2, y2),
                (x1, y1 + border, x1 + border, y2 - border),
                (x2 - border, y1 + border, x2, y2 - border),
            ];
            r.fill_boxes_f(
                &rects,
                &self.border_color.get(),
                srgb,
                RenderIntent::Perceptual,
            );
        }
        {
            let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)];
            let color = match self.hover.borrow_mut().is_empty() {
                true => self.bg_color.get(),
                false => self.bg_hover_color.get(),
            };
            r.fill_boxes_f(&rects, &color, srgb, RenderIntent::Perceptual);
        }
        if let Some(tex) = self.tex.get() {
            let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
            r.render_texture(
                &tex,
                None,
                tx.round() as _,
                ty.round() as _,
                None,
                None,
                r.scale(),
                None,
                None,
                AcquireSync::None,
                ReleaseSync::None,
                false,
                srgb_srgb,
                RenderIntent::Perceptual,
                AlphaMode::PremultipliedElectrical,
            );
        }
    }

    fn child_at(&self, _x: f32, _y: f32) -> Option<Rc<dyn GuiElement>> {
        None
    }

    fn hover(&self, seat: &PortalSeat, hover: bool) -> bool {
        let ret;
        let mut set = self.hover.borrow_mut();
        if hover {
            ret = set.is_empty();
            set.insert(seat.global_id);
        } else {
            set.remove(&seat.global_id);
            ret = set.is_empty();
        }
        ret
    }

    fn destroy(&self) {
        self.owner.take();
    }

    fn button(&self, seat: &PortalSeat, button: u32, state: u32) {
        if let Some(owner) = self.owner.get() {
            owner.button(seat, button, state);
        }
    }
}

const DEFAULT_FONT: &str = "sans-serif 16";

pub struct Label {
    pub data: GuiElementData,
    pub font: Arc<String>,
    pub text: RefCell<String>,
    pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
}

impl Default for Label {
    fn default() -> Self {
        Self {
            data: Default::default(),
            font: Arc::new(DEFAULT_FONT.into()),
            text: RefCell::new("".to_string()),
            tex: Default::default(),
        }
    }
}

impl GuiElement for Label {
    fn data(&self) -> &GuiElementData {
        &self.data
    }

    fn layout(
        &self,
        ctx: &Rc<dyn GfxContext>,
        scale: f32,
        _max_width: f32,
        _max_height: f32,
    ) -> (f32, f32) {
        let text = self.text.borrow_mut();
        let tex = ptl_text::render(
            ctx,
            None,
            &self.font,
            &text,
            Color::from_gray_srgb(255),
            Some(scale as _),
            false,
        );
        let (tex, width, height) = match tex {
            Some((t, _)) => {
                let (width, height) = t.size();
                (Some(t.clone()), width, height)
            }
            _ => (None, 0, 0),
        };
        self.tex.set(tex);
        (width as f32 / scale, height as f32 / scale)
    }

    fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
        if let Some(tex) = self.tex.get() {
            let (tx, ty) = r.scale_point_f(x, y);
            r.render_texture(
                &tex,
                None,
                tx.round() as _,
                ty.round() as _,
                None,
                None,
                r.scale(),
                None,
                None,
                AcquireSync::None,
                ReleaseSync::None,
                false,
                color_manager.srgb_gamma22(),
                RenderIntent::Perceptual,
                AlphaMode::PremultipliedElectrical,
            );
        }
    }

    fn child_at(&self, _x: f32, _y: f32) -> Option<Rc<dyn GuiElement>> {
        None
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub enum Align {
    #[expect(dead_code)]
    Left,
    #[default]
    Center,
    #[expect(dead_code)]
    Right,
}

#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub enum Orientation {
    #[default]
    Horizontal,
    Vertical,
}

#[derive(Default)]
pub struct Flow {
    pub data: GuiElementData,
    pub in_margin: Cell<f32>,
    pub cross_margin: Cell<f32>,
    pub orientation: Cell<Orientation>,
    pub cross_align: Cell<Align>,
    pub elements: RefCell<Vec<Rc<dyn GuiElement>>>,
}

impl GuiElement for Flow {
    fn data(&self) -> &GuiElementData {
        &self.data
    }

    fn layout(
        &self,
        ctx: &Rc<dyn GfxContext>,
        scale: f32,
        max_width: f32,
        max_height: f32,
    ) -> (f32, f32) {
        let elements = self.elements.borrow_mut();
        let orientation = self.orientation.get();
        let (max_in_size, _max_cross_size) = match orientation {
            Orientation::Horizontal => (max_width, max_height),
            Orientation::Vertical => (max_height, max_width),
        };
        let mut runs = vec![];
        let mut run = vec![];
        let cross_margin = self.cross_margin.get();
        let in_margin = self.in_margin.get();
        {
            let mut run_cross_size: f32 = 0.0;
            let mut in_pos = in_margin;
            for element in elements.deref() {
                let (w, h) = element.layout(ctx, scale, max_width, max_height);
                let (cur_in_size, cur_cross_size) = match orientation {
                    Orientation::Horizontal => (w, h),
                    Orientation::Vertical => (h, w),
                };
                if in_pos + cur_in_size > max_in_size && run.len() > 0 {
                    runs.push((run, run_cross_size));
                    run = vec![];
                    in_pos = in_margin;
                    run_cross_size = 0.0;
                }
                run_cross_size = run_cross_size.max(cur_cross_size);
                run.push((element, cur_in_size, cur_cross_size));
                in_pos += cur_in_size + in_margin;
            }
            if run.len() > 0 {
                runs.push((run, run_cross_size));
            }
        }
        let mut max_in_pos: f32 = 0.0;
        let mut cross_pos = cross_margin;
        for (run, run_cross_size) in runs {
            let mut in_pos = in_margin;
            for (element, cur_in_size, cur_cross_size) in run {
                let cur_cross_pos = cross_pos
                    + match self.cross_align.get() {
                        Align::Left => 0.0,
                        Align::Center => (run_cross_size - cur_cross_size) / 2.0,
                        Align::Right => run_cross_size - cur_cross_size,
                    };
                let (x, y, w, h) = match orientation {
                    Orientation::Horizontal => (in_pos, cur_cross_pos, cur_in_size, cur_cross_size),
                    Orientation::Vertical => (cur_cross_pos, in_pos, cur_cross_size, cur_in_size),
                };
                element.data().x.set(x);
                element.data().y.set(y);
                element.data().width.set(w);
                element.data().height.set(h);
                in_pos += in_margin + cur_in_size;
            }
            max_in_pos = max_in_pos.max(in_pos);
            cross_pos += cross_margin + run_cross_size;
        }
        let (w, h) = match orientation {
            Orientation::Horizontal => (max_in_pos, cross_pos),
            Orientation::Vertical => (cross_pos, max_in_pos),
        };
        (w.min(max_width), h.min(max_height))
    }

    fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
        for element in self.elements.borrow_mut().deref() {
            element.render_at(
                color_manager,
                r,
                x + element.data().x.get(),
                y + element.data().y.get(),
            );
        }
    }

    fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>> {
        for child in self.elements.borrow_mut().deref() {
            let data = child.data();
            let x1 = data.x.get();
            let y1 = data.y.get();
            if x >= x1 && x - x1 < data.width.get() && y >= y1 && y - y1 < data.height.get() {
                return Some(child.clone());
            }
        }
        None
    }

    fn destroy(&self) {
        for element in self.elements.borrow_mut().drain(..) {
            element.destroy();
        }
    }
}

pub struct OverlayWindow {
    pub layer_surface: Rc<UsrWlrLayerSurface>,
    pub data: Rc<WindowData>,
    pub owner: CloneCell<Option<Rc<dyn OverlayWindowOwner>>>,
}

pub trait OverlayWindowOwner {
    fn kill(&self, upwards: bool);
}

pub struct WindowData {
    pub frame_missed: Cell<bool>,
    pub first_scale: Cell<bool>,
    pub have_frame: Cell<bool>,
    pub scale: Cell<Scale>,
    pub render_trigger: AsyncEvent,
    pub render_task: Cell<Option<SpawnedFuture<()>>>,
    pub dpy: Rc<PortalDisplay>,
    pub content: CloneCell<Option<Rc<dyn GuiElement>>>,
    pub surface: Rc<UsrWlSurface>,
    pub viewport: Rc<UsrWpViewport>,
    pub fractional_scale: Rc<UsrWpFractionalScale>,
    pub bufs: RefCell<Vec<Rc<GuiBuffer>>>,
    pending_bufs: CopyHashMap<ZwpLinuxBufferParamsV1Id, Rc<GuiBufferPending>>,
    pub width: Cell<i32>,
    pub height: Cell<i32>,
    pub owner: CloneCell<Option<Rc<dyn WindowDataOwner>>>,
    pub seats: CopyHashMap<GlobalName, Rc<GuiWindowSeatState>>,
}

#[derive(Default)]
pub struct GuiWindowSeatState {
    pub x: Cell<f32>,
    pub y: Cell<f32>,
    pub tree: RefCell<Vec<Rc<dyn GuiElement>>>,
    pub cursor: Cell<Option<KnownCursor>>,
}

pub trait WindowDataOwner {
    fn post_layout(&self);
    fn kill(&self, upwards: bool);
}

impl WindowDataOwner for OverlayWindow {
    fn post_layout(&self) {
        self.layer_surface
            .set_size(self.data.width.get(), self.data.height.get());
        self.data.surface.commit();
    }

    fn kill(&self, upwards: bool) {
        if let Some(owner) = self.owner.take() {
            owner.kill(upwards);
        }
        self.layer_surface
            .con
            .remove_obj(self.layer_surface.deref());
    }
}

const NUM_BUFFERS: usize = 2;

impl OverlayWindow {
    pub fn new(output: &Rc<PortalOutput>) -> Rc<Self> {
        let data = WindowData::new(&output.dpy);
        let layer_surface = output
            .dpy
            .ls
            .get_layer_surface(&data.surface, &output.wl, OVERLAY);
        layer_surface.set_size(1, 1);
        let slf = Rc::new(Self {
            layer_surface,
            data,
            owner: Default::default(),
        });
        slf.data.owner.set(Some(slf.clone()));
        slf.layer_surface.owner.set(Some(slf.clone()));
        slf.data.surface.commit();
        slf
    }
}

impl WindowData {
    pub fn schedule_render(&self) {
        self.render_trigger.trigger();
    }

    pub fn new(dpy: &Rc<PortalDisplay>) -> Rc<Self> {
        let surface = dpy.comp.create_surface();
        let viewport = dpy.vp.get_viewport(&surface);
        let fractional_scale = dpy.fsm.get_fractional_scale(&surface);
        viewport.set_destination(1, 1);
        let data = Rc::new(WindowData {
            frame_missed: Cell::new(false),
            first_scale: Cell::new(true),
            have_frame: Cell::new(true),
            bufs: Default::default(),
            pending_bufs: Default::default(),
            width: Cell::new(0),
            height: Cell::new(0),
            owner: Default::default(),
            render_trigger: Default::default(),
            render_task: Cell::new(None),
            dpy: dpy.clone(),
            content: Default::default(),
            surface,
            viewport,
            scale: Cell::new(Scale::from_int(1)),
            fractional_scale,
            seats: Default::default(),
        });
        data.render_task.set(Some(dpy.state.eng.spawn2(
            "render",
            Phase::Present,
            data.clone().render_task(),
        )));
        data.fractional_scale.owner.set(Some(data.clone()));
        data
    }

    pub fn layout(&self) {
        let ctx = match self.dpy.render_ctx.get() {
            Some(ctx) => ctx,
            _ => return,
        };
        let scale = self.scale.get().to_f64() as f32;
        let content = match self.content.get() {
            Some(c) => c,
            _ => return,
        };
        let (mut width, mut height) =
            content.layout(&ctx.ctx.ctx, scale, f32::INFINITY, f32::INFINITY);
        content.data().width.set(width);
        content.data().height.set(height);
        width = width.max(1.0);
        height = height.max(1.0);
        self.width.set(width.round() as _);
        self.height.set(height.round() as _);
        self.viewport
            .set_destination(width.round() as _, height.round() as _);
        if let Some(owner) = self.owner.get() {
            owner.post_layout();
        }
    }

    async fn render_task(self: Rc<Self>) {
        loop {
            self.render_trigger.triggered().await;
            self.render();
        }
    }

    fn render(self: &Rc<Self>) {
        self.frame_missed.set(true);
        if !self.have_frame.get() {
            return;
        }
        let bufs = self.bufs.borrow_mut();
        let buf = 'get_buf: {
            for buf in bufs.deref() {
                if buf.free.get() {
                    break 'get_buf buf;
                }
            }
            return;
        };

        let res = buf.fb.render_custom(
            AcquireSync::Implicit,
            ReleaseSync::Implicit,
            self.dpy.state.color_manager.srgb_gamma22(),
            self.scale.get(),
            Some(&Color::from_gray_srgb(0)),
            &self.dpy.state.color_manager.srgb_gamma22().linear,
            None,
            self.dpy.state.color_manager.srgb_linear(),
            &mut |r| {
                if let Some(content) = self.content.get() {
                    content.render_at(&self.dpy.state.color_manager, r, 0.0, 0.0)
                }
            },
        );
        if let Err(e) = res {
            log::error!("Could not render frame: {}", ErrorFmt(e));
            return;
        }

        self.frame_missed.set(false);

        self.surface.frame().owner.set(Some(self.clone()));

        self.have_frame.set(false);
        buf.free.set(false);

        self.surface.attach(&buf.wl);
        self.surface.damage();
        self.surface.commit();
    }

    pub fn kill(&self, upwards: bool) {
        if let Some(owner) = self.owner.take() {
            owner.kill(upwards);
        }
        self.render_task.take();
        for pb in self.pending_bufs.lock().drain_values() {
            pb.params.con.remove_obj(pb.params.deref());
        }
        for buf in self.bufs.borrow_mut().drain(..) {
            buf.wl.con.remove_obj(buf.wl.deref());
        }
        self.fractional_scale
            .con
            .remove_obj(self.fractional_scale.deref());
        self.viewport.con.remove_obj(self.viewport.deref());
        self.surface.con.remove_obj(self.surface.deref());
        if let Some(content) = self.content.take() {
            content.destroy();
        }
    }

    pub fn allocate_buffers(self: &Rc<Self>) {
        {
            for buf in self.pending_bufs.lock().drain_values() {
                buf.params.con.remove_obj(buf.params.deref());
            }
        }
        {
            let mut bufs = self.bufs.borrow_mut();
            for buf in bufs.drain(..) {
                buf.wl.con.remove_obj(buf.wl.deref());
            }
        }
        let ctx = match self.dpy.render_ctx.get() {
            Some(ctx) => ctx,
            _ => return,
        };
        let dmabuf = match self.dpy.dmabuf.get() {
            Some(dmabuf) => dmabuf,
            _ => return,
        };
        self.frame_missed.set(true);
        let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32;
        let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
        let formats = &ctx.usable_formats;
        let format = match formats.get(&ARGB8888.drm) {
            None => {
                log::error!("Render context does not support ARGB8888 format");
                return;
            }
            Some(f) => f,
        };
        if format.write_modifiers.is_empty() {
            log::error!("Render context cannot render to ARGB8888 format");
            return;
        }
        let modifiers: Vec<_> = format.write_modifiers.keys().copied().collect();
        let mut usage = BO_USE_RENDERING;
        if !needs_render_usage(format.write_modifiers.values()) {
            usage = BufferUsage::none();
        }
        for _ in 0..NUM_BUFFERS {
            let bo = match ctx.ctx.ctx.allocator().create_bo(
                &self.dpy.state.dma_buf_ids,
                width,
                height,
                ARGB8888,
                &modifiers,
                usage,
            ) {
                Ok(b) => b,
                Err(e) => {
                    log::error!("Could not allocate dmabuf: {}", ErrorFmt(e));
                    return;
                }
            };
            let img = match ctx.ctx.ctx.clone().dmabuf_img(bo.dmabuf()) {
                Ok(b) => b,
                Err(e) => {
                    log::error!("Could not import dmabuf into EGL: {}", ErrorFmt(e));
                    return;
                }
            };
            let fb = match img.to_framebuffer() {
                Ok(b) => b,
                Err(e) => {
                    log::error!(
                        "Could not turns EGL image into framebuffer: {}",
                        ErrorFmt(e)
                    );
                    return;
                }
            };
            let params = dmabuf.create_params();
            params.create(bo.dmabuf());
            let pending = Rc::new(GuiBufferPending {
                bo: Cell::new(Some(bo)),
                window: self.clone(),
                fb,
                params,
                size: (width, height),
            });
            pending.params.owner.set(Some(pending.clone()));
            self.pending_bufs.set(pending.params.id, pending.clone());
        }
    }

    fn tree_at(&self, tree: &mut Vec<Rc<dyn GuiElement>>, mut x: f32, mut y: f32) {
        let mut element = match self.content.get() {
            Some(e) => e,
            _ => return,
        };
        tree.push(element.clone());
        while let Some(c) = element.child_at(x, y) {
            tree.push(c.clone());
            x -= c.data().x.get();
            y -= c.data().y.get();
            element = c;
        }
    }

    pub fn motion(&self, pseat: &PortalSeat, x: Fixed, y: Fixed, _enter: bool) {
        let x = x.to_f64() as f32;
        let y = y.to_f64() as f32;
        let seat = self
            .seats
            .lock()
            .entry(pseat.global_id)
            .or_default()
            .clone();
        seat.x.set(x);
        seat.y.set(y);

        let mut tree = seat.tree.borrow_mut();
        let old_element = tree.last().cloned();
        self.tree_at(&mut tree, x, y);
        let new_element = tree.last().cloned();

        let element_changed = match (&old_element, &new_element) {
            (Some(old), Some(new)) => !rc_eq(old, new),
            (None, None) => false,
            _ => true,
        };

        if element_changed {
            if let Some(o) = &old_element {
                o.hover(pseat, false);
            }
            if let Some(o) = &new_element {
                o.hover(pseat, true);
            }
        }

        if element_changed {
            self.schedule_render();
        }

        let cursor = match &new_element {
            Some(e) => e.hover_cursor(),
            _ => KnownCursor::Default,
        };

        if seat.cursor.replace(Some(cursor)) != Some(cursor) {
            pseat.jay_pointer.set_known_cursor(cursor);
        }
    }

    pub fn button(&self, pseat: &PortalSeat, button: u32, state: u32) {
        let seat = match self.seats.get(&pseat.global_id) {
            Some(s) => s,
            _ => return,
        };
        let element = seat.tree.borrow_mut().last().cloned();
        if let Some(e) = element {
            e.button(pseat, button, state);
        }
    }
}

pub struct GuiBuffer {
    pub wl: Rc<UsrWlBuffer>,
    pub window: Rc<WindowData>,
    pub fb: Rc<dyn GfxFramebuffer>,
    pub _bo: Option<Rc<dyn BufferObject>>,
    pub free: Cell<bool>,
    pub _size: (i32, i32),
}

struct GuiBufferPending {
    pub bo: Cell<Option<Rc<dyn BufferObject>>>,
    pub window: Rc<WindowData>,
    pub fb: Rc<dyn GfxFramebuffer>,
    pub params: Rc<UsrLinuxBufferParams>,
    pub size: (i32, i32),
}

impl UsrWlBufferOwner for GuiBuffer {
    fn release(&self) {
        self.free.set(true);
        if self.window.frame_missed.get() {
            self.window.schedule_render();
        }
    }
}

impl UsrWpFractionalScaleOwner for WindowData {
    fn preferred_scale(self: Rc<Self>, ev: &PreferredScale) {
        let mut layout = self.first_scale.replace(false);
        let scale = Scale::from_wl(ev.scale);
        layout |= self.scale.replace(scale) != scale;
        if layout {
            self.layout();
            self.allocate_buffers();
        }
    }
}

impl UsrWlCallbackOwner for WindowData {
    fn done(self: Rc<Self>) {
        self.have_frame.set(true);
        if self.frame_missed.get() {
            self.schedule_render();
        }
    }
}

impl UsrWlrLayerSurfaceOwner for OverlayWindow {
    fn configure(&self, _ev: &Configure) {
        self.data.schedule_render();
    }

    fn closed(&self) {
        self.data.kill(true);
    }
}

impl UsrLinuxBufferParamsOwner for GuiBufferPending {
    fn created(&self, buffer: Rc<UsrWlBuffer>) {
        buffer.con.add_object(buffer.clone());
        let buf = Rc::new(GuiBuffer {
            wl: buffer,
            window: self.window.clone(),
            fb: self.fb.clone(),
            _bo: self.bo.take(),
            free: Cell::new(true),
            _size: self.size,
        });
        buf.wl.owner.set(Some(buf.clone()));
        self.window.bufs.borrow_mut().push(buf);
        self.params.con.remove_obj(self.params.deref());
        self.window.pending_bufs.remove(&self.params.id);
        if self.window.frame_missed.get() {
            self.window.schedule_render();
        }
    }

    fn failed(&self) {
        self.window.kill(true);
    }
}
