enyo.kind({
	name:"Parse.RestClient",
	kind:"Component",
	statics:{
		key:"Parse.RestClient.User",
		user:null,
		setUser:function(user, applicationId) {
			var prior = this.user;

			this.user = user;
			if(localStorage) {
				if(this.user) {
					localStorage.setItem(this.key+"/"+applicationId, enyo.json.stringify(user));	
				} else {
					localStorage.removeItem(this.key+"/"+applicationId);
				}
			}

			if(!(prior && this.user && prior.objectId === this.user.objectId)) {
				enyo.Signals.send("onChangeUser", {user:this.user});
			}
		},
		currentUser:function(applicationId) {
			if(!this.user && localStorage) {
				var s = localStorage.getItem(this.key+"/"+applicationId);
				if(s) {
					this.user = enyo.json.parse(s);
					enyo.Signals.send("onChangeUser", {user: this.user});
				}
			}

			return this.user;
		}
	},
	published:{
		applicationId:"",
		key:"",
		keyType: "js"   // or rest
	},
	events:{
		onAdd:"",
		onUpdate:"",
		onGet:"",
		onRemove:"",
		onSearch:"",
		onError:"",
		onCreateUser:"",
		onRemoveUser:"",
		onLogin:"",
		onBatch:""
	},
	handlers:{
		onLogin:"loginHandler"
	},
	parse:{
		host:"api.parse.com",
		version:"1"
	},
	reservedFields: {__type:1, createdAt:1, updatedAt:1, className:1, sessionToken:1},
	getUrl:function(endpoint, className, id) {
		var p = ["",this.parse.version,endpoint];

		if(className) {
			p.push(className);
		}

		if(id) {
			p.push(id);
		}
		
		return p.join("/");
	},
	getAjax:function(config) {
		var params;
		var url = "https://"+this.parse.host+this.getUrl(config.endpoint, config.className, config.id);

		if(this.keyType == "js") {

			var user = Parse.RestClient.currentUser(this.applicationId);

			// map Header attributes into body members
			config.data = enyo.mixin(config.data || {}, {
				_ApplicationId: this.applicationId,
				_ClientVersion: "js1.1.13",
				//_InstallationId: "2dafa3a8-393d-9339-3f96-f2e836b8d1e9"
				_JavaScriptKey: this.key,
				_SessionToken: user ? user.sessionToken : ""
			});

			// all requests are channeled via POST regardless of method
			if(config.method !== "POST") {
				config.data._method = config.method;
				config.method = "POST";
			}

			// stringify the data since we're passing as text/plain
			config.data = enyo.json.stringify(config.data);

			params = {
				method:"POST",
				url:url,
				cacheBust:false,
				contentType:"text/plain",
				headers: {
					"cache-control": null
				}
			};

		} else {
			params = {
				method:(config.method || "GET").toUpperCase(),
				url:url,
				cacheBust:false,
				contentType:"application/json",
				headers:{
					"X-Parse-Application-Id":this.applicationId,
					"X-Parse-REST-API-Key":this.key,
					"cache-control": null
				}
			};

			var user = Parse.RestClient.currentUser(this.applicationId);
			if(user) {
				params.headers["X-Parse-Session-Token"] = user.sessionToken;
			}
		}

		return new enyo.Ajax(params);
	},
	call:function(config) {
		if(this.batchMode) {
			this.commands.push(config);
			return;
		}

		var x = this.getAjax(config);

		if(config.method === "POST" || config.method === "PUT") {
			x.setPostBody(config.data);
			x.go();
		} else {
			x.go(config.data);
		}

		x.error(this, function(sender, response) {
			var e = {error:"Unknown Error", code:response};

			if(sender.xhrResponse.body) {
				try {
					enyo.mixin(e, enyo.json.parse(sender.xhrResponse.body));
				} catch(x) {}
			}

			this.doError(e);
			if(config.callback) {
				config.callback(sender, e);
			}
		});

		if(config.event) {
			x.response(this, function(sender, response) {
				var stop = false;
				var e = {
					response:response,
					stop:function() {
						stop = true;
					}
				};
				
				this[config.event](e);
				
				if(stop) {
					x.fail();
				} else {
					return response;
				}
			});
		}

		if(config.callback) {
			x.response(function(sender, response) {
				config.callback(sender, {response:response});
			});
		}
	},
	clean:function(o) {
		if(o) {
			for(var k in this.reservedFields) {
				delete o[k];
			}
		}
	},
	add:function(className, o, callback) {
		this.clean(o);

		this.call({
			endpoint:"classes",
			className:className,
			method:"POST",
			data:o,
			event:"doAdd",
			callback:callback
		});
	},
	retrieve:function(className, id, callback) {
		this.call({
			endpoint:"classes",
			className:className,
			method:"GET",
			id:id,
			event:"doGet",
			callback:callback
		});
	},
	update:function(className, o, callback) {
		this.clean(o);

		this.call({
			endpoint:"classes",
			className:className,
			method:"PUT",
			id:o.objectId,
			data:o,
			event:"doUpdate",
			callback:callback
		});
	},
	search:function(className, query, callback) {
		this.call({
			endpoint:"classes",
			className:className,
			method:"GET",
			data:query,
			event:"doSearch",
			callback:callback
		});
	},
	remove:function(className, id, callback) {
		this.call({
			endpoint:"classes",
			className:className,
			method:"DELETE",
			id:id,
			event:"doRemove",
			callback:callback
		});
	},
	batch:function(commands, callback) {
		if(enyo.isArray(commands)) {
			commands = {requests:commands};
		}

		this.call({
			endpoint:"batch",
			method:"POST",
			data:commands,
			event:"doBatch",
			callback:callback
		});
	},
	batchSession:function(sessionFunction, callback) {
		this.commands = [];
		this.batchMode = true;

		sessionFunction(this);

		var requests = [],
			me = this;

		enyo.forEach(this.commands, function(config) {
			requests.push({
				method:config.method,
				path:me.getPath(config.endpoint, config.className, config.id),
				body:config.data
			});
		});

		this.commands = null;
		this.batchMode = false;
		this.batch({requests:requests}, callback);
	},
	run:function(name, args, callback) {
		this.call({
			endpoint:"functions",
			className:name,
			method:"POST",
			data:args,
			callback:callback
		});
	},
	createUser:function(username, password, data, callback) {
		// remap callback when data is omitted
		if(enyo.isFunction(data)) {
			callback = data;
			data = {};
		}

		enyo.mixin(data, {
			username:username,
			password:password
		});

		this.call({
			endpoint:"users",
			method:"POST",
			event:"doCreateUser",
			data:data,
			callback:enyo.bind(this, "registerHandler", callback, data)
		});
	},
	removeUser:function(id, callback) {
		this.call({
			endpoint:"users",
			method:"DELETE",
			id:id,
			event:"doRemoveUser",
			callback:callback
		});
	},
	requestPasswordReset:function(email, callback) {
		this.call({
			endpoint:"requestPasswordReset",
			method:"POST",
			data:{email:email},
			callback:callback
		});
	},
	login:function(username, password, callback) {
		this.call({
			endpoint:"login",
			method:"GET",
			event:"doLogin",
			data:{username:username, password:password},
			callback:callback
		});
	},
	logout:function() {
		Parse.RestClient.setUser(null, this.applicationId);
	},
	registerHandler:function(callback, data, sender, event) {
		// store session key on successful login
		if(event.response) {
			Parse.RestClient.setUser(enyo.mixin(data, event.response), this.applicationId);
		}

		callback(sender, event);
	},
	loginHandler:function(sender, event) {
		// store session key on successful login
		if(event.response) {
			Parse.RestClient.setUser(event.response, this.applicationId);
		}
	},
	currentUser:function() {
		return Parse.RestClient.currentUser(this.applicationId);
	}
});