'use strict';

var utils = require('../../qbUtils');
var config = require('../../qbConfig');

var ERR_UNKNOWN_INTERFACE = 'Unknown interface. SDK support browser / node env.';

var MARKERS = {
    CLIENT: 'jabber:client',
    CHAT: 'urn:xmpp:chat-markers:0',
    STATES: 'http://jabber.org/protocol/chatstates',
    MARKERS: 'urn:xmpp:chat-markers:0',
    CARBONS: 'urn:xmpp:carbons:2',
    ROSTER: 'jabber:iq:roster',
    MUC: 'http://jabber.org/protocol/muc',
    PRIVACY: 'jabber:iq:privacy',
    LAST: 'jabber:iq:last'
};

function ChatNotConnectedError(message, fileName, lineNumber) {
    var instance = new Error(message, fileName, lineNumber);
    instance.name = 'ChatNotConnectedError';
    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, ChatNotConnectedError);
    }
    return instance;
}

ChatNotConnectedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});

if (Object.setPrototypeOf) {
    Object.setPrototypeOf(ChatNotConnectedError, Error);
} else {
    ChatNotConnectedError.__proto__ = Error; // jshint ignore:line
}

var qbChatHelpers = {
    MARKERS: MARKERS,
    ChatNotConnectedError: ChatNotConnectedError,
    /**
     * @param {params} this object may contains Jid or Id property
     * @return {string} jid of user
     */
    buildUserJid: function(params) {
        var jid;

        if ('userId' in params) {
            jid = params.userId + '-' + config.creds.appId + '@' + config.endpoints.chat;

            if ('resource' in params) {
                jid = jid + '/' + params.resource;
            }
        } else if ('jid' in params) {
            jid = params.jid;
        }

        return jid;
    },
    createStanza: function(builder, params, type) {
        var stanza;

        if(utils.getEnv().browser) {
            stanza = builder(params);
        } else {
            stanza = new builder(type ? type : 'message', params);
        }

        return stanza;
    },
    getAttr: function(el, attrName) {
        var attr;

        if(!el) {
            return null;
        }

        if(typeof el.getAttribute === 'function') {
            attr = el.getAttribute(attrName);
        } else if(el.attrs) {
            attr = el.attrs[attrName];
        }

        return attr ? attr : null;
    },
    getElement: function(stanza, elName) {
        var el;

        if(typeof stanza.querySelector === 'function') {
            el = stanza.querySelector(elName);
        } else if(typeof stanza.getChild === 'function'){
            el = stanza.getChild(elName) || stanza.children.find(child => typeof child.getChild === 'function' && this.getElement(child, elName));
        } else {
            throw ERR_UNKNOWN_INTERFACE;
        }

        return el ? el : null;
    },
    getAllElements: function(stanza, elName) {
        var el;

        if(typeof stanza.querySelectorAll === 'function') {
            el = stanza.querySelectorAll(elName);
        } else if(typeof stanza.getChild === 'function'){
            el = stanza.getChild(elName);
        } else {
            throw ERR_UNKNOWN_INTERFACE;
        }

        return el ? el : null;
    },
    getElementText: function(stanza, elName) {
        var el,
            txt;

        if(typeof stanza.querySelector === 'function') {
            el = stanza.querySelector(elName);
            txt = el ? el.textContent : null;
        } else if(typeof stanza.getChildText === 'function') {
            txt = stanza.getChildText(elName) || stanza.children.find(child => typeof child.getChildText === 'function' && this.getElementText(child, elName));
        } else {
            throw ERR_UNKNOWN_INTERFACE;
        }

        return txt ? txt : null;
    },
    _JStoXML: function(title, obj, msg) {
        var self = this;

        msg.c(title);

        Object.keys(obj).forEach(function(field) {
            if (typeof obj[field] === 'object') {
                self._JStoXML(field, obj[field], msg);
            } else {
                msg.c(field).t(obj[field]).up();
            }
        });

        msg.up();
    },
    _XMLtoJS: function(extension, title, obj) {
        var self = this;

        extension[title] = {};

        for (var i = 0, len = obj.childNodes.length; i < len; i++) {
            if (obj.childNodes[i].childNodes.length > 1) {
                extension[title] = self._XMLtoJS(extension[title], obj.childNodes[i].tagName, obj.childNodes[i]);
            } else {
                extension[title][obj.childNodes[i].tagName] = obj.childNodes[i].textContent;
            }
        }
        return extension;
    },
    filledExtraParams: function(stanza, extension) {
        var helper = this;

        Object.keys(extension).forEach(function(field) {
            if (field === 'attachments') {
                extension[field].forEach(function(attach) {
                    if (utils.getEnv().browser) {
                        stanza.c('attachment', attach).up();
                    } else {
                        stanza.getChild('extraParams')
                            .c('attachment', attach).up();
                    }
                });
            } else if (typeof extension[field] === 'object') {
                helper._JStoXML(field, extension[field], stanza);
            } else {
                if (utils.getEnv().browser) {
                    stanza.c(field).t(extension[field]).up();
                } else {
                    stanza.getChild('extraParams')
                        .c(field).t(extension[field]).up();
                }
            }
        });

        stanza.up();

        return stanza;
    },
    parseExtraParams: function(extraParams) {
        var self = this;

        if (!extraParams) {
            return null;
        }

        var extension = {};

        var dialogId,
            attach,
            attributes;

        var attachments = [];

        if (utils.getEnv().browser) {
            for (var i = 0, len = extraParams.childNodes.length; i < len; i++) {
                // parse attachments
                if (extraParams.childNodes[i].tagName === 'attachment') {
                    attach = {};
                    attributes = extraParams.childNodes[i].attributes;

                    for (var j = 0, len2 = attributes.length; j < len2; j++) {
                        if (attributes[j].name === 'size') {
                            attach[attributes[j].name] = parseInt(attributes[j].value);
                        } else {
                            attach[attributes[j].name] = attributes[j].value;
                        }
                    }

                    attachments.push(attach);

                    // parse 'dialog_id'
                } else if (extraParams.childNodes[i].tagName === 'dialog_id') {
                    dialogId = extraParams.childNodes[i].textContent;
                    extension.dialog_id = dialogId;

                    // parse other user's custom parameters
                } else {
                    if (extraParams.childNodes[i].childNodes.length > 1) {
                        // Firefox issue with 4K XML node limit:
                        // http://www.coderholic.com/firefox-4k-xml-node-limit/
                        var nodeTextContentSize = extraParams.childNodes[i].textContent.length;

                        if (nodeTextContentSize > 4096) {
                            var wholeNodeContent = "";
                            for (var k=0; k<extraParams.childNodes[i].childNodes.length; ++k) {
                                wholeNodeContent += extraParams.childNodes[i].childNodes[k].textContent;
                            }
                            extension[extraParams.childNodes[i].tagName] = wholeNodeContent;
                        } else {
                            extension = self._XMLtoJS(extension, extraParams.childNodes[i].tagName, extraParams.childNodes[i]);
                        }
                    } else {
                        extension[extraParams.childNodes[i].tagName] = extraParams.childNodes[i].textContent;
                    }
                }
            }

        } else {
            for (var c = 0, lenght = extraParams.children.length; c < lenght; c++) {
                if (extraParams.children[c].name === 'attachment') {
                    attach = {};
                    attributes = extraParams.children[c].attrs;

                    var attrKeys = Object.keys(attributes);

                    for (var l = 0; l < attrKeys.length; l++) {
                        if(attrKeys[l] === 'size'){
                            attach.size = parseInt(attributes.size);
                        } else {
                            attach[attrKeys[l]] = attributes[attrKeys[l]];
                        }
                    }

                    attachments.push(attach);

                } else if (extraParams.children[c].name === 'dialog_id') {
                    dialogId = extraParams.getChildText('dialog_id');
                    extension.dialog_id = dialogId;
                }

                if (extraParams.children[c].children.length === 1) {
                    var child = extraParams.children[c];

                    extension[child.name] = child.children[0];
                }
            }
        }

        if (attachments.length > 0) {
            extension.attachments = attachments;
        }

        if (extension.moduleIdentifier) {
            delete extension.moduleIdentifier;
        }

        return {
            extension: extension,
            dialogId: dialogId
        };
    },
    getUniqueId: function(suffix) {
        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : r & 0x3 | 0x8;
            return v.toString(16);
        });
        if (typeof(suffix) == 'string' || typeof(suffix) == 'number') {
            return uuid + ':' + suffix;
        } else {
            return uuid + '';
        }
    },
    getErrorFromXMLNode: function(stanzaError) {
        var errorElement = this.getElement(stanzaError, 'error');
        var errorCode = parseInt(this.getAttr(errorElement, 'code'));
        var errorText = this.getElementText(errorElement, 'text');

        return utils.getError(errorCode, errorText);
    },
    getLocalTime: function() {
        return (new Date()).toTimeString().split(' ')[0];
    }
};

module.exports = qbChatHelpers;
