use std::time::Duration;
use std::{mem, ptr};
use xcb_dl::ffi::{
    xcb_atom_t, xcb_connection_t, xcb_generic_error_t, xcb_window_t, XCB_ATOM_STRING,
    XCB_PROP_MODE_REPLACE,
};
use xcb_dl::Xcb;

unsafe fn check_err(err: *mut xcb_generic_error_t) {
    if !err.is_null() {
        panic!("{:#?}", *err);
    }
}

unsafe fn get_atom(lib: &Xcb, con: *mut xcb_connection_t, atom: &str) -> xcb_atom_t {
    let mut err = ptr::null_mut();
    let atom_reply = lib.xcb_intern_atom_reply(
        con,
        lib.xcb_intern_atom(con, 0, atom.len() as u16, atom.as_ptr() as _),
        &mut err,
    );
    check_err(err);
    assert!(!atom_reply.is_null());
    let atom = (*atom_reply).atom;
    libc::free(atom_reply as _);
    atom
}

unsafe fn replace_property<T: ?Sized>(
    lib: &Xcb,
    con: *mut xcb_connection_t,
    window: xcb_window_t,
    prop: xcb_atom_t,
    ty: xcb_atom_t,
    t: &T,
) {
    let cookie = lib.xcb_change_property_checked(
        con,
        XCB_PROP_MODE_REPLACE as _,
        window,
        prop,
        ty,
        8,
        mem::size_of_val(t) as _,
        t as *const _ as _,
    );
    check_err(lib.xcb_request_check(con, cookie));
}

unsafe fn create_window(lib: &Xcb, con: *mut xcb_connection_t) -> xcb_window_t {
    let setup = lib.xcb_get_setup(con);
    let screen = &*lib.xcb_setup_roots_iterator(setup).data;
    let window_id = lib.xcb_generate_id(con);
    let cookie = lib.xcb_create_window_checked(
        con,
        screen.root_depth,
        window_id,
        screen.root,
        0,
        0,
        200,
        200,
        0,
        0,
        screen.root_visual,
        0,
        ptr::null(),
    );
    check_err(lib.xcb_request_check(con, cookie));
    window_id
}

fn main() {
    unsafe {
        let lib = Xcb::load().unwrap();
        let con = lib.xcb_connect(ptr::null(), ptr::null_mut());
        assert_eq!(lib.xcb_connection_has_error(con), 0);
        let window_id = create_window(&lib, con);
        let wm_name = get_atom(&lib, con, "WM_NAME");
        replace_property(&lib, con, window_id, wm_name, XCB_ATOM_STRING, "Hello XCB");
        check_err(lib.xcb_request_check(con, lib.xcb_map_window_checked(con, window_id)));
        assert!(lib.xcb_flush(con) > 0);
        std::thread::sleep(Duration::from_secs(5));
    }
}