pub use crate::xcon::{
    formatter::Formatter,
    parser::Parser,
    wire_type::{Message, Request, XEvent},
};
use {
    crate::{
        async_engine::{Phase, SpawnedFuture},
        compositor::DISPLAY,
        io_uring::IoUringError,
        state::State,
        utils::{
            buf::DynamicBuf,
            bufio::{BufIo, BufIoError, BufIoMessage},
            clonecell::CloneCell,
            errorfmt::ErrorFmt,
            numcell::NumCell,
            oserror::{OsError, OsErrorExt2},
            queue::AsyncQueue,
            stack::Stack,
            vec_ext::VecExt,
        },
        wire_xcon::{
            CreateGC, CreatePixmap, EXTENSIONS, Extension, FreeGC, FreePixmap, GetInputFocus,
            GetProperty, ListExtensions, PutImage, QueryExtension, RenderCreateCursor,
            RenderCreatePicture, RenderQueryPictFormats, Setup,
        },
        xcon::{
            consts::{IMAGE_FORMAT_Z_PIXMAP, RENDER_PICT_TYPE_DIRECT},
            incoming::handle_incoming,
            outgoing::handle_outgoing,
            wire_type::SendEvent,
            xauthority::{LOCAL, MIT_MAGIC_COOKIE, XAuthority},
        },
    },
    ahash::AHashMap,
    bstr::{BString, ByteSlice},
    std::{
        any::TypeId,
        cell::{Cell, RefCell},
        collections::VecDeque,
        fmt::Debug,
        future::Future,
        io::Write,
        mem::{self, MaybeUninit},
        ops::{Deref, DerefMut},
        pin::Pin,
        ptr,
        rc::{Rc, Weak},
        task::{Context, Poll, Waker},
    },
    thiserror::Error,
    uapi::{OwnedFd, c},
};

pub mod consts;
mod formatter;
mod incoming;
mod outgoing;
mod parser;
mod wire_type;
mod xauthority;

