﻿window.location.isSameDomain = function(uri) {
	return !/^http/.test(uri) 
		|| new RegExp('^' + this.protocol + '//' + this.hostname + '/').test(uri);
};

var notificationDispatcher = $.init(function() {
	var 
	freeze,
	controllers = {},
	endpoint = '/{controllers}/{attendee}/',
	dataType,
	interval,
	attendee,
	predicate,
	timeoutID,
	timeoutInit = function(int) {
		timeoutID = window.setTimeout(launchNotificationChecking, -(-int) || interval);
	},
	launchNotificationChecking = function() {
		if (timeoutID)
			window.clearTimeout(timeoutID);

		if (predicate && !predicate() && (timeoutInit() || true))
			return;

		freeze ? timeoutInit(2000) : checkNotifications(timeoutInit);
	},
	checkNotifications = function() {
		var c = '', timestamps = {}, callback, names, force;

		for (var i = 0; i < arguments.length; i++)
			$.isFunction(arguments[i])
				? callback = arguments[i]
					: $.isArray(arguments[i])
						? names = arguments[i]
							: force = arguments[i];

		$.each(names || controllers, function(name) {
			name = this.attempts ? name : this;

			var receiver = controllers[name];

			if ((receiver.attendeeDepended && !attendee) ||
				(!force && (receiver.attempt = (receiver.attempt % receiver.attempts) + 1) != 1))
				return;

			c += '+' + name;

			if (receiver.timestamp)
				timestamps[name] = receiver.timestamp.toEtag();
		});

		if (!c)
			return callback && callback();

		var url = endpoint
			.replace('{controllers}', c.replace(/^\+/, ''))
			.replace('{attendee}', (attendee || 'everyone'))
			.replace(/([^:])\/\//, '$1/')
		;

		$.ajax({
			url: url,
			data: timestamps,
			dataType: dataType,
			success: function(data) {
				callback && callback();

				$(data).each(function() {
					controllers[this.type].timestamp = this.timestamp;
					controllers[this.type].process(this.updates, attendee);
				});
			},
			error: function(xhr, st, e) {
				$.browser.safari && console && console.error && console.error('NotificationDispatcher error');

				if (xhr.status == 403)
					window.location = '/login/?returnUrl=' + escape(window.location);
			}
		});
	},
	changeAttendee = this.changeAttendee = function(_attendee) {
		attendee = _attendee;

		$.each(controllers, function() {
			$.isFunction(this.changeAttendee) && this.changeAttendee(attendee);
		});
	};

	this.checkNotifications = function(callback, predicate) {
		if (!dataType)
			return $.browser.safari && console && console.error && console.error('Notification dispatcher is not initialized.');

		checkNotifications.apply(this, Array.prototype.slice.call(arguments, 0, 2).concat(true));
	};

	this.freeze = function() {
		freeze = true;
	};

	this.unfreeze = function() {
		freeze = false;
	};

	this.init = function() {
		var e, a, c;

		for (var i = 0; i < arguments.length; i++)
			switch (typeof arguments[i]) {
			case 'string':
				e = arguments[i]; /*endpoint*/
				break;
			case 'number':
				a = arguments[i]; /*attendee*/
				break;
			case 'object':
				c = arguments[i]; /*controllers*/
				break;
			case 'function':
				predicate = arguments[i];
				break;
		}

		endpoint = e + endpoint;
		dataType =
			/^http/.test(endpoint) &&
			!new RegExp('^((' + window.location.protocol + '//' + window.location.hostname + '/)|(/))').test(endpoint)
				? 'jsonp' : 'json';

		var r, k, ints = {};

		$.each(c, function(name) {
			this && $.isFunction(this.process)
				? ints[name] = (controllers[name] = this).interval : null;
		});

		$.each(ints, function() {
			if (interval || (interval = this) != this)
				for (k = this; r = k % interval; k = interval, interval = r);
		});

		$.each(controllers, function(name) {
			this.attempt = (this.attempts = (ints[name] || interval) / interval) && 0;
		});

		changeAttendee(a);
		launchNotificationChecking();
	};
});

var serviceChannel = $.init(function() {
	var 
	baseUri,
	dataType,
	me = this, 
	//methodType, 
	call = function() {
		if (!dataType)
			$.browser.safari && console && console.error && console.error('Service channel is not initialized.');

		var endpoint, data, callback, errorCallback;

		for (var i = 0; i < arguments.length; i++)
			!endpoint
				? endpoint = arguments[i]
					: $.isFunction(arguments[i])
						? (!callback && (callback = arguments[i])) || (errorCallback = arguments[i])
							: data = arguments[i];

		$.ajax({
			url: baseUri + endpoint,
			type: data ? 'POST' : 'GET',
			data: data,
			cache: !data ? false : undefined,
			dataType: dataType,
			success: callback,
			error: function(xhr, st, e) {
				$.browser.safari && console && console.error && console.error('Service channel error.');

				if (xhr.status == 403)
					window.location = '/login/?returnUrl=' + escape(window.location);
				
				errorCallback && errorCallback(st, e);
			}
		});
	};

	this.init = function() {
		baseUri = ((arguments[0] || '') + '/').replace(/\/\/$/, '/');

		dataType = window.location.isSameDomain(baseUri) ? 'json' : 'jsonp';
		//methodType = window.location.isSameDomain(baseUri) ? 'POST' : undefined;
	};
	
	var callCtor = function(endpoint) {
		return function(attendee) {
			var args = Array.prototype.slice.call(arguments);
			args[0] = endpoint + attendee;
			
			call.apply(me, args);
		};
	};

	this.sendMessage = callCtor('send-message/');
	this.endConversation = callCtor('end-conversation/');
	this.sendBusy = callCtor('send-busy/');
	this.blockContact = callCtor('send-block/');
	this.addContact = callCtor('add-contact/');
	this.removeContact = callCtor('remove-contact/');
});


