/*
 *  Copyright 2013 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * The carts DAO
 * @private
 */
Ext.define('Ametys.plugins.cart.CartsDAO', {
	singleton: true,
	
	constructor: function(config)
 	{
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method getCartsInformation 
		 * Gets carts information.
		 * This calls the method 'getCartsInformation' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
 		 * @param {String[]} parameters.cartIds The ids of the carts to retrieve.
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object[]} callback.carts The carts' properties. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
         * @param {Object[]} callback.notAllowedCarts The properties of non-authorized carts
         * @param {String[]} callback.unknownCarts The unfound carts
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
			    role: "org.ametys.plugins.cart.CartsDAO",
				methodName: "getCartsInformation",
	     		callback: {
	         		handler: Ext.emptyFn
	     		},
				waitMessage: true,
				errorMessage: {
				    msg: "{{i18n DAOS_CART_REQUEST_ERROR}}",
				    category: Ext.getClassName(this)
				}
			}
		);
		
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method createCart 
		 * Creates a cart.
		 * This calls the method 'createCart' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
 		 * @param {String} parameters.title The title
 		 * @param {String} parameters.description The description
         * @param {String} parameters.documentation The cart documentation link
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object} callback.returnedValue The value return from the server. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
			    role: "org.ametys.plugins.cart.CartsDAO",
				methodName: "createCart",
	     		callback: {
	         		handler: this._createCartCb
	     		},
				waitMessage: true,
				errorMessage: {
				    msg: "{{i18n PLUGINS_CART_CREATION_FAILED}}",
				    category: Ext.getClassName(this)
				}
			}
		);
		
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method updateCart 
		 * Updates a cart.
		 * This calls the method 'updateCart' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
		 * @param {String} parameters.id The id of the cart to edit
 		 * @param {String} parameters.title The title
 		 * @param {String} parameters.description The description
         * @param {String} parameters.documentation The cart documentation link
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object} callback.returnedValue The value return from the server. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
				role: "org.ametys.plugins.cart.CartsDAO",
	     		methodName: "updateCart",
	     		callback: {
	         		handler: this._updateCartCb
	     		},
	     		waitMessage: true,
	     		errorMessage: {
	     			msg: "{{i18n PLUGINS_CART_MODIFICATION_FAILED}}",
	     			category: Ext.getClassName(this)
	     		}
			}
		);
		
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method deleteCart s
		 * Deletes some carts.
		 * This calls the method 'deleteCarts' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
		 * @param {String[]} parameters.ids The ids of carts to delete
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object} callback.returnedValue The value return from the server. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
			    role: "org.ametys.plugins.cart.CartsDAO",
	     		methodName: "deleteCarts",
	     		callback: {
	         		handler: this._deleteCartsCb
	     		},
	     		waitMessage: true,
	     		errorMessage: {
	     			msg: "{{i18n PLUGINS_CART_DELETE_CART_ERROR}}",
	     			category: Ext.getClassName(this)
	     		}
  			}
  		);
  		
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method addElements 
		 * Add elements to a cart.
		 * This calls the method 'addElements' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
		 * @param {String} parameters.cartId The cart id.
		 * @param {String} parameters.type The type of element.
		 * @param {Object} parameters.elementParams The parameters of the element.
		 * @param {Object} [parameters.callback] The callback function to call.
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object} callback.returnedValue The value return from the server. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
			    role: "org.ametys.plugins.cart.CartsDAO",
	     		methodName: "addElements",
	     		callback: {
	         		handler: this._addElementsCb
	     		},
	     		localParamsIndex: 3,
	     		waitMessage: true,
	     		errorMessage: {
	     			msg: true,
	     			category: Ext.getClassName(this)
	     		}
  			}
  		);
  		
		/**
		 * @callable
		 * @member Ametys.plugins.cart.CartsDAO
		 * @method deleteElements 
		 * Deletes elements of a cart.
		 * This calls the method 'deleteElements' of the server DAO 'CartsDAO'.
		 * @param {Object[]} parameters The parameters to transmit to the server method
		 * @param {String} parameters.cartId The cart id.
		 * @param {Object[]} parameters.cartElements The elements to delete.
		 * @param {Function} callback The function to call when the java process is over. Use options.scope for the scope. 
		 * @param {Object} callback.returnedValue The value return from the server. Null on error (please note that when an error occured, the callback may not be called depending on the value of errorMessage).
		 * @param {Object} callback.arguments Other arguments specified in option.arguments                 
		 * @param {Object} [options] Advanced options for the call.
		 * @param {Boolean/String/Object} [options.errorMessage] Display an error message. See Ametys.data.ServerComm#callMethod errorMessage.
		 * @param {Boolean/String/Object} [options.waitMessage] Display a waiting message. See Ametys.data.ServerComm#callMethod waitMessage.
		 * @param {Number} [options.scope] This parameter is the scope used to call the callback. Moreover is the given class is a mixin of Ametys.data.ServerCaller, its methods Ametys.data.ServerCaller#beforeServerCall and Ametys.data.ServerCaller#afterServerCall will be used so see their documentation to look for additional options (such a refreshing on Ametys.ribbon.element.ui.ButtonController#beforeServerCall).
		 * @param {Number} [options.priority] The message priority. See Ametys.data.ServerComm#callMethod for more information on the priority. PRIORITY_SYNCHRONOUS cannot be used here.
		 * @param {String} [options.cancelCode] Cancel similar unachieved read operations. See Ametys.data.ServerComm#callMethod cancelCode.
		 * @param {Object} [options.arguments] Additional arguments set in the callback.arguments parameter.                  
		 * @param {Boolean} [options.ignoreCallbackOnError] If the server throws an exception, should the callback beeing called with a null parameter. See Ametys.data.ServerComm#callMethod ignoreOnError.
		 */
		this.addCallables(
			{
			    role: "org.ametys.plugins.cart.CartsDAO",
	     		methodName: "deleteElements",
	     		callback: {
	         		handler: this._deleteElementsCb
	     		},
	     		errorMessage: {
	     			msg: "{{i18n PLUGINS_CART_REMOVE_CARTELEMENT_ERROR}}",
	     			category: Ext.getClassName(this)
	     		}
  			}
  		);
 	},
 	
 	/**
	 * Callback function called after #createCart is processed
	 * @param {Object} response The response object.
	 * @private
	 */
 	_createCartCb: function(response)
 	{
 		var msg = response['message'];
		if (msg == 'not-allowed')
		{
			Ametys.Msg.show({
				title: "{{i18n PLUGINS_CART_CREATE_UPDATE_NOT_ALLOWED_TITLE}}",
				msg: "{{i18n PLUGINS_CART_CREATE_UPDATE_NOT_ALLOWED_MSG}}",
				buttons: Ext.Msg.OK,
				icon: Ext.Msg.ERROR
			});
		}
		else
		{
			var cartId = response.id;
			Ext.create("Ametys.message.Message", {
				type: Ametys.message.Message.CREATED,
				targets: {
				    id: Ametys.message.MessageTarget.CART,
					parameters: {
						id: cartId
					}
				}
			});
		}
 	},
 	
 	/**
	 * Callback function called after #updateCart is processed
	 * @param {Object} response The response object.
	 * @param {Array} args The callback arguments.
	 * @param {Array} params The parameters.
	 * @private
	 */
 	_updateCartCb: function(response, args, params)
 	{
 		var msg = response['message'];
		if (msg == 'not-allowed')
		{
			Ametys.Msg.show({
				title: "{{i18n PLUGINS_CART_CREATE_UPDATE_NOT_ALLOWED_TITLE}}",
				msg: "{{i18n PLUGINS_CART_CREATE_UPDATE_NOT_ALLOWED_MSG}}",
				buttons: Ext.Msg.OK,
				icon: Ext.Msg.ERROR
			});
		}
		else
		{
			var cartId = response.id;
			
			this.localUpdate(cartId, {
				title: params[1],
				description: params[2],
                documentation: params[3]
			});
				
			Ext.create("Ametys.message.Message", {
				type: Ametys.message.Message.MODIFIED,
				targets: {
				    id: Ametys.message.MessageTarget.CART,
					parameters: {
						id: cartId
					}
				}
			});
		}
 	},
 	
 	/**
	 * Callback function called after #deleteCart is processed
	 * @param {Object} response The response object.
	 * @param {Array} args The callback arguments.
	 * @param {Array} params The parameters.
	 * @private
	 */
 	_deleteCartsCb: function(response, args, params)
 	{
 		Ext.create("Ametys.message.Message", {
			type:  Ametys.message.Message.DELETED,
			targets: {
			    id: Ametys.message.MessageTarget.CART,
				parameters: {ids: response.deletedCarts}
			}
		});
		
		var notallowedCarts = response.notallowedCarts;
		if (notallowedCarts.length > 0)
		{
			Ametys.Msg.show({
				title: "{{i18n PLUGINS_CART_DELETE_CART_TITLE}}",
				msg: "{{i18n PLUGINS_CART_DELETE_CART_NOT_ALLOWED}}" + notallowedCarts.join(','),
				buttons: Ext.Msg.OK,
				icon: Ext.Msg.ERROR
			});
		}
		
		// Remove cart from navigation history
		Ametys.navhistory.HistoryDAO.removeEntries (response.deletedCarts);
 	},
 	
 	/**
	 * Callback function called after #addElements is processed
	 * @param {Object} response The response object.
	 * @param {Array} args The callback arguments.
	 * @param {Array} params The parameters.
	 * @private
	 */
 	_addElementsCb: function(response, args, params)
 	{
 		var msg = response.msg;
		var callback = params[3];
		
		if (msg == 'not-allowed')
		{
			Ametys.Msg.show({
				title: "{{i18n PLUGINS_CART_ADD_TO_CART_NOT_ALLOWED_TITLE}}",
				msg: "{{i18n PLUGINS_CART_ADD_TO_CART_NOT_ALLOWED_MSG}}",
				buttons: Ext.Msg.OK,
				icon: Ext.Msg.ERROR
			});
			if (typeof(callback) == 'function')
			{
				callback(false);
			}
			return;
		}
		
		var id = response.id;
		
		Ext.create("Ametys.message.Message", {
	        type: Ametys.message.Message.MODIFIED,
	        targets: {
	            id: Ametys.message.MessageTarget.CART,
	      	  	parameters: { 
	      	  		id: id
	      	  	}
	        }
	    });
 	},
 	
 	/**
	 * Callback function called after #deleteElements is processed
	 * @param {Object} response The response object.
	 * @param {Array} args The callback arguments.
	 * @param {Array} params The parameters.
	 * @private
	 */
 	_deleteElementsCb: function(response, args, params)
 	{
 		var msg = response['msg'];
		if (msg == 'not-allowed')
		{
			Ametys.Msg.show({
				title: "{{i18n PLUGINS_CART_REMOVE_CARTELEMENT_TITLE}}",
				msg: "{{i18n PLUGINS_CART_REMOVE_CARTELEMENT_NOT_ALLOWED}}",
				buttons: Ext.Msg.OK,
				icon: Ext.Msg.ERROR
			});
		}
		else
		{
			var cartId = params[0];
			var elmts = params[1];
			
			var targets = [];
			for (var i=0; i < elmts.length; i++)
			{
				targets.push({
				    id: 'cartElement',
					parameters: {
						id: elmts[i].id,
						cartElementType: elmts[i].type,
						cartId: cartId
					}
				});
			}
			
			Ext.create("Ametys.message.Message", {
				type: Ametys.message.Message.DELETED,
				targets: targets
			});
		}
 	},

    /**
     * Get a cart by id asynchronously
     * @param {String} id The id of the cart to get. If id is empty, the callback will be called with null
     * @param {Function} callback The method that will be called 
     * @param {Ametys.plugins.cart.Cart} callback.cart The retrieved cart. Can be empty.
     */
    getCart: function (id, callback)
    {
        if (Ext.isEmpty(id))
        {
            callback(null);
            return;
        }
        this._getCarts ([id], Ext.bind(this._getCartCb, this, [callback], 1));
    },
    
    /**
     * @private
     * Called after #getCart to finally call the callback argument
     * @param {Ametys.plugins.cart.Cart[]} carts The retrieved carts. Can be empty.
     * @param {Function} callback The function to call back
     * @param {Ametys.plugins.cart.Cart} callback.cart The asked cart. Can be empty.
     */
    _getCartCb: function (carts, callback)
    {
        callback((carts.length == 0) ? null : carts[0]);
    },
    
    /**
     * Get several carts by ids asynchronously
     * @param {String[]} ids The ids of the cart to gets. If ids are empty, the callback will be called with an empty array
     * @param {Function} callback The method that will be called 
     * @param {Ametys.plugins.cart.Cart[]} callback.carts The retrieved carts. Can be empty.
     */
    getCarts: function (ids, callback)
    {
        if (Ext.isEmpty(ids))
        {
            callback([]);
            return;
        }
        this._getCarts (ids, Ext.bind(this._getCartsCb, this, [callback], 1));
    },
    
    /**
     * @private
     * Called after #getCarts to finally call the callback argument
     * @param {Ametys.plugins.cart.Cart[]} carts The retrieved carts. Can be empty.
     * @param {Function} callback The function to call back
     * @param {Ametys.plugins.cart.Cart[]} callback.carts The asked carts. Can be empty.
     */
    _getCartsCb: function (carts, callback)
    {
        callback(carts);
    },
        
	/**
	 * Retrieve carts from the cache, or by doing a server request.
	 * @param {String[]} cartIds Cart identifiers
	 * @param {Function} callback The callback function called after retrieving the carts. Has the following parameters: 
	 * @param {Ametys.plugins.cart.Cart[]} callback.carts An array of retrieved carts. Can be empty.
	 * @param {String[]} callback.unknownCarts Array of unknown cart ids among the requested list.
	 * @param {Ametys.plugins.cart.Cart[]} callback.notAllowedCarts An array of not-allowed carts. Can be empty.
	 * @param {Object} [scope=window] The callback scope, default to window. Can be null.
	 * @param {Boolean} [displayErrors=false] Set to true to display error dialog box if unknown carts are reported.
	 * @private
	 */
	_getCarts: function(cartIds, callback, scope, displayErrors)
	{
		this._initCache();
		
		cartIds = Ext.Array.from(cartIds);
		
		var resolved = {}, unresolved = [], cart;
		
		Ext.Array.each(cartIds, function(id) {
			cart = this._getFromCache(id);
			if (cart)
			{
				resolved[id] = cart;
			}
			else if (!Ext.Array.contains(this._resolving, id))
			{
				this._resolving.push(id);
				unresolved.push(id);
			}
		}, this);
		
		if (unresolved.length > 0)
		{
			this.getCartsInformation([cartIds], this._sendGetCartRequestCb, {scope: this, arguments: [callback, scope, displayErrors || false]});
			
		}
		else
		{
			callback.call(scope || window, Ext.Object.getValues(resolved), []);
		}
	},
	
	/**
	 * Callback function called after #getCartsInformation is processed
	 * @param {Object} response The response object.
	 * @param {Array} args The callback arguments.
	 * @param {Array} params The parameters.
	 * @private
	 */
	_sendGetCartRequestCb: function (response, args, params)
	{
		var callback = args[0] || Ext.emptyFn;
		var scope = args[1] || window;
		var displayErrors = args[3] || false;
		
		var carts = [];
		var notAllowedCarts = [];
		var unknownCarts = response.unknownCarts || [];
		
		// Populate cache.
		Ext.Array.forEach(response.carts, function(data) {
			var cart = Ext.create ('Ametys.plugins.cart.Cart', data);
			carts.push(cart);
			this._addToCache(data.id, cart);
		}, this);
		
		Ext.Array.forEach(response.notAllowedCarts, function(data) {
			var cart = Ext.create ('Ametys.plugins.cart.Cart', data);
			notAllowedCarts.push(cart);
		}, this);
		
		if (displayErrors && unknownCarts.length > 0)
		{
			Ametys.log.ErrorDialog.display({
				title: "{{i18n DAOS_CART_NOT_FOUND_ERROR_TITLE}}",
				text: "{{i18n DAOS_CART_NOT_FOUND_ERROR_MSG}}", 
				details: unknownCarts.join(", "),
				category: this.self.getName()
			});
		}
        
        if (typeof callback == 'function')
        {
            callback (carts, unknownCarts, notAllowedCarts);
        }
	},
	
	/**
	 * @property {Object} _cache The DAO cache. Keys are cart id, Values are an object representing the cart data.
	 * @private
	 */
	_cache: null,
	/**
	 * @property {Object} _cacheExpirations The object holding the expiration times for each entry in the cache
	 * @private
	 */
	_cacheExpirations : null,
	/**
	 * @property {String[]} _resolving Array of the requested cart that are currently being resolved on the server.
	 * Used to avoid making several requests for the same cart in a short period of time.
	 * @private
	 */
	_resolving: null,

	/**
	 * Update a cart in the cache. Nothing will be done if the entry is not present in the cache.
	 * This must be done when some data of a cart are modified.
	 * @param {String} cartId the cart id
	 * @param {Object} updatedData The properties to update for this cart.
	 */
	localUpdate: function(cartId, updatedData)
	{
		this._initCache();
		
		var cart = this._getFromCache(cartId);
		if (cart)
		{
			cart.setProperties(updatedData);
			this._addToCache(cartId, cart);
		}
	},
	
	/**
	 * Remove a cart in the cache.
	 * This must be used to invalidate an entry in the cache.
	 * Typically it must be done when a cart have just been removed on the server.
	 * @param {String} cartId the cart id
	 */
	localRemove: function(cartId)
	{
		this._initCache();
			
		delete this._cache[cartId];
		delete this._cacheExpirations[cartId];
		delete this._resolving[cartId];
		
	},
	
    /**
     * Cache initialization
     * @private
     */
    _initCache: function()
    {
        if (this._cache == null)
        {
            this._cache = {};
            this._cacheExpirations = {};
            this._resolving = [];
        }
    },
    
    /**
     * Cache invalidation
     */
    invalidateCache: function()
    {
        this._cache = null;
        this._initCache();
    },
    
	/**
	 * Internal method to put an entry in the cache.
	 * @private
	 */
	_addToCache: function(cartId, cartData)
	{
		this._cache[cartId] = cartData;
		this._cacheExpirations[cartId] = Ext.Date.add(new Date(), Ext.Date.MINUTE, 15);
		
		Ext.Array.remove(this._resolving, cartId);
	},
	
	/**
	 * Internal method to retrieve an entry in the cache.
	 * @private
	 */
	_getFromCache: function(cartId)
	{
		if (this._cacheExpirations[cartId] && new Date() <= this._cacheExpirations[cartId])
		{
			return this._cache[cartId];
		}
		else
		{
			this.localRemove(cartId);
			return null;
		}
	}
});