/*

Jappix - An open social platform
Implementation of XEP-0313: Message Archive Management

-------------------------------------------------

License: AGPL
Author: Valérian Saliou

*/

// Bundle
var MAM = (function () {

    /**
     * Alias of this
     * @private
     */
    var self = {};


    /* Constants */
    self.REQ_MAX = 50;
    self.SCROLL_THRESHOLD = 200;

    self.PREF_DEFAULTS = {
        'always' : 1,
        'never'  : 1,
        'roster' : 1
    };


    /* Variables */
    self.map_reqs = {};
    self.map_pending = {};
    self.map_states = {};
    self.msg_queue = {};


    /**
     * Gets the MAM configuration
     * @public
     * @return {undefined}
     */
    self.getConfig = function() {

        try {
            // Lock the archiving options
            $('#archiving').attr('disabled', true);
            
            // Get the archiving configuration
            var iq = new JSJaCIQ();
            iq.setType('get');

            iq.appendNode('prefs', { 'xmlns': NS_URN_MAM });

            con.send(iq, self.handleConfig);
        } catch(e) {
            Console.error('MAM.getConfig', e);
        }

    };


    /**
     * Handles the MAM configuration
     * @public
     * @param {object} iq
     * @return {undefined}
     */
    self.handleConfig = function(iq) {

        try {
            if(iq.getType() != 'error') {
                // Read packet
                var cur_default = $(iq.getNode()).find('prefs[xmlns="' + NS_URN_MAM + '"]').attr('default') || 'never';

                if(!(cur_default in self.PREF_DEFAULTS)) {
                    cur_default = 'never';
                }

                // Apply value to options
                $('#archiving').val(cur_default);
            }

            // Unlock the archiving option
            $('#archiving').removeAttr('disabled');

            // All done.
            Options.wait('mam');
        } catch(e) {
            Console.error('MAM.handleConfig', e);
        }

    };


    /**
     * Sets the MAM configuration
     * @public
     * @param {string} pref_default
     * @return {undefined}
     */
    self.setConfig = function(pref_default) {

        try {
            // Check parameters
            if(!(pref_default in self.PREF_DEFAULTS)) {
                pref_default = 'never';
            }

            // Send new configuration
            var iq = new JSJaCIQ();
            iq.setType('set');

            iq.appendNode('prefs', { 'xmlns': NS_URN_MAM, 'default': pref_default });

            con.send(iq);
        } catch(e) {
            Console.error('MAM.setConfig', e);
        }

    };


    /**
     * Removes all (or given) MAM archives
     * @public
     * @param {object} args
     * @return {undefined}
     */
    self.purgeArchives = function(args) {

        try {
            if(typeof args != 'object') {
                args = {};
            }

            var iq = new JSJaCIQ();
            iq.setType('set');

            var purge = iq.appendNode('purge', { 'xmlns': NS_METRONOME_MAM_PURGE });

            for(var c in args) {
                if(args[c])  purge.appendChild(iq.buildNode(c, {'xmlns': NS_METRONOME_MAM_PURGE}, args[c]));
            }
            
            con.send(iq, function(iq) {
                if(iq.getType() == 'result') {
                    Console.info('Archives purged (MAM).');
                } else {
                    Console.error('Error purging archives (MAM).');
                }
            });
        } catch(e) {
            Console.error('MAM.purgeArchives', e);
        }

    };


    /**
     * Gets the MAM configuration
     * @public
     * @param {object} args
     * @param {object} rsm_args
     * @param {function} callback
     * @return {undefined}
     */
    self.getArchives = function(args, rsm_args, callback) {

        try {
            if(typeof args != 'object') {
                args = {};
            }

            var req_id = genID();

            if(args['with']) {
                self.map_pending[args['with']] = 1;
                self.map_reqs[req_id] = args['with'];
            }

            var iq = new JSJaCIQ();
            iq.setType('get');
            iq.setID(req_id);

            var query = iq.setQuery(NS_URN_MAM);

            for(var c in args) {
                if(args[c] !== null)  query.appendChild(iq.buildNode(c, {'xmlns': NS_URN_MAM}, args[c]));
            }

            if(rsm_args && typeof rsm_args == 'object') {
                var rsm_set = query.appendChild(iq.buildNode('set', {'xmlns': NS_RSM}));

                for(var r in rsm_args) {
                    if(rsm_args[r] !== null)  rsm_set.appendChild(iq.buildNode(r, {'xmlns': NS_RSM}, rsm_args[r]));
                }
            }

            con.send(iq, function(res_iq) {
                self.handleArchives(res_iq, callback);
            });
        } catch(e) {
            Console.error('MAM.getArchives', e);
        }

    };


    /**
     * Handles the MAM configuration
     * @public
     * @param {object} iq
     * @param {function} callback
     * @return {undefined}
     */
    self.handleArchives = function(iq, callback) {

        try {
            var res_id = iq.getID();
            var res_with;

            if(res_id && res_id in self.map_reqs) {
                res_with = self.map_reqs[res_id];
            }

            if(iq.getType() != 'error') {
                if(res_with) {
                    var res_sel = $(iq.getQuery());
                    var res_rsm_sel = res_sel.find('set[xmlns="' + NS_RSM + '"]');

                    // Store that data
                    self.map_states[res_with] = {
                        'date': {
                            'start': res_sel.find('start').eq(0).text(),
                            'end': res_sel.find('end').eq(0).text()
                        },

                        'rsm': {
                            'first': res_rsm_sel.find('first').eq(0).text(),
                            'last': res_rsm_sel.find('last').eq(0).text(),
                            'count': parseInt(res_rsm_sel.find('count').eq(0).text() || 0)
                        }
                    };

                    // Generate stamps for easy operations
                    var start_stamp = DateUtils.extractStamp(Date.jab2date(self.map_states[res_with].date.start));
                    var start_end = DateUtils.extractStamp(Date.jab2date(self.map_states[res_with].date.end));

                    // Create MAM messages target
                    var target_html = '<div class="mam-chunk" data-start="' + Common.encodeQuotes(start_stamp) + '" data-end="' + Common.encodeQuotes(start_end) + '"></div>';
                    
                    var target_content_sel = $('#' + hex_md5(res_with) + ' .content');
                    var target_wait_sel = target_content_sel.find('.wait-mam');

                    if(target_wait_sel.size()) {
                        target_wait_sel.after(target_html);
                    } else {
                        target_content_sel.prepend(target_html);
                    }

                    // Any enqueued message to display?
                    if(typeof self.msg_queue[res_with] == 'object') {
                        for(var i in self.msg_queue[res_with]) {
                            (self.msg_queue[res_with][i])();
                        }

                        delete self.msg_queue[res_with];
                    }

                    // Remove XID from pending list
                    if(res_with in self.map_pending) {
                        delete self.map_pending[res_with];
                    }

                    Console.info('Got archives from: ' + res_with);
                } else {
                    Console.warn('Could not associate archive response with a known JID.');
                }
            } else {
                Console.error('Error handing archives (MAM).');
            }

            // Execute callback?
            if(typeof callback == 'function') {
                callback(iq);
            }
        } catch(e) {
            Console.error('MAM.handleArchives', e);
        }

    };


    /**
     * Handles a MAM-forwarded message stanza
     * @public
     * @param {object} fwd_stanza
     * @param {object} c_delay
     * @return {undefined}
     */
    self.handleMessage = function(fwd_stanza, c_delay) {

        try {
            // Build message node
            var c_message = fwd_stanza.find('message');

            if(c_message[0]) {
                // Re-build a proper JSJaC message stanza
                var message = JSJaCPacket.wrapNode(c_message[0]);

                // Check message type
                var type = message.getType() || 'chat';

                if(type == 'chat') {
                    // Read message data
                    var xid = Common.bareXID(Common.getStanzaFrom(message));
                    var id = message.getID();
                    var from_xid = xid;
                    var b_name = Name.getBuddy(xid);
                    var mode = (xid == Common.getXID()) ? 'me': 'him';

                    // Refactor chat XID (in case we were the sender of the archived message)
                    if(mode == 'me') {
                        xid = Common.bareXID(message.getTo());
                    }

                    var hash = hex_md5(xid);
                    var body = message.getBody();

                    // Read delay (required since we deal w/ a past message!)
                    var time, stamp;
                    var delay = c_delay.attr('stamp');

                    if(delay) {
                        time = DateUtils.relative(delay);
                        stamp = DateUtils.extractStamp(Date.jab2date(delay));
                    }
                    
                    // Last-minute checks before display
                    if(time && stamp && body) {
                        var mam_chunk_path = '#' + hash + ' .mam-chunk';

                        // No chat auto-scroll?
                        var no_scroll = Common.exists(mam_chunk_path);

                        // Select the custom target
                        var c_target_sel = function() {
                            return $(mam_chunk_path).filter(function() {
                                return $(this).attr('data-start') <= stamp && $(this).attr('data-end') >= stamp;
                            }).filter(':first');
                        };

                        // Display the message in that target
                        var c_msg_display = function() {
                            Message.display(type, from_xid, hash, b_name.htmlEnc(), body, time, stamp, 'old-message', true, null, mode, null, c_target_sel(), no_scroll);
                        };

                        // Hack: do not display the message in case we would duplicate it w/ current session messages
                        //       only used when initiating a new chat, avoids collisions
                        if(!(xid in self.map_states) && $('#' + hash).find('.one-line.user-message:last').text() == body) {
                            return;
                        }

                        if(c_target_sel().size()) {
                            // Display the message in that target
                            c_msg_display();
                        } else {
                            // Delay display (we may not have received the MAM reply ATM)
                            if(typeof self.msg_queue[xid] != 'object') {
                                self.msg_queue[xid] = [];
                            }

                            self.msg_queue[xid].push(c_msg_display);
                        }
                    }
                }
            }
        } catch(e) {
            Console.error('MAM.handleMessage', e);
        }

    };


    /**
     * Return class scope
     */
    return self;

})();