pub fn open_popout_window(
    app: &gtk::Application,
    preview: &LauncherPreview,
    state: &Rc<RefCell<LauncherState>>,
    child_proc: &Rc<RefCell<Option<RelayChild>>>,
    popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
    widgets: &LauncherWidgets,
    monitor_id: usize,
) {
    let already_open = {
        let popouts = popouts.borrow();
        popouts[monitor_id].is_some()
    };
    if already_open {
        return;
    }

    if let Some(binding) = widgets.display_panes[monitor_id]
        .preview_binding
        .borrow()
        .as_ref()
    {
        binding.set_enabled(false);
    }

    let (breakout_size, breakout_limit) = {
        let state = state.borrow();
        (
            state.breakout_size_choice(monitor_id),
            state.breakout_display_size(),
        )
    };
    let title = format!("Lesavka {}", widgets.display_panes[monitor_id].title);
    let window = gtk::ApplicationWindow::builder()
        .application(app)
        .title(&title)
        .default_width(breakout_size.width)
        .default_height(breakout_size.height)
        .build();
    super::ui_components::install_css(&window);
    super::ui_components::install_window_icon(&window);
    super::ui_components::install_window_chrome(&window, &title);
    window.set_resizable(false);

    let picture = gtk::Picture::new();
    picture.set_hexpand(true);
    picture.set_vexpand(true);
    picture.set_halign(gtk::Align::Fill);
    picture.set_valign(gtk::Align::Fill);
    picture.set_can_shrink(true);
    picture.set_keep_aspect_ratio(true);
    picture.set_size_request(breakout_size.width, breakout_size.height);
    let root = gtk::Box::new(gtk::Orientation::Vertical, 0);
    root.set_hexpand(true);
    root.set_vexpand(true);
    root.set_halign(gtk::Align::Fill);
    root.set_valign(gtk::Align::Fill);
    root.set_size_request(breakout_size.width, breakout_size.height);
    let frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
    frame.set_hexpand(true);
    frame.set_vexpand(true);
    frame.set_halign(gtk::Align::Fill);
    frame.set_valign(gtk::Align::Fill);
    frame.set_size_request(breakout_size.width, breakout_size.height);
    frame.set_child(Some(&picture));
    root.append(&frame);

    let stream_status = gtk::Label::new(Some(""));

    let binding = preview
        .install_on_picture(monitor_id, PreviewSurface::Window, &picture, &stream_status)
        .expect("preview binding for popout");

    window.set_child(Some(&root));
    install_popout_drag(&window, &picture);
    apply_popout_window_geometry(&window, &root, &picture, breakout_size, breakout_limit);

    let state_handle = Rc::clone(state);
    let child_proc_handle = Rc::clone(child_proc);
    let popouts_handle = Rc::clone(popouts);
    let widgets_handle = widgets.clone();
    let close_binding = binding.clone();
    window.connect_close_request(move |_| {
        let handle = {
            let mut popouts = popouts_handle.borrow_mut();
            popouts[monitor_id].take()
        };
        if let Some(handle) = handle {
            handle.binding.close();
            if let Some(preview_binding) = widgets_handle.display_panes[monitor_id]
                .preview_binding
                .borrow()
                .as_ref()
            {
                preview_binding.set_enabled(true);
            }
            {
                let mut state = state_handle.borrow_mut();
                state.set_display_surface(monitor_id, DisplaySurface::Preview);
            }
            let child_running = child_proc_handle.borrow().is_some();
            let state_snapshot = state_handle.borrow().clone();
            refresh_launcher_ui(&widgets_handle, &state_snapshot, child_running);
        } else {
            close_binding.close();
        }
        glib::Propagation::Proceed
    });

    {
        let mut state = state.borrow_mut();
        state.set_display_surface(monitor_id, DisplaySurface::Window);
    }
    {
        let mut popouts = popouts.borrow_mut();
        popouts[monitor_id] = Some(PopoutWindowHandle {
            window: window.clone(),
            root: root.clone(),
            frame: frame.clone(),
            picture: picture.clone(),
            status_label: stream_status.clone(),
            binding,
        });
    }
    let child_running = child_proc.borrow().is_some();
    let state_snapshot = state.borrow().clone();
    refresh_launcher_ui(widgets, &state_snapshot, child_running);
    window.present();
    schedule_popout_window_geometry(
        window.clone(),
        root.clone(),
        picture.clone(),
        breakout_size,
        breakout_limit,
    );
}

