/* global $, APP, config, Strophe*/
var logger = require("jitsi-meet-logger").getLogger(__filename);
var EventEmitter = require("events");
var Pako = require("pako");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
var RTCEvents = require("../../service/RTC/RTCEvents");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var JitsiConnectionErrors = require("../../JitsiConnectionErrors");
var JitsiConnectionEvents = require("../../JitsiConnectionEvents");
var RTC = require("../RTC/RTC");
var authenticatedUser = false;
function createConnection(bosh) {
    bosh = bosh || '/http-bind';
    return new Strophe.Connection(bosh);
};
//!!!!!!!!!! FIXME: ...
function initStrophePlugins(XMPP)
{
    require("./strophe.emuc")(XMPP);
    require("./strophe.jingle")(XMPP, XMPP.eventEmitter);
//    require("./strophe.moderate")(XMPP, eventEmitter);
    require("./strophe.util")();
    require("./strophe.ping")(XMPP, XMPP.eventEmitter);
    require("./strophe.rayo")();
    require("./strophe.logger")();
}
//!!!!!!!!!! FIXME: ...
///**
// * If given localStream is video one this method will advertise it's
// * video type in MUC presence.
// * @param localStream new or modified LocalStream.
// */
//function broadcastLocalVideoType(localStream) {
//    if (localStream.videoType)
//        XMPP.addToPresence('videoType', localStream.videoType);
//}
//
//function registerListeners() {
//    RTC.addStreamListener(
//        broadcastLocalVideoType,
//        StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED
//    );
//    RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
//        XMPP.addToPresence("devices", devices);
//    });
//}
function XMPP(options) {
    this.eventEmitter = new EventEmitter();
    this.connection = null;
    this.disconnectInProgress = false;
    this.forceMuted = false;
    this.options = options;
    initStrophePlugins(this);
//    registerListeners();
    this.connection = createConnection(options.bosh);
}
XMPP.prototype.getConnection = function(){ return connection; };
XMPP.prototype._connect = function (jid, password) {
    var self = this;
    // connection.connect() starts the connection process.
    //
    // As the connection process proceeds, the user supplied callback will
    // be triggered multiple times with status updates. The callback should
    // take two arguments - the status code and the error condition.
    //
    // The status code will be one of the values in the Strophe.Status
    // constants. The error condition will be one of the conditions defined
    // in RFC 3920 or the condition ‘strophe-parsererror’.
    //
    // The Parameters wait, hold and route are optional and only relevant
    // for BOSH connections. Please see XEP 124 for a more detailed
    // explanation of the optional parameters.
    //
    // Connection status constants for use by the connection handler
    // callback.
    //
    //  Status.ERROR - An error has occurred (websockets specific)
    //  Status.CONNECTING - The connection is currently being made
    //  Status.CONNFAIL - The connection attempt failed
    //  Status.AUTHENTICATING - The connection is authenticating
    //  Status.AUTHFAIL - The authentication attempt failed
    //  Status.CONNECTED - The connection has succeeded
    //  Status.DISCONNECTED - The connection has been terminated
    //  Status.DISCONNECTING - The connection is currently being terminated
    //  Status.ATTACHED - The connection has been attached
    var anonymousConnectionFailed = false;
    var connectionFailed = false;
    var lastErrorMsg;
    this.connection.connect(jid, password, function (status, msg) {
        logger.log('Strophe status changed to',
            Strophe.getStatusString(status), msg);
        if (status === Strophe.Status.CONNECTED) {
            if (self.options.useStunTurn) {
                self.connection.jingle.getStunAndTurnCredentials();
            }
            logger.info("My Jabber ID: " + self.connection.jid);
            // Schedule ping ?
            var pingJid = connection.domain;
            connection.ping.hasPingSupport(
                pingJid,
                function (hasPing) {
                    if (hasPing)
                        connection.ping.startInterval(pingJid);
                    else
                        console.warn("Ping NOT supported by " + pingJid);
                }
            );
            
            if (password)
                authenticatedUser = true;
            if (self.connection && self.connection.connected &&
                Strophe.getResourceFromJid(self.connection.jid)) {
                // .connected is true while connecting?
//                self.connection.send($pres());
                self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_ESTABLISHED,
                    Strophe.getResourceFromJid(self.connection.jid));
            }
        } else if (status === Strophe.Status.CONNFAIL) {
            if (msg === 'x-strophe-bad-non-anon-jid') {
                anonymousConnectionFailed = true;
            }
            else
            {
                connectionFailed = true;
            }
            lastErrorMsg = msg;
        } else if (status === Strophe.Status.DISCONNECTED) {
            // Stop ping interval
            self.connection.ping.stopInterval();
            self.disconnectInProgress = false;
            if (anonymousConnectionFailed) {
                // prompt user for username and password
                self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
                    JitsiConnectionErrors.PASSWORD_REQUIRED);
            } else if(connectionFailed) {
                self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
                    JitsiConnectionErrors.OTHER_ERROR,
                    msg ? msg : lastErrorMsg);
            } else {
                self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_DISCONNECTED,
                    msg ? msg : lastErrorMsg);
            }
        } else if (status === Strophe.Status.AUTHFAIL) {
            // wrong password or username, prompt user
            self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
                JitsiConnectionErrors.PASSWORD_REQUIRED);
        }
    });
}
XMPP.prototype.connect = function (jid, password) {
    if(!jid) {
        var configDomain = this.options.hosts.anonymousdomain || this.options.hosts.domain;
        // Force authenticated domain if room is appended with '?login=true'
        if (this.options.hosts.anonymousdomain &&
            window.location.search.indexOf("login=true") !== -1) {
            configDomain = this.options.hosts.domain;
        }
        jid = configDomain || window.location.hostname;
    }
    return this._connect(jid, password);
};
XMPP.prototype.createRoom = function (roomName, options, useNicks, nick) {
    var roomjid = roomName  + '@' + this.options.hosts.muc;
    if (useNicks) {
        if (nick) {
            roomjid += '/' + nick;
        } else {
            roomjid += '/' + Strophe.getNodeFromJid(this.connection.jid);
        }
    } else {
        var tmpJid = Strophe.getNodeFromJid(this.connection.jid);
        if(!authenticatedUser)
            tmpJid = tmpJid.substr(0, 8);
        roomjid += '/' + tmpJid;
    }
    return this.connection.emuc.createRoom(roomjid, null, options);
}
XMPP.prototype.addListener = function(type, listener) {
    this.eventEmitter.on(type, listener);
};
XMPP.prototype.removeListener = function (type, listener) {
    this.eventEmitter.removeListener(type, listener);
};
//FIXME: this should work with the room
XMPP.prototype.leaveRoom = function (jid) {
    var handler = this.connection.jingle.jid2session[jid];
    if (handler && handler.peerconnection) {
        // FIXME: probably removing streams is not required and close() should
        // be enough
        if (RTC.localAudio) {
            handler.peerconnection.removeStream(
                RTC.localAudio.getOriginalStream(), true);
        }
        if (RTC.localVideo) {
            handler.peerconnection.removeStream(
                RTC.localVideo.getOriginalStream(), true);
        }
        handler.peerconnection.close();
    }
    this.eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE);
    this.connection.emuc.doLeave(jid);
};
/**
 * Sends 'data' as a log message to the focus. Returns true iff a message
 * was sent.
 * @param data
 * @returns {boolean} true iff a message was sent.
 */
