/**
 * Disco Strophe Plugin
 * Implement http://xmpp.org/extensions/xep-0030.html
 * TODO: manage node hierarchies, and node on info request
 */

import { Strophe, $iq } from "strophe.js";

Strophe.addConnectionPlugin("disco", {
    _connection: null,
    _identities: [],
    _features: [],
    _items: [],

    /** Function: init
     * Plugin init
     *
     * Parameters:
     *   (Strophe.Connection) conn - Strophe connection
     */
    init(conn) {
        this._connection = conn;
        this._identities = [];
        this._features = [];
        this._items = [];
        // disco info
        conn.addHandler(
            this._onDiscoInfo.bind(this),
            Strophe.NS.DISCO_INFO,
            "iq",
            "get",
            null,
            null,
        );
        // disco items
        conn.addHandler(
            this._onDiscoItems.bind(this),
            Strophe.NS.DISCO_ITEMS,
            "iq",
            "get",
            null,
            null,
        );
    },

    /** Function: addIdentity
     * See http://xmpp.org/registrar/disco-categories.html
     * Parameters:
     *   (String) category - category of identity (like client, automation, etc ...)
     *   (String) type - type of identity (like pc, web, bot , etc ...)
     *   (String) name - name of identity in natural language
     *   (String) lang - lang of name parameter
     *
     * Returns:
     *   Boolean
     */
    addIdentity(category, type, name, lang) {
        for (let i = 0; i < this._identities.length; i++) {
            if (
                this._identities[i].category === category &&
                this._identities[i].type === type &&
                this._identities[i].name === name &&
                this._identities[i].lang === lang
            ) {
                return false;
            }
        }
        this._identities.push({
            category,
            type,
            name,
            lang,
        });
        return true;
    },

    /** Function: addFeature
     *
     * Parameters:
     *   (String) var_name - feature name (like jabber:iq:version)
     *
     * Returns:
     *   boolean
     */
    addFeature(var_name) {
        for (let i = 0; i < this._features.length; i++) {
            if (this._features[i] === var_name) {
                return false;
            }
        }
        this._features.push(var_name);
        return true;
    },

    /** Function: addItem
     *
     * Parameters:
     *   (String) jid
     *   (String) name
     *   (String) node
     *   (Function) call_back
     *
     * Returns:
     *   boolean
     */
    addItem(jid, name, node, call_back) {
        if (node && !call_back) {
            return false;
        }
        this._items.push({
            jid,
            name,
            node,
            call_back,
        });
        return true;
    },

    /** Function: info
     * Info query
     *
     * Parameters:
     *   (Function) call_back
     *   (String) jid
     *   (String) node
     */
    info(jid, node, success, error) {
        const attrs = { xmlns: Strophe.NS.DISCO_INFO };
        if (node) {
            attrs.node = node;
        }

        const info = $iq({
            from: this._connection.jid,
            to: jid,
            type: "get",
        }).c("query", attrs);
        this._connection.sendIQ(info, success, error);
    },

    /** Function: items
     * Items query
     *
     * Parameters:
     *   (Function) call_back
     *   (String) jid
     *   (String) node
     */
    items(jid, node, success, error) {
        const attrs = { xmlns: Strophe.NS.DISCO_ITEMS };
        if (node) {
            attrs.node = node;
        }

        const items = $iq({
            from: this._connection.jid,
            to: jid,
            type: "get",
        }).c("query", attrs);
        this._connection.sendIQ(items, success, error);
    },

    /** PrivateFunction: _onDiscoInfo
     * Called when receive info request
     */
    _onDiscoInfo(stanza) {
        const id = stanza.getAttribute("id");
        const from = stanza.getAttribute("from");
        const node = stanza
            .getElementsByTagName("query")[0]
            .getAttribute("node");
        let attrs = { xmlns: Strophe.NS.DISCO_INFO };
        if (node) {
            attrs.node = node;
        }
        const iqresult = $iq({ type: "result", id, to: from }).c(
            "query",
            attrs,
        );
        for (let i = 0; i < this._identities.length; i++) {
            attrs = {
                category: this._identities[i].category,
                type: this._identities[i].type,
            };
            if (this._identities[i].name) {
                attrs.name = this._identities[i].name;
            }
            if (this._identities[i].lang) {
                attrs["xml:lang"] = this._identities[i].lang;
            }
            iqresult.c("identity", attrs).up();
        }
        for (let i = 0; i < this._features.length; i++) {
            iqresult.c("feature", { var: this._features[i] }).up();
        }
        this._connection.send(iqresult.tree());
    },

    /** PrivateFunction: _onDiscoItems
     * Called when receive items request
     */
    _onDiscoItems(stanza) {
        const id = stanza.getAttribute("id");
        const from = stanza.getAttribute("from");
        const query_attrs = { xmlns: Strophe.NS.DISCO_ITEMS };
        const node = stanza
            .getElementsByTagName("query")[0]
            .getAttribute("node");
        let items = null;
        if (node) {
            query_attrs.node = node;
            for (let i = 0; i < this._items.length; i++) {
                if (this._items[i].node === node) {
                    items = this._items[i].call_back(stanza);
                    break;
                }
            }
        } else {
            items = this._items;
        }
        if (!items) {
            items = [];
        }
        const iqresult = $iq({ type: "result", id, to: from }).c(
            "query",
            query_attrs,
        );
        for (let i = 0; i < items.length; i++) {
            const attrs = { jid: items[i].jid };
            if (items[i].name) {
                attrs.name = items[i].name;
            }
            if (items[i].node) {
                attrs.node = items[i].node;
            }
            iqresult.c("item", attrs).up();
        }
        this._connection.send(iqresult.tree());
    },
});

export default Strophe;
