struct IconPixmap {
    int32 width;
    int32 height;
    uint8[] data;
}

struct ToolTip {
    string icon_title;
    IconPixmap[] icon_pixmap;
    string title;
    string text;
}

[DBus (name = "org.kde.StatusNotifierItem")]
interface StatusNotifierItem : Object {
    public const string OBJECT = "/StatusNotifierItem";

    // Properties
    public abstract string id { owned get; }
    public abstract string title { owned get; }
    public abstract string icon_name { owned get; }
    public abstract IconPixmap[] icon_pixmap { owned get; }
    public abstract ToolTip tool_tip { owned get; }
    public abstract ObjectPath menu { owned get; }

    // AppIndicator TODO: split in separate interface
    public abstract string icon_theme_path { owned get; }

    // Methods
    public abstract void activate(int x, int y);
    public abstract void context_menu(int x, int y);

    // Signals
    public virtual signal void new_title() {
        update_cache("Title", "title");
    }

    public virtual signal void new_icon() {
        update_cache("IconName", "icon-name");
        update_cache("IconPixmap", "icon-pixmap");

        // AppIndicator only
        update_cache("IconThemePath", "icon-theme-path");
    }

    public virtual signal void new_tool_tip() {
        update_cache("ToolTip", "tool-tip");
    }

    // Internal cache updates and signalling
    //
    // This looks like the most ugly implementation ever, however:
    // - Vala DBus abstraction is abstract
    // - GObject notify signals doesn't work
    // - g_properties_changed does not get called automatically when the cache is updated
    // - this is simple and just works
    internal abstract signal void changed(string property);

    void update_cache(string property, string name) {
        var me = this as DBusProxy;

        var par = new Variant("(ss)", me.get_interface_name(), property);
        me.call.begin("org.freedesktop.DBus.Properties.Get", par, 0, -1, null, (obj, result) => {
            try {
                Variant res, v;
                res = me.call.end(result);
                res.get("(v)", out v);

                if (v != me.get_cached_property(property)) {
                    me.set_cached_property(property, v);
                    (me as StatusNotifierItem).changed(name);
                }
            } catch (DBusError.INVALID_ARGS e) { /* Ignore properties that don't exist */ }
        });
    }
}