XMPP.prototype.sendLogs = function (data) {
    if(!this.connection.emuc.focusMucJid)
        return false;
    var deflate = true;
    var content = JSON.stringify(data);
    if (deflate) {
        content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
    }
    content = Base64.encode(content);
    // XEP-0337-ish
    var message = $msg({to: this.connection.emuc.focusMucJid, type: 'normal'});
    message.c('log', { xmlns: 'urn:xmpp:eventlog',
        id: 'PeerConnectionStats'});
    message.c('message').t(content).up();
    if (deflate) {
        message.c('tag', {name: "deflated", value: "true"}).up();
    }
    message.up();
    this.connection.send(message);
    return true;
};
// Gets the logs from strophe.jingle.
XMPP.prototype.getJingleLog = function () {
    return this.connection.jingle ? this.connection.jingle.getLog() : {};
};
// Gets the logs from strophe.
XMPP.prototype.getXmppLog = function () {
    return this.connection.logger ? this.connection.logger.log : null;
};
XMPP.prototype.dial = function (to, from, roomName,roomPass) {
    this.connection.rayo.dial(to, from, roomName,roomPass);
};
XMPP.prototype.setMute = function (jid, mute) {
    this.connection.moderate.setMute(jid, mute);
};
XMPP.prototype.eject = function (jid) {
    this.connection.moderate.eject(jid);
};
XMPP.prototype.getSessions = function () {
    return this.connection.jingle.sessions;
};
XMPP.prototype.disconnect = function () {
    if (this.disconnectInProgress || !this.connection || !this.connection.connected)
    {
        this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);
        return;
    }
    this.disconnectInProgress = true;
    this.connection.disconnect();
};
module.exports = XMPP;