class ProxyIcon : Object {
    StatusNotifierItem item;
    Gtk.StatusIcon icon;
    DbusmenuGtk.Menu menu;

    public ProxyIcon(string name, string object) throws IOError {
        init.begin(name, object);
    }

    async void init(string name, string object) {
        item = yield Bus.get_proxy(BusType.SESSION, name, object, DBusProxyFlags.NONE, null);
        icon = new Gtk.StatusIcon();

        item.changed.connect(property => {
            switch(property) {
            case "icon-name":
            case "icon-pixmap": set_icon(); break;
            case "tool-tip": set_tooltip(); break;
            }
        });

        set_icon();
        icon.set_name(item.id); // MUST be called after an icon is set due to Gdk internals
        set_tooltip();

        if (item.menu != null) {
            stdout.printf("%s set menu: %s\n", name, item.menu);
            menu = new DbusmenuGtk.Menu(name, item.menu);
        }

        icon.activate.connect(() => item.activate(0, 0)); // TODO: consider smarter coordinate hints
        icon.button_press_event.connect(on_button_press);
    }

    bool on_button_press(Gdk.EventButton event) {
        if (event.button == 3) {
            if (menu != null) {
                menu.popup_at_pointer(event);
                return true;
            }

            item.context_menu((int)event.x_root, (int)event.y_root);
            return true;
        }
        return false;
    }

    void set_icon() {
        var name = item.icon_name;
        if (name != null && name != "") {
            var theme = Gtk.IconTheme.get_for_screen(icon.screen);
            var path = item.icon_theme_path;
            if (path != null) {
                string[] paths;
                theme.get_search_path(out paths);
                if (!(path in paths)) {
                    theme.append_search_path(path);
                }
            }
            theme.rescan_if_needed();

            set_icon_name(name);
            if (theme.has_icon(name)) {
                return;
            }
        }

        var pixmaps = item.icon_pixmap;
        if (pixmaps != null) {
            set_icon_pixmap(pixmaps);
        }
    }

    void set_icon_name(string name) {
        icon.set_from_icon_name(name);
    }

    void set_icon_pixmap(IconPixmap[] pixmaps) {
        var pixmap = pixmaps[0]; // TODO: choose the best one

        // Convert from ARGB to RGBA
        for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 3) {
            var alpha = pixmap.data[i];
            pixmap.data[i] = pixmap.data[i + 1];
            pixmap.data[i + 1] = pixmap.data[i + 2];
            pixmap.data[i + 2] = pixmap.data[i + 3];
            pixmap.data[i + 3] = alpha;
        }

        var pixbuf = new Gdk.Pixbuf.from_data(pixmap.data, Gdk.Colorspace.RGB,
                true, 8, pixmap.width, pixmap.height, pixmap.width * 4);
        icon.set_from_pixbuf(pixbuf);
    }

    void set_tooltip() {
        var tooltip = item.tool_tip;
        string title = tooltip.title;
        string text = tooltip.text;

        if (text != null && text != "") {
            title += @"\n$(tooltip.text)";
        }
        icon.set_tooltip_markup(title);
    }
}
