pub fn open_session_log_popout(
    app: &gtk::Application,
    handle: &Rc<RefCell<Option<gtk::ApplicationWindow>>>,
    buffer: &gtk::TextBuffer,
) {
    open_text_buffer_popout(
        app,
        handle,
        None,
        buffer,
        "Lesavka Log",
        "Copy",
        gtk::WrapMode::WordChar,
    );
}

pub fn open_diagnostics_popout(
    app: &gtk::Application,
    handle: &Rc<RefCell<Option<gtk::ApplicationWindow>>>,
    label_handle: &Rc<RefCell<Option<gtk::Label>>>,
    scroll_handle: &Rc<RefCell<Option<gtk::ScrolledWindow>>>,
    rendered_text: &Rc<RefCell<String>>,
) {
    if let Some(window) = handle.borrow().as_ref() {
        window.present();
        return;
    }

    let window = gtk::ApplicationWindow::builder()
        .application(app)
        .title("Lesavka Diagnostics")
        .default_width(980)
        .default_height(680)
        .build();
    super::ui_components::install_css(&window);
    super::ui_components::install_window_icon(&window);
    super::ui_components::install_window_chrome(&window, "Lesavka Diagnostics");

    let root = gtk::Box::new(gtk::Orientation::Vertical, 10);
    root.set_margin_start(14);
    root.set_margin_end(14);
    root.set_margin_top(14);
    root.set_margin_bottom(14);

    let toolbar = gtk::Box::new(gtk::Orientation::Horizontal, 8);
    let copy_button = gtk::Button::with_label("Copy Report");
    toolbar.append(&copy_button);
    root.append(&toolbar);

    let current_text = rendered_text.borrow().clone();
    let label = gtk::Label::new(Some(&current_text));
    label.add_css_class("status-log");
    label.set_selectable(true);
    label.set_xalign(0.0);
    label.set_yalign(0.0);
    label.set_wrap(false);
    label.set_halign(gtk::Align::Start);
    label.set_valign(gtk::Align::Start);
    label.set_hexpand(true);
    let shell = gtk::Box::new(gtk::Orientation::Vertical, 0);
    shell.set_hexpand(true);
    shell.set_vexpand(false);
    shell.append(&label);
    let scroll = gtk::ScrolledWindow::builder()
        .hexpand(true)
        .vexpand(true)
        .child(&shell)
        .build();
    *label_handle.borrow_mut() = Some(label.clone());
    *scroll_handle.borrow_mut() = Some(scroll.clone());
    root.append(&scroll);
    window.set_child(Some(&root));
    window.maximize();

    {
        let rendered_text = Rc::clone(rendered_text);
        copy_button.connect_clicked(move |_| {
            let current_text = rendered_text.borrow().clone();
            let _ = copy_plain_text(&current_text);
        });
    }

    {
        let handle = Rc::clone(handle);
        let label_handle = Rc::clone(label_handle);
        let scroll_handle = Rc::clone(scroll_handle);
        window.connect_close_request(move |_| {
            handle.borrow_mut().take();
            label_handle.borrow_mut().take();
            scroll_handle.borrow_mut().take();
            glib::Propagation::Proceed
        });
    }

    *handle.borrow_mut() = Some(window.clone());
    window.present();
}

