/*
 * LICENCE[[
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/CeCILL 2.O 
 *
 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the 
 * License. 
 * 
 * The Original Code is utc.fr code. 
 * 
 * The Initial Developer of the Original Code is 
 * david.rivron@utc.fr
 * 
 * Portions created by the Initial Developer are Copyright (C) 2012
 * the Initial Developer. All Rights Reserved. 
 * 
 * Contributor(s): 
 * 
 * 
 * Alternatively, the contents of this file may be used under the terms of 
 * either of the GNU General Public License Version 2 or later (the "GPL"), 
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 
 * or the CeCILL Licence Version 2.0 (http://www.cecill.info/licences.en.html), 
 * in which case the provisions of the GPL, the LGPL or the CeCILL are applicable 
 * instead of those above. If you wish to allow use of your version of this file 
 * only under the terms of either the GPL or the LGPL, and not to allow others 
 * to use your version of this file under the terms of the MPL, indicate your 
 * decision by deleting the provisions above and replace them with the notice 
 * and other provisions required by the GPL or the LGPL. If you do not delete 
 * the provisions above, a recipient may use your version of this file under 
 * the terms of any one of the MPL, the GPL, the LGPL or the CeCILL.
 * ]]LICENCE
 */
 
 var MessagesHandler = require('./MessagesHandler.js'),
	util = require('util');
/**
 * Handler d'un message de type 'uri'.
 * 
 * Propriétés du message client : {
 * 	type: uri,
 *		account: <utilisateur à l'origine de l'action> 
 * 	uri: <URI concernée par l'action>
 * 	action: open | focus | unfocus| dirty | undirty | close,
 * }
 * 
 * Propriétés du message serveur : {
 * 	type: uri,
 * 	account:  <utilisateur à l'origine du changement d'état> 
 * 	uri: <URI concernée par le changement d'état>
 * 	states: <états de l'URI pour l'utilisateur>
 * }
 */
	
var UriMessagesHandler = module.exports = function (pCollabServer) {
	MessagesHandler.call(this, 'uri', pCollabServer);
}

util.inherits(UriMessagesHandler, MessagesHandler);

UriMessagesHandler.prototype.onInit = function (pClient, pMessage) {
	pClient.urisStates = {};
	for (var vUri in pMessage.urisStates) {
		(function(pUri) {
			var vWspCd = vUri.substr(0, vUri.indexOf('/'));
			this.server.requirePerm(pClient, vWspCd, MessagesHandler.OBSERVE_PERM, function () {
				var vStates = pClient.urisStates[pUri] = pMessage.urisStates[pUri];
				var vEvents = [];
				if (vStates.reading) vEvents.push('focus');
				if (vStates.writing) vEvents.push('dirty');
				for (var i=0; i<vEvents.length; i++) {
					this.xDispatchUriMessage(pUri, pClient, vEvents[i]);
				}
			}.bind(this));
		}.bind(this)(vUri));
	}
};
	
UriMessagesHandler.prototype.onMessage = function (pClient, pMessage) {
	if (!pMessage.uri) return;
	var vWspCd = pMessage.uri.substr(0, pMessage.uri.indexOf('/'));
	this.server.requirePerm(pClient, vWspCd, MessagesHandler.OBSERVE_PERM, function () {
		this.xHandleMessage(pClient, pMessage);
	}.bind(this));
}

