Documentation
use crate::error::UiResult;
use dbus::blocking::{Connection, Proxy};
use dbus::channel::Token;
use dbus::{Message, Path};
use std::sync::Arc;
use std::time::Duration;
use crate::window::x11::ime::bus::{DBUS_PATH, DEST};
use crate::window::x11::ime::flag::Modifiers;
use crate::window::x11::ime::signal::{CommitText, UpdatePreeditText};

pub const INTERFACE_NAME: &'static str = "org.freedesktop.IBus.InputContext";


pub struct Context {
    conn: Arc<Connection>,
    obj_path: Path<'static>,
}

impl Context {
    pub fn init(conn: Arc<Connection>, name: &str) -> UiResult<Context> {
        let proxy = conn.with_proxy(DEST, DBUS_PATH, Duration::from_secs(1));
        let (obj_path, ): (Path,) = proxy.method_call(DEST, "CreateInputContext", (name,))?;
        Ok(Context {
            conn,
            obj_path,
        })
    }

    pub fn set_capabilities(&self, caps: u32) -> UiResult<()> {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        let () = proxy.method_call(INTERFACE_NAME, "SetCapabilities", (caps,))?;
        Ok(())
    }

    pub fn set_cursor_location(&self, x: i32, y: i32, w: i32, h: i32) -> UiResult<()> {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        let () = proxy.method_call(INTERFACE_NAME, "SetCursorLocation", (x, y, w, h))?;
        Ok(())
    }

    pub fn focus_in(&self) -> UiResult<()> {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        let () = proxy.method_call(INTERFACE_NAME, "FocusIn", ())?;
        Ok(())
    }

    pub fn focus_out(&self) -> UiResult<()> {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        let () = proxy.method_call(INTERFACE_NAME, "FocusOut", ())?;
        Ok(())
    }

    pub fn on_commit_text<F>(&self, mut callback: F) -> UiResult<Token>
    where
        F: FnMut(CommitText, &Connection, &Message) -> bool + Send + 'static,
    {
        let token = self.with_proxy(|p| {
            p.match_signal(move |a: CommitText, b: &Connection, c: &Message| {
                (callback)(a, b, c)
            })
        })?;
        Ok(token)
    }

    pub fn on_update_preedit_text<F>(&self, mut callback: F) -> UiResult<Token>
    where
        F: FnMut(UpdatePreeditText, &Connection, &Message) -> bool + Send + 'static,
    {
        let token = self.with_proxy(|p| {
            p.match_signal(move |a: UpdatePreeditText, b: &Connection, c: &Message| {
                callback(a, b, c)
            })
        })?;
        Ok(token)
    }

    pub fn process_key_event(&self, sym: u32, code: u32, modifiers: Modifiers) -> UiResult<bool> {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        let key_args = (sym, code, modifiers as u32);
        let (handled, ): (bool,) = proxy.method_call(INTERFACE_NAME, "ProcessKeyEvent", key_args)?;
        Ok(handled)
    }

    pub fn with_proxy<R, F: FnOnce(Proxy<&Connection>) -> R>(&self, f: F) -> R {
        let proxy = self.conn.with_proxy(DEST, &self.obj_path, Duration::from_secs(1));
        f(proxy)
    }
}