fn open_text_buffer_popout(
    app: &gtk::Application,
    handle: &Rc<RefCell<Option<gtk::ApplicationWindow>>>,
    scroll_handle: Option<&Rc<RefCell<Option<gtk::ScrolledWindow>>>>,
    buffer: &gtk::TextBuffer,
    title: &str,
    copy_button_label: &str,
    wrap_mode: gtk::WrapMode,
) {
    if let Some(window) = handle.borrow().as_ref() {
        window.present();
        return;
    }

    let window = gtk::ApplicationWindow::builder()
        .application(app)
        .title(title)
        .default_width(980)
        .default_height(680)
        .build();
    super::ui_components::install_css(&window);
    super::ui_components::install_window_icon(&window);
    super::ui_components::install_window_chrome(&window, title);

    let root = gtk::Box::new(gtk::Orientation::Vertical, 10);
    root.set_margin_start(14);
    root.set_margin_end(14);
    root.set_margin_top(14);
    root.set_margin_bottom(14);

    let toolbar = gtk::Box::new(gtk::Orientation::Horizontal, 8);
    let copy_button = gtk::Button::with_label(copy_button_label);
    toolbar.append(&copy_button);
    root.append(&toolbar);

    let view = gtk::TextView::with_buffer(buffer);
    view.add_css_class("status-log");
    view.set_editable(false);
    view.set_cursor_visible(false);
    view.set_monospace(true);
    view.set_wrap_mode(wrap_mode);
    let scroll = gtk::ScrolledWindow::builder()
        .hexpand(true)
        .vexpand(true)
        .child(&view)
        .build();
    if let Some(scroll_handle) = scroll_handle {
        *scroll_handle.borrow_mut() = Some(scroll.clone());
    }
    root.append(&scroll);
    window.set_child(Some(&root));
    window.maximize();

    {
        let buffer = buffer.clone();
        copy_button.connect_clicked(move |_| {
            let _ = copy_session_log(&buffer);
        });
    }

    {
        let handle = Rc::clone(handle);
        let scroll_handle = scroll_handle.cloned();
        window.connect_close_request(move |_| {
            handle.borrow_mut().take();
            if let Some(scroll_handle) = &scroll_handle {
                scroll_handle.borrow_mut().take();
            }
            glib::Propagation::Proceed
        });
    }

    *handle.borrow_mut() = Some(window.clone());
    window.present();
}

pub fn stop_child_process(child_proc: &Rc<RefCell<Option<RelayChild>>>) {
    if let Some(mut child) = child_proc.borrow_mut().take() {
        let _ = child.kill();
        let _ = child.wait();
    }
}

pub fn shutdown_launcher_runtime(
    child_proc: &Rc<RefCell<Option<RelayChild>>>,
    tests: &Rc<RefCell<DeviceTestController>>,
    preview: Option<&LauncherPreview>,
    widgets: &LauncherWidgets,
    popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
    diagnostics_popout: &Rc<RefCell<Option<gtk::ApplicationWindow>>>,
    log_popout: &Rc<RefCell<Option<gtk::ApplicationWindow>>>,
) {
    stop_child_process(child_proc);
    tests.borrow_mut().stop_all();

    if let Some(preview) = preview {
        preview.set_session_active(false);
        preview.shutdown_all();
    }

    for pane in &widgets.display_panes {
        if let Some(binding) = pane.preview_binding.borrow_mut().take() {
            binding.close();
        }
        pane.picture.set_paintable(Option::<&gdk::Paintable>::None);
        pane.stream_status.set_text("");
    }

    let mut detached_popouts = Vec::new();
    {
        let mut slots = popouts.borrow_mut();
        for slot in slots.iter_mut() {
            if let Some(handle) = slot.take() {
                detached_popouts.push(handle);
            }
        }
    }
    for handle in detached_popouts {
        handle.binding.close();
        handle
            .picture
            .set_paintable(Option::<&gdk::Paintable>::None);
        handle.window.set_child(Option::<&gtk::Widget>::None);
        handle.window.hide();
    }

    if let Some(window) = diagnostics_popout.borrow_mut().take() {
        widgets.diagnostics_popout_label.borrow_mut().take();
        widgets.diagnostics_popout_scroll.borrow_mut().take();
        window.set_child(Option::<&gtk::Widget>::None);
        window.hide();
    }
    if let Some(window) = log_popout.borrow_mut().take() {
        window.set_child(Option::<&gtk::Widget>::None);
        window.hide();
    }
}

pub fn reap_exited_child(child_proc: &Rc<RefCell<Option<RelayChild>>>) -> bool {
    let mut slot = child_proc.borrow_mut();
    match slot.as_mut() {
        Some(child) => match child.try_wait() {
            Ok(Some(_)) => {
                *slot = None;
                false
            }
            Ok(None) | Err(_) => true,
        },
        None => false,
    }
}

pub fn next_input_routing(routing: InputRouting) -> InputRouting {
    match routing {
        InputRouting::Remote => InputRouting::Local,
        InputRouting::Local => InputRouting::Remote,
    }
}