#[derive(Debug, Error)]
pub enum XconError {
    #[error("Unexpected EOF")]
    UnexpectedEof,
    #[error("Buffer slice is not properly aligned")]
    UnalignedSlice,
    #[error("Neither XAUTHORITY nor HOME is set")]
    HomeNotSet,
    #[error("Could not read Xauthority file")]
    ReadXAuthority(#[source] std::io::Error),
    #[error("Display field in Xauthority could not be parsed")]
    InvalidAuthorityDisplay,
    #[error("The DISPLAY is not set")]
    DisplayNotSet,
    #[error("DISPLAY contains an invalid value")]
    InvalidDisplayFormat,
    #[error("Could not create a unix socket")]
    CreateSocket(#[source] OsError),
    #[error("Could not connect to Xserver")]
    ConnectSocket(#[source] IoUringError),
    #[error("Could not retrive the hostname")]
    Hostname(#[source] OsError),
    #[error("Server did not send enough fds")]
    NotEnoughFds,
    #[error("Server rejected our connection attempt: {0}")]
    Connect(BString),
    #[error("Server requires additional authentication: {0}")]
    Authenticate(BString),
    #[error(transparent)]
    BufIoError(#[from] BufIoError),
    #[error("The server did not send a reply to a request")]
    MissingReply,
    #[error("The server did not send fds with a reply")]
    MissingFds,
    #[error("The server sent a message with an excessive size")]
    ExcessiveMessageSize,
    #[error(transparent)]
    XconError(Rc<XconError>),
    #[error("The server does not support the `{0}` extension")]
    ExtensionUnavailable(&'static str),
    #[error("The server returned error {0}")]
    CoreError(u8),
    #[error("The extension `{}` returned error {}", .0.name(), .1)]
    ExtensionError(Extension, u8),
    #[error("The connection to the server has already been closed")]
    Dead,
    #[error("Could not query the `{0}` extension")]
    QueryExtension(BString, #[source] Box<XconError>),
    #[error("All available xids have been used")]
    XidExhausted,
    #[error("Enum contains an unknown variant")]
    UnknownEnumVariant,
    #[error("Could not query the render pict formats")]
    QueryPictFormats(#[source] Box<XconError>),
    #[error("The server does not support the picture format for cursors")]
    CursorFormatNotSupported,
    #[error("Could not create a pixmap")]
    CreatePixmap(#[source] Box<XconError>),
    #[error("Could not create a graphics context")]
    CreateGc(#[source] Box<XconError>),
    #[error("Could not upload an image")]
    PutImage(#[source] Box<XconError>),
    #[error("Could not create a picture")]
    CreatePicture(#[source] Box<XconError>),
    #[error("Could not create a cursor")]
    CreateCursor(#[source] Box<XconError>),
    #[error("Property has an invalid type")]
    InvalidPropertyType,
    #[error("Property has an invalid format. Expected: {0}; Actual: {1}")]
    InvalidPropertyFormat(u8, u8),
    #[error("Length of the property data is not a multiple of its format")]
    IrregularPropertyLength,
    #[error("The property is not set")]
    PropertyUnavailable,
}

#[derive(Debug)]
struct ExtensionIdRange {
    name: BString,
    extension: Option<Extension>,
    first: u8,
}

#[derive(Default, Debug)]
struct ExtensionData {
    opcodes: [Option<u8>; EXTENSIONS.len()],
    first_event: [Option<u8>; EXTENSIONS.len()],
    ext_by_opcode: AHashMap<u8, Extension>,
    events: Vec<ExtensionIdRange>,
    errors: Vec<ExtensionIdRange>,
}

pub struct Xcon {
    data: Rc<XconData>,
    outgoing: Cell<Option<SpawnedFuture<()>>>,
    incoming: Cell<Option<SpawnedFuture<()>>>,
    root_window: u32,
    extensions: Rc<ExtensionData>,

    xid_next: Cell<u32>,
    xid_inc: u32,
    xid_max: u32,
}

impl Drop for Xcon {
    fn drop(&mut self) {
        self.data.kill();
    }
}

struct XconData {
    bufio: Rc<BufIo>,
    in_bufs: Stack<Vec<u8>>,
    next_serial: NumCell<u64>,
    last_recv_serial: Cell<u64>,
    reply_handlers: RefCell<VecDeque<Box<dyn ReplyHandler>>>,
    dead: Cell<bool>,
    need_sync: Cell<bool>,
    extensions: CloneCell<Option<Rc<ExtensionData>>>,
    xorg: CloneCell<Weak<Xcon>>,
    events: AsyncQueue<Event>,
}

pub struct Reply<T: Message<'static>> {
    socket: Rc<XconData>,
    buf: Vec<u8>,
    t: T::Generic<'static>,
}

impl<T: Message<'static>> Debug for Reply<T>
where
    T::Generic<'static>: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.t.fmt(f)
    }
}

pub struct Event {
    socket: Rc<XconData>,
    ext: Option<Extension>,
    code: u16,
    buf: Vec<u8>,
    serial: u64,
}

impl Deref for Event {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        &self.buf
    }
}

impl Event {
    pub fn ext(&self) -> Option<Extension> {
        self.ext
    }

    pub fn code(&self) -> u16 {
        self.code
    }

    pub fn serial(&self) -> u64 {
        self.serial
    }

    pub fn parse<'a, M: Message<'a>>(&'a self) -> Result<M, XconError> {
        let mut parser = Parser::new(&self.buf, vec![]);
        let res = M::deserialize(&mut parser);
        if let Ok(res) = &res {
            log::trace!("event {:?}", res);
        }
        res
    }
}

impl Drop for Event {
    fn drop(&mut self) {
        self.socket.in_bufs.push(mem::take(&mut self.buf));
    }
}

impl<T: Message<'static>> Reply<T> {
    pub fn get<'a>(&'a self) -> &'a T::Generic<'a> {
        unsafe { mem::transmute(&self.t) }
    }
}

impl<T: Message<'static>> Drop for Reply<T> {
    fn drop(&mut self) {
        if self.buf.capacity() > 0 {
            self.socket.in_bufs.push(mem::take(&mut self.buf));
        }
    }
}

unsafe trait ReplyHandler {
    fn has_fds(&self) -> bool;
    fn serial(&self) -> u64;
    fn handle_result(
        self: Box<Self>,
        socket: &Rc<XconData>,
        parser: &mut Parser<'static>,
        buf: Vec<u8>,
    ) -> Result<(), XconError>;
    fn handle_noreply(self: Box<Self>, bufio: &Rc<XconData>) -> Result<(), XconError>;
    fn handle_error(self: Box<Self>, error: XconError);
}

struct AsyncReplyHandler<T: Message<'static>> {
    serial: u64,
    slot: Weak<AsyncReplySlot<T>>,
}

impl<T: Message<'static>> AsyncReplyHandler<T> {
    fn done(self, res: Result<Reply<T>, XconError>) {
        if let Some(slot) = self.slot.upgrade() {
            slot.data.set(Some(res));
            if let Some(waker) = slot.waker.take() {
                waker.wake();
            }
        } else if let Err(e) = res {
            log::error!(
                "Received an error whose handler has already been dropped: {}",
                ErrorFmt(e)
            );
        }
    }
}

unsafe impl<T: Message<'static>> ReplyHandler for AsyncReplyHandler<T> {
    fn has_fds(&self) -> bool {
        T::HAS_FDS
    }

    fn serial(&self) -> u64 {
        self.serial
    }

    fn handle_result(
        self: Box<Self>,
        socket: &Rc<XconData>,
        parser: &mut Parser<'static>,
        buf: Vec<u8>,
    ) -> Result<(), XconError> {
        let msg = <T::Generic<'static> as Message<'static>>::deserialize(parser);
        let msg = match msg {
            Ok(m) => m,
            Err(e) => {
                let e = Rc::new(e);
                self.done(Err(XconError::XconError(e.clone())));
                return Err(XconError::XconError(e));
            }
        };
        log::trace!("result {:?}", msg);
        let reply = Reply {
            socket: socket.clone(),
            buf,
            t: msg,
        };
        self.done(Ok(reply));
        Ok(())
    }

    fn handle_noreply(self: Box<Self>, socket: &Rc<XconData>) -> Result<(), XconError> {
        if TypeId::of::<T::Generic<'static>>() == TypeId::of::<()>() {
            let reply = Reply {
                socket: socket.clone(),
                buf: vec![],
                t: unsafe { ptr::read(&() as *const () as *const T::Generic<'static>) },
            };
            self.done(Ok(reply));
            Ok(())
        } else {
            self.done(Err(XconError::MissingReply));
            Err(XconError::MissingReply)
        }
    }

    fn handle_error(self: Box<Self>, error: XconError) {
        self.done(Err(error))
    }
}

struct AsyncReplySlot<T: Message<'static>> {
    data: Cell<Option<Result<Reply<T>, XconError>>>,
    waker: Cell<Option<Waker>>,
}

pub struct AsyncReply<T: Message<'static>> {
    slot: Rc<AsyncReplySlot<T>>,
    xorg: Rc<XconData>,
}

impl<T: Message<'static>> Future for AsyncReply<T> {
    type Output = Result<Reply<T>, XconError>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(d) = self.slot.data.take() {
            Poll::Ready(d)
        } else {
            self.slot.waker.set(Some(cx.waker().clone()));
            self.xorg.send_sync();
            Poll::Pending
        }
    }
}

