/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.data.core.RemoteStore");
dojo.require("dojo.data.core.Read");
dojo.require("dojo.data.core.Write");
dojo.require("dojo.data.core.Result");
dojo.require("dojo.experimental");
dojo.require("dojo.Deferred");
dojo.require("dojo.lang.declare");
dojo.require("dojo.json");
dojo.require("dojo.io.*");
dojo.experimental("dojo.data.core.RemoteStore");
dojo.lang.declare("dojo.data.core.RemoteStore", [dojo.data.core.Read, dojo.data.core.Write], {_datatypeMap:{}, _jsonRegistry:dojo.json.jsonRegistry, initializer:function (kwArgs) {
	if (!kwArgs) {
		kwArgs = {};
	}
	this._serverQueryUrl = kwArgs.queryUrl || "";
	this._serverSaveUrl = kwArgs.saveUrl || "";
	this._deleted = {};
	this._changed = {};
	this._added = {};
	this._results = {};
	this._data = {};
	this._numItems = 0;
}, _setupQueryRequest:function (result, requestKw) {
	result.query = result.query || "";
	requestKw.url = this._serverQueryUrl + encodeURIComponent(result.query);
	requestKw.method = "get";
	requestKw.mimetype = "text/json";
}, _resultToQueryMetadata:function (serverResponseData) {
	return serverResponseData;
}, _resultToQueryData:function (serverResponseData) {
	return serverResponseData.data;
}, _remoteToLocalValues:function (attributes) {
	for (var key in attributes) {
		var values = attributes[key];
		for (var i = 0; i < values.length; i++) {
			var value = values[i];
			var type = value.datatype || value.type;
			if (type) {
				var localValue = value.value;
				if (this._datatypeMap[type]) {
					localValue = this._datatypeMap[type](value);
				}
				values[i] = localValue;
			}
		}
	}
	return attributes;
}, _queryToQueryKey:function (query) {
	if (typeof query == "string") {
		return query;
	} else {
		return dojo.json.serialize(query);
	}
}, _assertIsItem:function (item) {
	if (!this.isItem(item)) {
		throw new Error("dojo.data.RemoteStore: a function was passed an item argument that was not an item");
	}
}, get:function (item, attribute, defaultValue) {
	var valueArray = this.getValues(item, attribute);
	if (valueArray.length == 0) {
		return defaultValue;
	}
	return valueArray[0];
}, getValues:function (item, attribute) {
	var itemIdentity = this.getIdentity(item);
	this._assertIsItem(itemIdentity);
	var changes = this._changed[itemIdentity];
	if (changes) {
		var newvalues = changes[attribute];
		if (newvalues !== undefined) {
			return newvalues;
		} else {
			return [];
		}
	}
	return this._data[itemIdentity][0][attribute];
}, getAttributes:function (item) {
	var itemIdentity = this.getIdentity(item);
	if (!itemIdentity) {
		return undefined;
	}
	var atts = [];
	var attrDict = this._data[itemIdentity][0];
	for (var att in attrDict) {
		atts.push(att);
	}
	return atts;
}, hasAttribute:function (item, attribute) {
	var valueArray = this.getValues(item, attribute);
	return valueArray.length ? true : false;
}, containsValue:function (item, attribute, value) {
	var valueArray = this.getValues(item, attribute);
	for (var i = 0; i < valueArray.length; i++) {
		if (valueArray[i] == value) {
			return true;
		}
	}
	return false;
}, isItem:function (something) {
	if (!something) {
		return false;
	}
	var itemIdentity = something;
	if (this._deleted[itemIdentity]) {
		return false;
	}
	if (this._data[itemIdentity]) {
		return true;
	}
	if (this._added[itemIdentity]) {
		return true;
	}
	return false;
}, find:function (keywordArgs) {
	var result = null;
	if (keywordArgs instanceof dojo.data.core.Result) {
		result = keywordArgs;
		result.store = this;
	} else {
		result = new dojo.data.core.Result(keywordArgs, this);
	}
	var query = result.query;
	var self = this;
	var bindfunc = function (type, data, evt) {
		var scope = result.scope || dj_global;
		if (type == "load") {
			result.resultMetadata = self._resultToQueryMetadata(data);
			var dataDict = self._resultToQueryData(data);
			if (result.onbegin) {
				result.onbegin.call(scope, result);
			}
			var count = 0;
			var resultData = [];
			var newItemCount = 0;
			for (var key in dataDict) {
				if (result._aborted) {
					break;
				}
				if (!self._deleted[key]) {
					var values = dataDict[key];
					var attributeDict = self._remoteToLocalValues(values);
					var existingValue = self._data[key];
					var refCount = 1;
					if (existingValue) {
						refCount = ++existingValue[1];
					} else {
						newItemCount++;
					}
					self._data[key] = [attributeDict, refCount];
					resultData.push(key);
					count++;
					if (result.onnext) {
						result.onnext.call(scope, key, result);
					}
				}
			}
			self._results[self._queryToQueryKey(query)] = resultData;
			self._numItems += newItemCount;
			result.length = count;
			if (result.saveResult) {
				result.items = resultData;
			}
			if (!result._aborted && result.oncompleted) {
				result.oncompleted.call(scope, result);
			}
		} else {
			if (type == "error" || type == "timeout") {
				dojo.debug("find error: " + dojo.json.serialize(data));
				if (result.onerror) {
					result.onerror.call(scope, data);
				}
			}
		}
	};
	var bindKw = keywordArgs.bindArgs || {};
	bindKw.sync = result.sync;
	bindKw.handle = bindfunc;
	this._setupQueryRequest(result, bindKw);
	var request = dojo.io.bind(bindKw);
	result._abortFunc = request.abort;
	return result;
}, getIdentity:function (item) {
	if (!this.isItem(item)) {
		return null;
	}
	return (item.id ? item.id : item);
}, newItem:function (attributes, keywordArgs) {
	var itemIdentity = keywordArgs["identity"];
	if (this._deleted[itemIdentity]) {
		delete this._deleted[itemIdentity];
	} else {
		this._added[itemIdentity] = 1;
	}
	if (attributes) {
		for (var attribute in attributes) {
			var valueOrArrayOfValues = attributes[attribute];
			if (dojo.lang.isArray(valueOrArrayOfValues)) {
				this.setValues(itemIdentity, attribute, valueOrArrayOfValues);
			} else {
				this.set(itemIdentity, attribute, valueOrArrayOfValues);
			}
		}
	}
	return {id:itemIdentity};
}, deleteItem:function (item) {
	var identity = this.getIdentity(item);
	if (!identity) {
		return false;
	}
	if (this._added[identity]) {
		delete this._added[identity];
	} else {
		this._deleted[identity] = 1;
	}
	if (this._changed[identity]) {
		delete this._changed[identity];
	}
	return true;
}, setValues:function (item, attribute, values) {
	var identity = this.getIdentity(item);
	if (!identity) {
		return undefined;
	}
	var changes = this._changed[identity];
	if (!changes) {
		changes = {};
		this._changed[identity] = changes;
	}
	changes[attribute] = values;
	return true;
}, set:function (item, attribute, value) {
	return this.setValues(item, attribute, [value]);
}, unsetAttribute:function (item, attribute) {
	return this.setValues(item, attribute, []);
}, _initChanges:function () {
	this._deleted = {};
	this._changed = {};
	this._added = {};
}, _setupSaveRequest:function (saveKeywordArgs, requestKw) {
	requestKw.url = this._serverSaveUrl;
	requestKw.method = "post";
	requestKw.mimetype = "text/plain";
	var deleted = [];
	for (var key in this._deleted) {
		deleted.push(key);
	}
	var saveStruct = {"changed":this._changed, "deleted":deleted};
	var oldRegistry = dojo.json.jsonRegistry;
	dojo.json.jsonRegistry = this._jsonRegistry;
	var jsonString = dojo.json.serialize(saveStruct);
	dojo.json.jsonRegistry = oldRegistry;
	requestKw.postContent = jsonString;
}, save:function (keywordArgs) {
	keywordArgs = keywordArgs || {};
	var result = new dojo.Deferred();
	var self = this;
	var bindfunc = function (type, data, evt) {
		if (type == "load") {
			if (result.fired == 1) {
				return;
			}
			var key = null;
			for (key in self._added) {
				if (!self._data[key]) {
					self._data[key] = [{}, 1];
				}
			}
			for (key in self._changed) {
				var existing = self._data[key];
				var changes = self._changed[key];
				if (existing) {
					existing[0] = changes;
				} else {
					self._data[key] = [changes, 1];
				}
			}
			for (key in self._deleted) {
				if (self._data[key]) {
					delete self._data[key];
				}
			}
			self._initChanges();
			result.callback(true);
		} else {
			if (type == "error" || type == "timeout") {
				result.errback(data);
			}
		}
	};
	var bindKw = {sync:keywordArgs["sync"], handle:bindfunc};
	this._setupSaveRequest(keywordArgs, bindKw);
	var request = dojo.io.bind(bindKw);
	result.canceller = function (deferred) {
		request.abort();
	};
	return result;
}, revert:function () {
	this._initChanges();
	return true;
}, isDirty:function (item) {
	if (item) {
		var identity = item.id || item;
		return this._deleted[identity] || this._changed[identity];
	} else {
		var key = null;
		for (key in this._changed) {
			return true;
		}
		for (key in this._deleted) {
			return true;
		}
		for (key in this._added) {
			return true;
		}
		return false;
	}
}, createReference:function (idstring) {
	return {id:idstring};
}, getSize:function () {
	return this._numItems;
}, forgetResults:function (query) {
	var queryKey = this._queryToQueryKey(query);
	var results = this._results[queryKey];
	if (!results) {
		return false;
	}
	var removed = 0;
	for (var i = 0; i < results.length; i++) {
		var key = results[i];
		var existingValue = this._data[key];
		if (existingValue[1] <= 1) {
			delete this._data[key];
			removed++;
		} else {
			existingValue[1] = --existingValue[1];
		}
	}
	delete this._results[queryKey];
	this._numItems -= removed;
	return true;
}});

