# Source: xmppvideoroom.js

    //var XMPPVideoRoom = (function() {

    	/**
    	 * Interface with Jitsi Video Room and WebRTC-streamer API
    	 * @constructor
    	 * @param {string} xmppUrl - url of XMPP server
    	 * @param {string} srvurl - url of WebRTC-streamer
    	*/
    	var XMPPVideoRoom = function XMPPVideoRoom (xmppUrl, srvurl, bus) {
    		this.xmppUrl     = xmppUrl;
    		this.srvurl      = srvurl || "//"+window.location.hostname+":"+window.location.port;
    		this.sessionList = {};
    		this.bus = bus;
    	};

    	/**
    	* Ask to publish a stream from WebRTC-streamer in a XMPP Video Room user
    	* @param {string} roomid - id of the XMPP Video Room to join
    	* @param {string} url - WebRTC stream to publish
    	* @param {string} name - name in Video Room
    	*/
    	XMPPVideoRoom.prototype.join = function(roomid, url, name) {
    		var connection = new Strophe.Connection("https://" + this.xmppUrl + "/http-bind");
    		connection.addHandler(this.OnJingle.bind(this, connection, url), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
    		connection.roomid = roomid;
    		connection.name = name;
    		this.emit(connection.roomid + '/' + connection.name, "joining");

    //		connection.rawInput = function (data) { console.log('RECV: ' + data); };
    //		connection.rawOutput = function (data) { console.log('SEND: ' + data); };
    		// disco stuff
    		if (connection.disco) {
    			connection.disco.addIdentity('client', 'http://jitsi.org/jitsimeet');
    			connection.disco.addFeature("urn:xmpp:jingle:1");
    			connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:1");
    			connection.disco.addFeature("urn:xmpp:jingle:transports:ice-udp:1");
    			connection.disco.addFeature("urn:xmpp:jingle:apps:dtls:0");
    			connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:audio");
    			connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:video");
    			connection.disco.addFeature("urn:ietf:rfc:5761")
    			connection.disco.addFeature("urn:ietf:rfc:5888"); // a=group, e.g. bundle

    		}
    		connection.connect(this.xmppUrl, null, this.onConnect.bind(this, connection, roomid, name));
    	}

    	/**
    	* Ask to leave a XMPP Video Room user
    	* @param {string} roomid - id of the XMPP Video Room to leave
    	* @param {string} name - name in Video Room
    	*/
    	XMPPVideoRoom.prototype.leave = function (roomId, username) {
    		Object.entries(this.sessionList).forEach( ([sid, session]) => {
    			if ( (session.connection.roomid === roomId) && (session.connection.name === username) ) {
    				this.leaveSession(sid);
    			}
    		});
    	}

    	/**
    	* Ask to leave all XMPP Video Room
    	*/
    	XMPPVideoRoom.prototype.leaveAll = function () {
    		Object.keys(this.sessionList).forEach( (sid) => {
    			this.leaveSession(sid);
    		});
    		this.sessionList = {};
    	}

    	/*
    	/* HTTP callback for /getIceCandidate
    	*/
    	XMPPVideoRoom.prototype.onReceiveCandidate = function(connection, answer, candidateList) {
    		console.log("============candidateList:" +  JSON.stringify(candidateList));
    		var jingle = answer.querySelector("jingle");
    		var sid = jingle.getAttribute("sid");
    		var from = answer.getAttribute("to");
    		var to = answer.getAttribute("from");

    		candidateList.forEach(function (candidate) {
    			var jsoncandidate = SDPUtil.parse_icecandidate(candidate.candidate);
    			console.log("<=== webrtc candidate:" +  JSON.stringify(jsoncandidate));
    			// get ufrag
    			var ufrag = "";
    			var elems = candidate.candidate.split(' ');
    			for (var i = 8; i < elems.length; i += 2) {
    				switch (elems[i]) {
    					case 'ufrag':
    						ufrag = elems[i + 1];
    						break;
    				}
    			}
    			// get pwd
    			var pwd = "";
    			var transports = $(jingle).find('>content>transport');
    			transports.each( (idx,transport) => {
    				if (ufrag == transport.getAttribute('ufrag')) {
    					pwd = transport.getAttribute('pwd');
    				}
    			});

    			// convert candidate from webrtc to jingle
    			var iq = $iq({ type: "set",  from, to })
    					.c('jingle', {xmlns: 'urn:xmpp:jingle:1'})
    						.attrs({ action: "transport-info",  sid, ufrag, pwd })
    						.c('candidate', jsoncandidate)
    						.up()
    					.up();

    			var id = connection.sendIQ(iq, () => {
    				console.log("===> xmpp transport-info ok sid:" + sid);
    			},() => {
    				console.log("############transport-info error sid:" + sid);
    			});
    		});

    		var id = connection.sendIQ(answer, () => {
    			console.log("===> xmpp session-accept ok sid:" + sid);
    			this.emit(connection.roomid + '/' + connection.name, "published");
    		},() => {
    			console.log("############session-accept error sid:" + sid);
    		});
    	}

    	/*
    	/* HTTP callback for  /call
    	*/
    	XMPPVideoRoom.prototype.onCall = function(connection, iq, data) {
    		var jingle = iq.querySelector("jingle");
    		var sid = jingle.getAttribute("sid");
    		console.log("<=== webrtc answer sid:" + sid);

    		this.sessionList[sid].state = "UP";
    		var earlyCandidates = this.sessionList[sid].earlyCandidates;
    		while (earlyCandidates.length) {
    				var candidate = earlyCandidates.shift();
    				var method = this.srvurl + "/api/addIceCandidate?peerid="+ sid;
    				request("POST" , method, { body: JSON.stringify(candidate) }).done( function (response) {
    						if (response.statusCode === 200) {
    							console.log("method:"+method+ " answer:" +response.body);
    						}
    						else {
    							bind.onError(response.statusCode);
    						}
    					}
    				);
    		}

    		var sdp = new SDP(data.sdp);
    		var iqAnswer = $iq({ type: "set",  from: iq.getAttribute("to"), to: iq.getAttribute("from") })
    		var jingle = iqAnswer.c('jingle', {xmlns: 'urn:xmpp:jingle:1'})
    						.attrs({ action: "session-accept",  sid, responder:iq.getAttribute("to") });

    		var answer = sdp.toJingle(jingle);
    		var bind = this;
    		var method = this.srvurl + "/api/getIceCandidate?peerid="+ sid;
    		request("GET" , method).done( function (response) {
    				if (response.statusCode === 200) {
    					bind.onReceiveCandidate(connection, answer.node, JSON.parse(response.body));
    				}
    				else {
    					bind.onError(response.statusCode);
    				}
    			}
    		);
    	}

    	XMPPVideoRoom.prototype.emit = function(name, state) {
    		if (this.bus) {
    			this.bus.emit('state', name, state);
    		}
    	}

    	XMPPVideoRoom.prototype.onError = function (error) {
    		console.log("############onError:" + error)
    	}

    	/*
    	/* XMPP callback for  jingle
    	*/
    	XMPPVideoRoom.prototype.OnJingle = function(connection, url, iq) {
    		var jingle = iq.querySelector("jingle");
    		var sid = jingle.getAttribute("sid");
    		var action = jingle.getAttribute("action");
    		const fromJid = iq.getAttribute('from');
    		const toJid = iq.getAttribute('to');
    		console.log("OnJingle from:" + fromJid + " to:" + toJid
                                          + " sid:" + sid + " action:" + action);
    		var id = iq.getAttribute("id");
    		var ack = $iq({ type: "result", to: fromJid, id })

    		var bind = this;

    		if (action === "session-initiate") 	{
    			connection.sendIQ(ack);

    			const resource = Strophe.getResourceFromJid(fromJid);
    			const isP2P = (resource !== 'focus');
    			console.log("<=== xmpp offer sid:" + sid + " resource:" + resource + " initiator:" + jingle.getAttribute("initiator"));

    			if (!isP2P) {
    				this.emit(connection.roomid + '/' + connection.name, "publishing");

    				var sdp = new SDP('');
    				sdp.fromJingle($(jingle));

    				this.sessionList[sid] = { connection, state: "INIT", earlyCandidates:[] } ;

    				var method = this.srvurl + "/api/call?peerid="+ sid +"&url="+encodeURIComponent(url)+"&options="+encodeURIComponent("rtptransport=tcp&timeout=60");
    				request("POST" , method, {body:JSON.stringify({type:"offer",sdp:sdp.raw})}).done( function (response) {
    						if (response.statusCode === 200) {
    							bind.onCall(connection, iq, JSON.parse(response.body));
    						}
    						else {
    							bind.onError(response.statusCode);
    						}
    					}
    				);
    			}

    		} else if (action === "transport-info") {
    			connection.sendIQ(ack);

    			console.log("<=== xmpp candidate sid:" + sid);

    			if (this.sessionList[sid]) {
    				var contents = $(jingle).find('>content');
    				contents.each( (contentIdx,content) => {
    					var transports = $(content).find('>transport');
    					transports.each( (idx,transport) => {
    						var ufrag = transport.getAttribute('ufrag');
    						var candidates = $(transport).find('>candidate');
    						candidates.each ( (idx,candidate) => {
    							var sdp = SDPUtil.candidateFromJingle(candidate);
    							sdp = sdp.replace("a=candidate","candidate");
    							sdp = sdp.replace("\r\n"," ufrag " + ufrag);
    							var candidate = { candidate:sdp, sdpMid:"", sdpMLineIndex:contentIdx }
    							console.log("===> webrtc candidate :" + JSON.stringify(candidate));

    							if (this.sessionList[sid].state == "INIT") {
    								this.sessionList[sid].earlyCandidates.push(candidate);
    							} else {
    								var method = this.srvurl + "/api/addIceCandidate?peerid="+ sid;
    								request("POST" , method, { body: JSON.stringify(candidate) }).done( function (response) {
    										if (response.statusCode === 200) {
    											console.log("method:"+method+ " answer:" +response.body);
    										}
    										else {
    											bind.onError(response.statusCode);
    										}
    									}
    								);
    							}
    						});
    					});
    				});
    			}
    		} else if (action === "session-terminate") {
    			connection.sendIQ(ack);
    			console.log("<=== xmpp session-terminate sid:" + sid + " reason:" + jingle.querySelector("reason").textContent);
    			this.leaveSession(sid);
    		}

    		return true;
    	}

    	/*
    	/* XMPP callback for  presence
    	*/
    	XMPPVideoRoom.prototype.OnPresence = function(connection,pres)
    	{
    		const resource = Strophe.getResourceFromJid(pres.getAttribute('from'));
            const xElement = pres.getElementsByTagNameNS('http://jabber.org/protocol/muc#user', 'x')[0];
    		const mucUserItem = xElement && xElement.getElementsByTagName('item')[0];
    		if (mucUserItem) {
    			var msg = " jid:" + mucUserItem.getAttribute('jid')
    						+ " role:" + mucUserItem.getAttribute('role')
    						+ " affiliation:" + mucUserItem.getAttribute('affiliation');

    			const statusEl = pres.getElementsByTagName('status')[0];
    			if (statusEl) {
    				msg += " status:" + statusEl.getAttribute('code');
    			}

    			const nickEl = pres.getElementsByTagName('nick')[0];
    			if (nickEl) {
    				msg += "OnPresence nick:" + nickEl.textContent;
    			}
    			console.log ( "OnPresence " + msg);
    		}
    		if (resource === connection.name) {
    			console.log ( "OnPresence " + connection.name + " connected");
    		}
    		return true;
    	}

    	/*
    	/* XMPP callback for  connect
    	*/
    	XMPPVideoRoom.prototype.onConnect = function(connection, roomid, name, status)
    	{
    	    if (status === Strophe.Status.CONNECTING) {
    			console.log('Strophe is connecting.');
    	    } else if (status === Strophe.Status.CONNFAIL) {
    			console.log('Strophe failed to connect.');
    	    } else if (status === Strophe.Status.DISCONNECTING) {
    			console.log('Strophe is disconnecting.');
    	    } else if (status === Strophe.Status.DISCONNECTED) {
    			console.log('Strophe is disconnected.');
    	    } else if (status === Strophe.Status.CONNECTED) {
    			console.log('Strophe is connected.');

    			var roomUrl = roomid + "@" + "conference." + this.xmppUrl;
    			var extPresence = Strophe.xmlElement('nick', {xmlns:'http://jabber.org/protocol/nick'}, name);
    			connection.muc.join(roomUrl, name, null, this.OnPresence.bind(this,connection), null, null, null, extPresence);

    			this.emit(connection.roomid + '/' + connection.name, "joined");
    		}
    	}

    	/*
    	/* leave a session
    	*/
    	XMPPVideoRoom.prototype.leaveSession = function (sid) {
    		var session = this.sessionList[sid];
    		if (session) {
    			this.emit(session.connection.roomid + '/' + session.connection.name, "leaving");

    			var roomUrl = session.connection.roomid + "@" + "conference." + this.xmppUrl;
    			// close jingle session
    			var iq = $iq({ type: "set",  from: roomUrl +"/" + session.connection.name, to: roomUrl })
    							.c('jingle', {xmlns: 'urn:xmpp:jingle:1'})
    								.attrs({ action: "session-terminate",  sid})
    							.up();
    			session.connection.sendIQ(iq);

    			// close WebRTC session
    			var bind = this;
    			var method = this.srvurl + "/api/hangup?peerid="+ sid;
    			request("GET" , method).done( function (response) {
    					if (response.statusCode === 200) {
    						console.log("method:"+method+ " answer:" +response.body);
    					}
    					else {
    						bind.onError(response.statusCode);
    					}
    				}
    			);
    			session.connection.muc.leave(roomUrl, session.connection.name);
    			session.connection.flush();
    			session.connection.disconnect();
    			this.emit(session.connection.roomid + '/' + session.connection.name, "leaved");

    			delete this.sessionList[sid];
    		}
    	}

    //	return XMPPVideoRoom;
    //})();

    //module.exports = XMPPVideoRoom;