impl Xcon {
    pub fn root_window(&self) -> u32 {
        self.root_window
    }

    pub async fn event(&self) -> Event {
        self.data.events.pop().await
    }

    pub fn generate_id(&self) -> Result<u32, XconError> {
        if self.xid_next.get() == self.xid_max {
            return Err(XconError::XidExhausted);
        }
        let id = self.xid_next.get();
        self.xid_next.set(id + self.xid_inc);
        Ok(id)
    }

    pub async fn connect(state: &Rc<State>) -> Result<Rc<Self>, XconError> {
        let authority = match XAuthority::load() {
            Ok(a) => a,
            Err(e) => {
                log::warn!(
                    "Could not parse Xauthority file. Proceeding without authorization: {}",
                    ErrorFmt(e)
                );
                vec![]
            }
        };
        let display = parse_display()?;
        let mut addr = c::sockaddr_un {
            sun_family: c::AF_UNIX as _,
            ..uapi::pod_zeroed()
        };
        {
            let mut path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
            let _ = write!(path, "/tmp/.X11-unix/X{}", display);
        }
        let fd = uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
            .map(Rc::new)
            .map_os_err(XconError::CreateSocket)?;
        if let Err(e) = state.ring.connect(&fd, &addr).await {
            return Err(XconError::ConnectSocket(e));
        }
        let mut hnbuf = [MaybeUninit::<u8>::uninit(); 256];
        let hn = uapi::gethostname(&mut hnbuf[..])
            .map(|hn| hn.to_bytes())
            .map_os_err(XconError::Hostname)?;
        let (auth_method, auth_value) = 'auth: {
            for auth in &authority {
                if auth.display == display
                    && auth.family == LOCAL
                    && auth.host == hn
                    && auth.method == MIT_MAGIC_COOKIE
                {
                    break 'auth (&auth.method[..], &auth.value[..]);
                }
            }
            (&[], &[])
        };
        Self::connect_to_fd(state, &fd, auth_method, auth_value).await
    }

    pub async fn connect_to_fd(
        state: &Rc<State>,
        fd: &Rc<OwnedFd>,
        auth_method: &[u8],
        auth_value: &[u8],
    ) -> Result<Rc<Self>, XconError> {
        let data = Rc::new(XconData {
            bufio: Rc::new(BufIo::new(fd, &state.ring)),
            in_bufs: Default::default(),
            next_serial: NumCell::new(1),
            last_recv_serial: Cell::new(0),
            reply_handlers: Default::default(),
            dead: Cell::new(false),
            need_sync: Cell::new(false),
            extensions: Default::default(),
            xorg: CloneCell::new(Weak::new()),
            events: Default::default(),
        });
        let outgoing = state.eng.spawn2(
            "xcon send",
            Phase::PostLayout,
            handle_outgoing(data.clone()),
        );
        let mut buf = data.bufio.buf();
        let mut fds = vec![];
        {
            let mut formatter = Formatter::new(&mut fds, &mut buf, 0);
            #[cfg(target_endian = "little")]
            const ENDIAN: u8 = b'l';
            #[cfg(target_endian = "big")]
            const ENDIAN: u8 = b'B';
            formatter.write_packed(&ENDIAN);
            formatter.pad(1);
            formatter.write_packed(&11u16);
            formatter.write_packed(&0u16);
            formatter.write_packed(&(auth_method.len() as u16));
            formatter.write_packed(&(auth_value.len() as u16));
            formatter.pad(2);
            formatter.write_packed(auth_method.as_bytes());
            formatter.align(4);
            formatter.write_packed(auth_value.as_bytes());
            formatter.align(4);
        }
        data.bufio.send(BufIoMessage {
            fds,
            buf: buf.unwrap(),
        });
        let mut incoming = data.bufio.incoming();
        let mut buf = data.in_bufs.pop().unwrap_or_default();
        buf.clear();
        incoming.fill_msg_buf(8, &mut buf).await?;
        let len = u16::from_ne_bytes([buf[6], buf[7]]) as usize * 4;
        incoming.fill_msg_buf(len, &mut buf).await?;
        let mut parser = Parser::new(&buf, vec![]);
        let res: u8 = buf[0];
        if res == 0 {
            parser.pad(1)?;
            let reason_len: u8 = parser.read_pod()?;
            parser.pad(6)?;
            let reason = parser.read_string(reason_len as usize)?;
            return Err(XconError::Connect(reason.to_owned()));
        }
        if res == 2 {
            parser.pad(6)?;
            let reason_len: u16 = parser.read_pod()?;
            let reason = parser.read_string(reason_len as usize * 4)?;
            return Err(XconError::Authenticate(reason.to_owned()));
        }
        let setup = Setup::deserialize(&mut parser)?;
        let incoming = state
            .eng
            .spawn("X incoming", handle_incoming(data.clone(), incoming));
        let slf = Rc::new(Self {
            extensions: data.fetch_extension_data().await?,
            outgoing: Cell::new(Some(outgoing)),
            incoming: Cell::new(Some(incoming)),
            xid_next: Cell::new(setup.resource_id_base),
            xid_inc: 1 << setup.resource_id_mask.trailing_zeros(),
            xid_max: setup.resource_id_mask | setup.resource_id_base,
            root_window: setup.screens[0].root,
            data,
        });
        slf.data.xorg.set(Rc::downgrade(&slf));
        Ok(slf)
    }

    pub fn call<'a, T: Request<'a>>(self: &Rc<Self>, t: &T) -> AsyncReply<T::Reply> {
        log::trace!("send {:?}", t);
        self.data.call_with_serial(t, &self.extensions).0
    }

    pub fn call_with_serial<'a, T: Request<'a>>(
        self: &Rc<Self>,
        t: &T,
    ) -> (AsyncReply<T::Reply>, u64) {
        log::trace!("send {:?}", t);
        self.data.call_with_serial(t, &self.extensions)
    }

    pub fn send_event<'a, T: XEvent<'a>>(
        self: &Rc<Self>,
        propagate: bool,
        destination: u32,
        event_mask: u32,
        t: &T,
    ) -> AsyncReply<()> {
        log::trace!("send {:?}", t);
        self.data
            .send_event(t, &self.extensions, propagate, destination, event_mask)
    }

    pub async fn get_property3<T: PropertyType>(
        self: &Rc<Self>,
        window: u32,
        property: u32,
        ty: u32,
        delete: bool,
        buf: &mut Vec<T>,
    ) -> Result<u32, XconError> {
        let len = buf.len();
        match self.get_property2(window, property, ty, delete, buf).await {
            Ok(n) => Ok(n),
            Err(e) => {
                buf.truncate(len);
                Err(e)
            }
        }
    }

    pub async fn get_property<T: PropertyType>(
        self: &Rc<Self>,
        window: u32,
        property: u32,
        ty: u32,
        buf: &mut Vec<T>,
    ) -> Result<u32, XconError> {
        let len = buf.len();
        match self.get_property2(window, property, ty, false, buf).await {
            Ok(n) => Ok(n),
            Err(e) => {
                buf.truncate(len);
                Err(e)
            }
        }
    }

    async fn get_property2<T: PropertyType>(
        self: &Rc<Self>,
        window: u32,
        property: u32,
        ty: u32,
        delete: bool,
        buf: &mut Vec<T>,
    ) -> Result<u32, XconError> {
        let mut gp = GetProperty {
            delete: delete as _,
            window,
            property,
            ty,
            long_offset: 0,
            long_length: 128,
        };
        let format = size_of::<T>() as u8 * 8;
        loop {
            let res = self.call(&gp).await?;
            let res = res.get();
            if res.format == 0 {
                return Err(XconError::PropertyUnavailable);
            }
            if gp.ty != 0 && gp.ty != res.ty {
                return Err(XconError::InvalidPropertyType);
            }
            gp.ty = res.ty;
            if res.format != format {
                return Err(XconError::InvalidPropertyFormat(format, res.format));
            }
            if res.data.len() % size_of::<T>() != 0 {
                return Err(XconError::IrregularPropertyLength);
            }
            let len = res.data.len() / size_of::<T>();
            buf.reserve(len);
            let (_, uninit) = buf.split_at_spare_mut_bytes_ext();
            uninit[..res.data.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(res.data));
            unsafe {
                buf.set_len(buf.len() + len);
            }
            if res.bytes_after == 0 {
                return Ok(res.ty);
            }
            gp.long_offset += gp.long_length;
        }
    }

    pub async fn create_cursor(
        self: &Rc<Self>,
        pixels: &[Cell<u8>],
        width: i32,
        height: i32,
        xhot: i32,
        yhot: i32,
    ) -> Result<u32, XconError> {
        let cursor_format = 'cursor_format: {
            let res = match self.call(&RenderQueryPictFormats {}).await {
                Ok(r) => r,
                Err(e) => return Err(XconError::QueryPictFormats(Box::new(e))),
            };
            for format in res.get().formats.iter() {
                let valid = format.ty == RENDER_PICT_TYPE_DIRECT
                    && format.depth == 32
                    && format.direct.red_shift == 16
                    && format.direct.red_mask == 0xff
                    && format.direct.green_shift == 8
                    && format.direct.green_mask == 0xff
                    && format.direct.blue_shift == 0
                    && format.direct.blue_mask == 0xff
                    && format.direct.alpha_shift == 24
                    && format.direct.alpha_mask == 0xff;
                if valid {
                    break 'cursor_format format.id;
                }
            }
            return Err(XconError::CursorFormatNotSupported);
        };
        let pixmap = self.generate_id()?;
        let gc = self.generate_id()?;
        let picture = self.generate_id()?;
        let cursor = self.generate_id()?;
        let create_pixmap = self.call(&CreatePixmap {
            depth: 32,
            pid: pixmap,
            drawable: self.root_window,
            width: width as _,
            height: height as _,
        });
        let create_gc = self.call(&CreateGC {
            cid: gc,
            drawable: pixmap,
            values: Default::default(),
        });
        let put_image = self.call(&PutImage {
            format: IMAGE_FORMAT_Z_PIXMAP,
            drawable: pixmap,
            gc,
            width: width as _,
            height: height as _,
            dst_x: 0,
            dst_y: 0,
            left_pad: 0,
            depth: 32,
            data: unsafe { mem::transmute::<&[Cell<u8>], &[u8]>(pixels) },
        });
        self.call(&FreeGC { gc });
        let create_picture = self.call(&RenderCreatePicture {
            pid: picture,
            drawable: pixmap,
            format: cursor_format,
            values: Default::default(),
        });
        self.call(&FreePixmap { pixmap });
        let create_cursor = self.call(&RenderCreateCursor {
            cid: cursor,
            source: picture,
            x: xhot as _,
            y: yhot as _,
        });
        if let Err(e) = create_pixmap.await {
            return Err(XconError::CreatePixmap(Box::new(e)));
        }
        if let Err(e) = create_gc.await {
            return Err(XconError::CreateGc(Box::new(e)));
        }
        if let Err(e) = put_image.await {
            return Err(XconError::PutImage(Box::new(e)));
        }
        if let Err(e) = create_picture.await {
            return Err(XconError::CreatePicture(Box::new(e)));
        }
        if let Err(e) = create_cursor.await {
            return Err(XconError::CreateCursor(Box::new(e)));
        }
        Ok(cursor)
    }
}

