//>>built
define("dojox/store/LightstreamerStore", [
	"dojo/_base/lang",
	"dojo/_base/array",
	"dojo/_base/declare",
	"dojo/_base/Deferred",
	"dojo/store/util/QueryResults"
], function(dojo){
	dojo.getObject("store", true, dojox);
	
	//	NOTE: The usual Lightstreamer web client MUST be loaded to use this store,
	//	and will not be wrapped as an AMD module for now.
	var nextId = 0;

	function translate(id, updateInfo, schema, o){
		//	private function to convert the returned object from an update to a JSON-like object.
		o = o || {};
		dojo.forEach(schema, function(field){
			o[field] = updateInfo.getNewValue(field);
		});
		if(!("id" in o)){ o["id"] = id; }
		return o;
	}

/*=====
dojox.store.LightstreamerStore.__queryOptionsArgs = function(dataAdapter, itemsRange, requestedBufferSize, requestedMaxFrequency, selector, snapshotRequired, commandLogic){
	//	dataAdapter: String?
	//		The data adapter to be used for a query.
	//	itemsRange: Array?
	//		The range of items in the form of [ start, end ] to receive back from Lightstreamer.
	//	requestedBufferSize: Number?
	//		The length of the internal queuing buffers to be used by the server.
	//	requestedMaxFrequency: Number?
	//		The maximum update frequency (updates per second) to be used by Lightstreamer.
	//	selector: String?
	//		The name of a selector, to be recognized by the Metadata Adapter in Lightstreamer.
	//	snapshotRequired: Boolean?
	//		Whether or not to request snapshot delivery.
	//	commandLogic: Array?
	//		An array of arguments in the following form: [ flag, commandPos, keyPos, underSchema, underDataAdapter ]

	this.dataAdapter = dataAdapter;
	this.itemsRange = itemsRange;
	this.requestedBufferSize = requestedBufferSize;
	this.requestedMaxFrequency = requestedMaxFrequency;
	this.selector = selector;
	this.snapshotRequired = snapshotRequired;
	this.commandLogic = commandLogic;
}
=====*/

	dojo.declare("dojox.store.LightstreamerStore", [], {
		_index: {},	//	a cache for data objects returned

		//	pushPage: (Lightstreamer)pushPage
		//		The main connection created by the typical Lightstreamer Web Client
		pushPage: null,

		//	group: String[]
		//		The group of items to be returned from the Lightstreamer Server.
		group: [],

		//	schema: String[]
		//		The list of fields for each item you wish to get back from Lightstreamer
		schema: [],

		constructor: function(pushPage, group, schema, dataAdapter){
			//	summary:
			//		The constructor for the LightstreamerStore.
			//	pushPage: pushPage
			//		This is the pushPage created by using the typical Lightstreamer web client
			//	dataAdapter: String
			//		This is the data adapter to connect to (defined with the Lightstreamer server)
			//	group: String[]
			//		An array of the item names you wish to get back from Lightstreamer.
			//	schema: String[]
			//		The list of fields for each item you wish to get back from Lightstreamer.

			this.pushPage = pushPage;
			this.group = group;
			this.schema = schema;
			this.dataAdapter = dataAdapter || "DEFAULT";
		},

		query: function(query, options){
			//	summary:
			//		Start receiving streams from the Lightstreamer server.
			//
			//	description:
			//		The main method of the LightstreamerStore, query opens up a data stream
			//		from a Lightstreamer server (based on the pushPage definition used in the
			//		constructor) and sets up a way to observe the returned results from said
			//		stream.  It is based on Lightstreamer's NonVisualTable object, and by
			//		default will run the return from the Lightstreamer server through a 
			//		private "translate" function, which takes the updateInfo object normally
			//		returned by Lightstreamer's web client and converts it into a straight
			//		JSON-like object that can be used for data consumption.
			//
			//	query: String
			//		The name of the mode to use for the resulting stream. (RAW, MERGE, COMMAND or DISTINCT)
			//		
			//	options: LightstreamerStore.__QueryOptionsArgs
			//		Additional options to consume. See http://www.lightstreamer.com/docs/client_web_jsdoc/NonVisualTable.html
			//		for more information on these properties. All properties are optional.
			//
			//	returns: dojo.store.util.QueryResults
			//		A query results object that can be used to observe data being returned,
			//		as well as stop the stream of data.  Note that this results object is
			//		customized with an "observe" method and a "close" method; observe is the
			//		main hook into the constant data stream returned by Lightstreamer, and
			//		the close method will stop the query/stream.
			//
			//	example:
			//		Query a server:
			//	|	var results = myLSStore.query("MERGE", { dataAdapter: "QUOTE_ADAPTER", snapshotRequired: true });
			//	|	results.observe(function(obj){
			//	|		//	do something with obj
			//	|	});

			options = options || {};
			var results = new dojo.Deferred(),
				snapshotReceived,
				resultsArray = [],
				self = this,
				id = "store-" + nextId++,
				pushPage = this.pushPage,
				table = new NonVisualTable(this.group, this.schema, query);
        
			if(!("dataAdapter" in options) && this.dataAdapter){
				table.setDataAdapter(this.dataAdapter);
			}

			for(var prop in options) {
				var setter = "set" + prop.charAt(0).toUpperCase() + prop.slice(1);
				if(setter in table && dojo.isFunction(table[setter])){
					table[setter][(dojo.isArray(options[prop])?"apply":"call")](table, options[prop]);
				}
			}
        
			table.onItemUpdate = function(id, updateInfo){
				var obj = translate(id, updateInfo, self.schema, self._index[id]);
				var newObject;
				if(!self._index[id]){
					newObject = true;
					self._index[id] = obj;
					if(!snapshotReceived){
						resultsArray.push(obj);
					}
				}
				table[snapshotReceived?"onPostSnapShot":"onPreSnapShot"](obj, newObject);
			};

			if(query == "MERGE" || options.snapshotRequired === false){
				snapshotReceived = true;
				results.resolve(resultsArray);
			} else { // eventually properly handle other subscription modes
				table.onEndOfSnapshot = function(){
					snapshotReceived = true;
					results.resolve(resultsArray);
				};
			}			

			//	note that these need to be two separate function objects.
			table.onPreSnapShot = function(){};
			table.onPostSnapShot = function(){};

			//	modify the deferred
			results = dojo.store.util.QueryResults(results);

			//	set up the two main ways of working with results
			var foreachHandler;
			results.forEach = function(callback){
				foreachHandler = dojo.connect(table, "onPreSnapShot", callback);
			};

			var observeHandler;
			results.observe = function(listener){
				observeHandler = dojo.connect(table, "onPostSnapShot", function(object, newObject){
					listener.call(results, object, newObject ? -1 : undefined);
				});
			};

			//	set up the way to stop the stream
			results.close = function(){
				if(foreachHandler){ dojo.disconnect(foreachHandler); }
				if(observeHandler){ dojo.disconnect(observeHandler); }
				pushPage.removeTable(id);
				table = null;
			};

			//	start up the stream
			pushPage.addTable(table, id);
			return results;
		},
		get: function(id){
			//	summary:
			//		Return a (cached) object from the Lightstreamer.
			//	id: String
			//		The identity of the object to retrieve.
			return this._index[id];
		}
	});

	return dojox.store.LightstreamerStore;
});