pub fn apply_popout_window_size(
    handle: &PopoutWindowHandle,
    size: BreakoutSizeChoice,
    display_limit: super::state::PreviewSourceSize,
) {
    handle.frame.set_size_request(size.width, size.height);
    apply_popout_window_geometry(
        &handle.window,
        &handle.root,
        &handle.picture,
        size,
        display_limit,
    );
    handle.window.present();
    schedule_popout_window_geometry(
        handle.window.clone(),
        handle.root.clone(),
        handle.picture.clone(),
        size,
        display_limit,
    );
}

pub fn dock_display_to_preview(
    state: &Rc<RefCell<LauncherState>>,
    child_proc: &Rc<RefCell<Option<RelayChild>>>,
    popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
    widgets: &LauncherWidgets,
    monitor_id: usize,
) {
    let handle = {
        let mut popouts = popouts.borrow_mut();
        popouts[monitor_id].take()
    };
    if let Some(handle) = handle {
        handle.binding.close();
        handle.window.close();
    }
    if let Some(binding) = widgets.display_panes[monitor_id]
        .preview_binding
        .borrow()
        .as_ref()
    {
        binding.set_enabled(true);
    }
    {
        let mut state = state.borrow_mut();
        state.set_display_surface(monitor_id, DisplaySurface::Preview);
    }
    let child_running = child_proc.borrow().is_some();
    if !child_running {
        let pane = &widgets.display_panes[monitor_id];
        pane.picture.set_paintable(Option::<&gdk::Paintable>::None);
        pane.preview_placeholder.set_visible(true);
        pane.stream_status.set_text("Connect relay to preview.");
    }
    let state_snapshot = state.borrow().clone();
    refresh_launcher_ui(widgets, &state_snapshot, child_running);
}

pub fn dock_all_displays_to_preview(
    state: &Rc<RefCell<LauncherState>>,
    child_proc: &Rc<RefCell<Option<RelayChild>>>,
    popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
    widgets: &LauncherWidgets,
) {
    let mut handles = Vec::new();
    {
        let mut popouts = popouts.borrow_mut();
        for monitor_id in 0..2 {
            if let Some(handle) = popouts[monitor_id].take() {
                handles.push(handle);
            }
        }
    }
    for handle in handles {
        handle.binding.close();
        handle.window.close();
    }

    for monitor_id in 0..2 {
        if let Some(binding) = widgets.display_panes[monitor_id]
            .preview_binding
            .borrow()
            .as_ref()
        {
            binding.set_enabled(true);
        }
    }

    {
        let mut state = state.borrow_mut();
        for monitor_id in 0..2 {
            state.set_display_surface(monitor_id, DisplaySurface::Preview);
        }
    }

    let child_running = child_proc.borrow().is_some();
    if !child_running {
        for pane in &widgets.display_panes {
            pane.picture.set_paintable(Option::<&gdk::Paintable>::None);
            pane.preview_placeholder.set_visible(true);
            pane.stream_status.set_text("Connect relay to preview.");
        }
    }
    let state_snapshot = state.borrow().clone();
    refresh_launcher_ui(widgets, &state_snapshot, child_running);
}

pub fn refresh_display_pane(pane: &DisplayPaneWidgets, surface: DisplaySurface) {
    if let Some(binding) = pane.preview_binding.borrow().as_ref() {
        binding.set_enabled(matches!(surface, DisplaySurface::Preview));
    }
    let preview_ready = pane.preview_binding.borrow().is_some();
    pane.action_button.set_sensitive(preview_ready);
    pane.clip_button.set_sensitive(preview_ready);
    pane.record_button.set_sensitive(preview_ready);
    pane.save_button.set_sensitive(true);
    match surface {
        DisplaySurface::Preview => {
            pane.stack.set_visible_child_name("preview");
            pane.action_button.set_label("↗");
            pane.action_button
                .set_tooltip_text(Some("Open this eye in its own breakout window."));
            pane.preview_placeholder
                .set_visible(pane.picture.paintable().is_none());
            if pane.preview_binding.borrow().is_none() {
                pane.stream_status.set_text("Preview unavailable");
            }
        }
        DisplaySurface::Window => {
            pane.stack.set_visible_child_name("placeholder");
            pane.action_button.set_label("↙");
            pane.action_button
                .set_tooltip_text(Some("Return this eye to the launcher preview."));
            pane.stream_status.set_text("Streaming in its own window");
            pane.preview_placeholder.set_visible(false);
        }
    }
}