impl XconData {
    fn kill(&self) {
        self.bufio.shutdown();
        self.dead.set(true);
        let handlers = mem::take(self.reply_handlers.borrow_mut().deref_mut());
        for handler in handlers {
            handler.handle_error(XconError::Dead);
        }
        if let Some(xorg) = self.xorg.get().upgrade() {
            xorg.outgoing.take();
            xorg.incoming.take();
        }
    }

    #[cold]
    fn dead<T: Message<'static>>(self: &Rc<Self>) -> AsyncReply<T> {
        AsyncReply {
            slot: Rc::new(AsyncReplySlot {
                data: Cell::new(Some(Err(XconError::Dead))),
                waker: Cell::new(None),
            }),
            xorg: self.clone(),
        }
    }

    #[cold]
    fn ext_unavailable<T: Message<'static>>(self: &Rc<Self>, idx: usize) -> AsyncReply<T> {
        AsyncReply {
            slot: Rc::new(AsyncReplySlot {
                data: Cell::new(Some(Err(XconError::ExtensionUnavailable(
                    EXTENSIONS[idx].name(),
                )))),
                waker: Cell::new(None),
            }),
            xorg: self.clone(),
        }
    }

    fn send_event<'a, T: XEvent<'a>>(
        self: &Rc<Self>,
        t: &T,
        extensions: &ExtensionData,
        propagate: bool,
        destination: u32,
        event_mask: u32,
    ) -> AsyncReply<()> {
        if self.dead.get() {
            return self.dead();
        }
        let first_event = match T::EXTENSION {
            None => 0,
            Some(idx) => match extensions.first_event[idx] {
                Some(o) => o,
                _ => return self.ext_unavailable(idx),
            },
        };
        let mut fds = vec![];
        let mut buf = self.bufio.buf();
        let mut formatter = Formatter::new(&mut fds, &mut buf, 0);
        let se = SendEvent {
            propagate: propagate as u8,
            destination,
            event_mask,
        };
        se.serialize(&mut formatter);
        t.serialize(&mut formatter);
        formatter.pad_to(44);
        formatter.write_request_length();
        buf[12] = first_event + T::OPCODE as u8;
        self.need_sync.set(true);
        self.send(fds, buf).0
    }

    fn call<'a, T: Request<'a>>(
        self: &Rc<Self>,
        t: &T,
        extensions: &ExtensionData,
    ) -> AsyncReply<T::Reply> {
        self.call_with_serial(t, extensions).0
    }

    fn call_with_serial<'a, T: Request<'a>>(
        self: &Rc<Self>,
        t: &T,
        extensions: &ExtensionData,
    ) -> (AsyncReply<T::Reply>, u64) {
        if self.dead.get() {
            return (self.dead(), 0);
        }
        let opcode = match T::EXTENSION {
            None => 0,
            Some(idx) => match extensions.opcodes[idx] {
                Some(o) => o,
                _ => return (self.ext_unavailable(idx), 0),
            },
        };
        let mut fds = vec![];
        let mut buf = self.bufio.buf();
        let mut formatter = Formatter::new(&mut fds, &mut buf, opcode);
        t.serialize(&mut formatter);
        formatter.align(4);
        formatter.write_request_length();
        self.need_sync.set(T::IS_VOID);
        self.send(fds, buf)
    }

    fn send<T: Message<'static>>(
        self: &Rc<Self>,
        fds: Vec<Rc<OwnedFd>>,
        buf: DynamicBuf,
    ) -> (AsyncReply<T>, u64) {
        self.bufio.send(BufIoMessage {
            fds,
            buf: buf.unwrap(),
        });
        let slot = Rc::new(AsyncReplySlot {
            data: Cell::new(None),
            waker: Cell::new(None),
        });
        let serial = self.next_serial.fetch_add(1);
        let handler = Box::new(AsyncReplyHandler {
            serial,
            slot: Rc::downgrade(&slot),
        });
        self.reply_handlers.borrow_mut().push_back(handler);
        let rep = AsyncReply {
            slot,
            xorg: self.clone(),
        };
        (rep, serial)
    }

    fn send_sync(&self) {
        if !self.need_sync.replace(false) {
            return;
        }
        let mut fds = vec![];
        let mut buf = self.bufio.buf();
        let mut formatter = Formatter::new(&mut fds, &mut buf, 0);
        GetInputFocus {}.serialize(&mut formatter);
        formatter.write_request_length();
        self.bufio.send(BufIoMessage {
            fds,
            buf: buf.unwrap(),
        });
        self.next_serial.fetch_add(1);
    }

    async fn fetch_extension_data(self: &Rc<Self>) -> Result<Rc<ExtensionData>, XconError> {
        let mut ext_by_name = AHashMap::new();
        for e in EXTENSIONS.iter().copied() {
            ext_by_name.insert(e.name().as_bytes().as_bstr(), e);
        }
        let mut ed = ExtensionData::default();
        let extensions = self.call(&ListExtensions {}, &ed).await?;
        let mut pending = vec![];
        for name in extensions.get().names.iter() {
            pending.push((name.val, self.call(&QueryExtension { name: name.val }, &ed)));
        }
        for (name, data) in pending {
            let data = match data.await {
                Ok(d) => d,
                Err(e) => return Err(XconError::QueryExtension(name.to_owned(), Box::new(e))),
            };
            let data = data.get();
            if data.present != 0 {
                let e = ext_by_name.get(name).copied();
                if data.first_event > 0 {
                    ed.events.push(ExtensionIdRange {
                        name: name.to_owned(),
                        extension: e,
                        first: data.first_event,
                    });
                }
                if data.first_error > 0 {
                    ed.errors.push(ExtensionIdRange {
                        name: name.to_owned(),
                        extension: e,
                        first: data.first_error,
                    });
                }
                if let Some(e) = e {
                    ed.opcodes[e as usize] = Some(data.major_opcode);
                    ed.first_event[e as usize] = Some(data.first_event);
                    ed.ext_by_opcode.insert(data.major_opcode, e);
                }
            }
        }
        ed.events.sort_by_key(|e| e.first);
        ed.errors.sort_by_key(|e| e.first);
        let ed = Rc::new(ed);
        self.extensions.set(Some(ed.clone()));
        Ok(ed)
    }
}

fn parse_display() -> Result<u32, XconError> {
    let display = match std::env::var(DISPLAY) {
        Ok(d) => d,
        _ => return Err(XconError::DisplayNotSet),
    };
    let num = match display.strip_prefix(":") {
        Some(p) => p,
        _ => return Err(XconError::InvalidDisplayFormat),
    };
    let num = match num.parse() {
        Ok(v) => v,
        _ => return Err(XconError::InvalidDisplayFormat),
    };
    Ok(num)
}

pub unsafe trait PropertyType {}

unsafe impl PropertyType for u8 {}
unsafe impl PropertyType for u16 {}
unsafe impl PropertyType for u32 {}