UriMessagesHandler.prototype.xHandleMessage = function (pClient, pMessage) {
	var vSelf = this,
		vUri = pMessage.uri,
		vClients = this.server.clients,
		vEvent = pMessage.action,
		vStates = pClient.urisStates[vUri];
	
	if (!vStates) vStates = pClient.urisStates[vUri] = { opened: false, reading: false, writing: false };
	var vEvents = [], vOldMergedStates = this.xMergeUriStates(vUri, pClient.account);
	
	switch (pMessage.action) {
 	case 'open':
 		vStates.opened = true;
 		vEvents.push('open');
 		break;
 	case 'focus':
		vStates.reading = true;
		vEvents.push('focus');
		break;
	case 'unfocus':
		vStates.reading = false;
		vEvents.push('unfocus');
		break;
	case 'dirty':
		var vIsLocked = Object.keys(vClients).some(function(pCliId) {
			var vUriStates = vClients[pCliId].urisStates[vUri];
			return vUriStates && vUriStates.writing;
		});
		if (vIsLocked) {
			console.error('Write conflict on ', vUri);
			return;
		}
		vStates.writing = true;
		vEvents.push('dirty');
		break;
	case 'undirty':
		vStates.writing = false;
		vEvents.push('undirty');
		break;
	case 'close':
		if (vStates.writing) vEvents.push('undirty');
		if (vStates.reading) vEvents.push('unfocus');
		if (vStates.opened) vEvents.push('close');
		for (var vState in vStates) vStates[vState] = false;
	}
	
	// Envoi  des changement d'état du client aux autres clients du même compte
	for (var i=0; i<vEvents.length; i++) {
		this.xDispatchUriMessage(vUri, pClient, vEvents[i], true);
	}
	
	// Envoi des changement d'état d'un compte aux autres comptes
	var vAccountEvents = [], vNewMergedStates = this.xMergeUriStates(vUri, pClient.account);
	if (vNewMergedStates.opened != vOldMergedStates.opened) vAccountEvents.push(vNewMergedStates.opened ? 'open' : 'close');
	if (vNewMergedStates.reading != vOldMergedStates.reading) vAccountEvents.push(vNewMergedStates.reading ? 'focus' : 'unfocus');
	if (vNewMergedStates.writing != vOldMergedStates.writing) vAccountEvents.push(vNewMergedStates.writing ? 'dirty' : 'undirty');
	for (var i=0; i<vAccountEvents.length; i++) {
		this.xDispatchUriMessage(vUri, pClient, vAccountEvents[i]);
	}
	
	var vHasState = Object.keys(vStates).some(function(pState) { return vStates[pState]; });
	if (!vHasState) delete pClient.urisStates[vUri];
}

UriMessagesHandler.prototype.xMergeUriStates = function (pUri, pAccount) {
	var vMergedStates = {};
	
	for (var vClientId in this.server.clients) {
		var vClient = this.server.clients[vClientId];
		if (pUri in vClient.urisStates && (!pAccount || vClient.account == pAccount)) {
			var vStates = vClient.urisStates[pUri];
			for (var vState in vStates) {
				vMergedStates[vState] = vMergedStates[vState] || vStates[vState];
			}
		}
	}
	return vMergedStates;
}

UriMessagesHandler.prototype.onClose = function (pClient) { 
	for (var vUri in pClient.urisStates) {
		this.xHandleMessage(pClient, { type: 'uri', uri: vUri, action: 'close', account: pClient.account, clientId: pClient.clientId });
	}
}

UriMessagesHandler.prototype.onNoPerm = function (pClient, pWspCd) { 
	for (var vUri in pClient.urisStates) {
		if (vUri.indexOf(pWspCd) == 0) {
			this.xHandleMessage(pClient, { type: 'uri', uri: vUri, action: 'close', account: pClient.account, clientId: pClient.clientId }); 
		}
	}
}

UriMessagesHandler.prototype.xDispatchUriMessage = function  (pUri, pClient, pEvent, pSameAccount) {
	// Envoie du changement d'état aux clients concernés par le workspace de l'URI
	var vWspCd = pUri.substr(0, pUri.indexOf('/'));
	var vMessage = { type: 'uri', uri: pUri, event: pEvent, account: pClient.account, clientId: pClient.clientId };
	for (var vClientId in this.server.clients) {
		var vClient = this.server.clients[vClientId];
		if (vClient != pClient && (!pSameAccount || pClient.account == vClient.account)) this.server.sendMessage(vClient, vWspCd, vMessage);
	}
}
