Documentation
use crate::error::{UiError, UiResult};
use crate::window::x11::ime::context::Context;
use dbus::blocking::Connection;
use dbus::channel::Channel;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::sync::Arc;
use std::time::Duration;
use std::{env, fs};

pub const DEST: &'static str = "org.freedesktop.IBus";
pub const DBUS_PATH: &'static str = "/org/freedesktop/IBus";

pub struct Bus {
    conn: Arc<Connection>,
    ctx: Context,

}

impl Bus {
    pub fn new(name: &str) -> UiResult<Bus> {
        let addr = Bus::get_address()?;
        let mut channel = Channel::open_private(&addr)?;
        channel.register()?;
        let conn = Arc::new(Connection::from(channel));
        let ctx = Context::init(conn.clone(), name)?;
        Ok(Bus {
            conn,
            ctx,
        })
    }

    pub fn process(&self, timeout: Duration) -> UiResult<bool> {
        let processed = self.conn.process(timeout)?;
        Ok(processed)
    }

    fn get_machine_id() -> UiResult<String> {
        let mid = "/etc/machine-id";
        let mid2 = "/var/lib/dbus/machine-id";
        let machine_id = fs::read_to_string(mid).or_else(|_| fs::read_to_string(mid2))?;
        Ok(machine_id.trim().to_string())
    }


    fn get_address() -> UiResult<String> {
        if let Ok(addr) = env::var("IBUS_ADDRESS") { return Ok(addr); }
        let display = env::var("DISPLAY").unwrap_or(":0.0".to_string());
        let mut displays = display.split(":");
        let mut host = displays.next().ok_or("获取显示主机错误")?.to_string();
        if host == "" { host = "unix".to_string(); }
        let num = displays.next().ok_or("获取显示编号错误")?.split(".").next().ok_or("获取显示编号1错误")?;
        let config_home = env::var("XDG_CONFIG_HOME").or_else(|_| {
            let mut home = env::var("HOME").or(Err("无法获取config路径"))?;
            home += "/.config";
            Ok::<String, UiError>(home)
        })?;
        let machine_id = Bus::get_machine_id()?;
        let addr_fp = format!("{}/ibus/bus/{}-{}-{}", config_home, machine_id, host, num);
        let addr_file = File::open(&addr_fp)?;
        let reader = BufReader::new(addr_file);
        let prefix = "IBUS_ADDRESS=";
        for line in reader.lines() {
            let line = line?;
            match line.trim_start().strip_prefix(prefix) {
                None => continue,
                Some(addr) => return Ok(addr.to_string())
            }
        }
        Err(format!("找不到Dbus addr: {}", prefix).into())
    }

    pub fn ctx(&self) -> &Context {
        &self.ctx
    }

}