/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
/**
 * TODO: If the user agrees, set the username/passs in a cookie
**/
define([
	"underscore"
	, "backbone"
	, "toolbox"
	, "models/dmap-type"
	, "models/dmap"
], function (_, Backbone, Toolbox, DMAPType, DMAPModel) {
	"use strict";

	var Client			= Toolbox.Base.extend(_.extend({

		constructor:	function(attributes, options) {
			_.bindAll(this
				, "setAuth"
				, "setState"
				, "getState"
				, "init"
				, "deinit"
				, "logout"
				, "buildRequestUid"
				, "url"
				, "urlHost"

				, "xhr"
				, "fetchXHR"
				, "fetchContentTypes"
				, "fetchServerInfo"
				, "fetchLogin"
				, "fetchDatabases"
				, "fetchDatabase"
				, "fetchPlaylists"
				, "fetchPlaylist"
				, "fetchBrowse"
				, "fetchgenres"
				, "fetchArtists"
				, "fetchAlbums"

				, "on"
				, "off"
				, "trigger"
			);

			_.extend(this, Client.defaults);
			this.attributes	= _.extend({}, Client.prototype.attributes, attributes);

			this.setAuth();

			return this;
		}

		, setAuth:	function(username, password) {
			if (!_.isUndefined(username)) {
				this.attributes.username	= username;
			}

			if (!_.isUndefined(password)) {
				this.attributes.password	= password;
			}

			if (!_.isEmpty(this.attributes.password) || !_.isEmpty(this.attributes.username)) {
				this.attributes.basicauth	= window.btoa((this.attributes.username || "") + ":" + (this.attributes.password || ""));
			}
		}

		// TODO: Many process should be able to set a state at the same time and it will change only when all of them are finoshed
		, setState:		function (state) {
			if (_.isNumber(state)) {
				this.trigger("state", this, state);

				return this.state	= state;
			} else if (_.isString(state) && !_.isUndefined(this._states[state])) {
				this.trigger("state", this, this._states[state]);

				return this.state	= this._states[state];
			}

			return undefined;
		}

		, getState:		function () {
			return this.state;
		}

		, init:			function () {
			var that	= this
			;

			this.setState("connecting");

			//console.debug("Fetching content types");
			that.fetchContentTypes(function __fnClientCbFetchContentTypes(contentTypes) {
				//console.debug("Fetching server info");
				that.fetchServerInfo(function __fnClientCbFetchServerInfo(serverInfo) {
					//console.debug("Fetching login");
					that.fetchLogin(function __fnClientCbFetchLogin(sessionInfo) {
						// TODO: Here we need to request /update for the database version
						//console.debug("Fetching databases info");
						//that.fetchDatabases(function () {
							//console.debug("Fetching database 1");
							//that.fetchDatabase(function () {
								//console.debug("Fetching playlists info");
								//that.fetchPlaylists(function () {
									//console.debug("Fetching playlist 1 of db 1");
									//that.fetchPlaylist(function () {
										//console.debug("Fetching genres of db 1");
										//that.fetchGenres(function () {
											//console.debug("Fetching artists of db 1");
											//that.fetchArtists(function () {
												//console.debug("Fetching albums of db 1");
												//that.fetchAlbums(function () {
													that.setState("connected");
													that.trigger("inited", serverInfo);
												//});
											//});
										//});
									//});
								//});
							//});
						//});
					});
				});
			});

			return this;
		}

		, deinit:			function () {
			var that	= this
			;

			this.logout();
		}

		,logout:			function () {

		}

		, buildRequestUid:	function(path, fields, query) {
			var that	= this
			;

			if (_.isEmpty(path) || !_.isString(path)) {
				throw new Error("INVALID_TYPE_ERROR");
			}

			if (!_.isArray(fields)) {
				fields	= [];
			}

			if (!_.isArray(query)) {
				query	= [];
			}


		}

		,url:		function (request) {
			var that	= this
			,	uri		= ""
			;

			if (typeof request === "string") {
				uri	= request; // Add leading slash here!
			}

			if (_.isNumber(this.collections.session.id)) {
				var prefix = "?";

				if (uri.indexOf("?") >= 0) {
					prefix	= "&";
				}

				uri += prefix + "session-id=" + this.collections.session.id;
			}

			return this.attributes.protocol + "://" + this.urlHost() + uri;
		}

		, urlHost:		function () {
			var that	= this
			;

			return this.attributes.hostname + ":" + this.attributes.port;
		}

		// TODO: Could be interesting to set a useragent, could possibly affect transcoding on the server side
		, xhr:			function (url, responseType, callback) {
			var that	= this
			;

			var xhr = new XMLHttpRequest();
			var contentType = "text/plain; charset=utf-8";

			if (typeof responseType === "function") {
				callback		= responseType;
				responseType	= "arraybuffer";
			}

			xhr.open("GET", url, true);
			xhr.responseType	= responseType;
			xhr.timeout			= 2000;
			xhr.setRequestHeader("Content-type", contentType);
			xhr.overrideMimeType("text/plain; charset=x-user-defined"); // Technically not need with v2 and responseType but for good measures

			if (!_.isEmpty(that.attributes.basicauth)) {
				xhr.withCredentials = true;
				xhr.setRequestHeader("Authorization", "Basic " + that.attributes.basicauth);
			}

			try {
				xhr.onerror		= function () {console.log("Error", arguments, this);};
				xhr.onabort		= function () {console.log("Abort", arguments, this);};
				xhr.ontimeout	= function () {
					that.setState("timedout");
				};

				xhr.onload		= function (e) {
					if (this.status === 200) {
						callback(null, this.response);
					} else if (this.status === 401) {
						that.setState("unauthorized");
						that.trigger("unauthorized", that, [this, e]);
					} else {
						that.setState("failed");
						callback(e.status);
					}
				};

				xhr.send();
			} catch (e) {
				console.error(e);
				callback(e, null);
			}
		}

		, fetchXHR:				function (path, success, error) {
			var that	= this
			;

			that.xhr(that.url(path), function __fetchXHRCallback(err, content) {
				if (err) {
					if (!_.isFunction(error)) {
						error	= function (err, cont) {
							throw [err, cont];
						};
					}

					error(err, content);
				} else {
					success(content);
				}
			});
		}

		, fetchContentTypes:	function (success, error) {
			var that	= this
			;

			that.fetchXHR("/content-codes", function __fetchContentTypesCallback(content) {
				that.collections.contentCodes	= new DMAPModel(content);
				if (_.isFunction(success)) {
					success(that.collections.contentCodes);
				}
			}, error);
		}

		, fetchServerInfo:		function (success, error) {
			var that	= this
			;

			that.fetchXHR("/server-info", function __fetchServerInfoCallback(content) {
				that.collections.serverInfo	= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.serverInfo);
				}
			}, error);
		}

		, fetchLogin:			function (success, error) {
			var that	= this
			;

			that.fetchXHR("/login", success, error);
		}

		, fetchDatabases:			function (success, error) {
			var that	= this
			;


			this.setState("loading");

			that.fetchXHR("/databases", function __fetchDatabasesCallback(content) {
				that.collections.databasesInfo		= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.databasesInfo);
				}

				that.setState("loaded");
			}, error);
		}

		, fetchDatabase:			function (success, error, dbId) {
			var that	= this
			;

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId	= 1;
			}


			this.setState("loading");

			that.fetchXHR("/databases/" + dbId + "/items", function __fetchDatabaseCallback(content) {
				that.collections.databases[dbId]	= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.databases[dbId]);
				}

				that.setState("loaded");
			}, error);
		}

		, fetchPlaylists:			function (success, error, dbId) {
			var that	= this
			;

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId	= 1;
			}


			this.setState("loading");

			that.fetchXHR("/databases/" + dbId + "/containers", function __fetchPlaylistsCallback(content) {
				that.collections.playlistsInfo		= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.playlistsInfo);
				}

				that.setState("loaded");
			}, error);
		}

		, fetchPlaylist:			function (success, error, playlistId, dbId) {
			var that	= this
			;

			if (_.isUndefined(playlistId) || !_.isNumeric(playlistId)) {
				playlistId	= 1;
			}

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId		= 1;
			}

			var requestUri	= "/databases/"
			;

			requestUri	+= dbId;
			requestUri	+= "/containers/";
			requestUri	+= playlistId;
			requestUri	+= "/items";
			requestUri	+= "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";


			this.setState("loading");

			that.fetchXHR(requestUri, function __fetchPlaylistCallback(content) {
				if (!_.isArray(that.collections.playlists[dbId])) {
					that.collections.playlists[dbId]	= [];
				}

				that.collections.playlists[dbId][playlistId]	= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.playlists[dbId][playlistId]);
				}

				that.setState("loaded");
			}, error);
		}

		, fetchBrowse:			function (success, error, dbId, type) {
			var that	= this
			;

			// TODO: We need to detect if the server supports browsing or not an implement our own otherwise.
			if (_.isEmpty(type) || !_.isString(type) || !_.isArray(that.collections.browser[type])) {
				throw new Error("INVALID_BROWSE_TYPE");
			}

			var requestUri	= "/databases/"
			;

			requestUri	+= dbId;
			requestUri	+= "/browse/";
			requestUri	+= type;
			requestUri	+= "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";


			this.setState("loading");

			that.fetchXHR(requestUri, function __fetchBrowseCallback(content) {

				that.collections.browser[type][dbId]	= new DMAPModel(content, {
					contentCodes:	that.collections.contentCodes
				});

				if (_.isFunction(success)) {
					success(that.collections.browser[type][dbId]);
				}

				that.setState("loaded");
			}, error);
		}

		, fetchGenres:			function (success, error, dbId) {
			var that	= this
			;

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId		= 1;
			}

			return that.fetchBrowse(success, error, dbId, "genres");
		}

		, fetchArtists:			function (success, error, dbId) {
			var that	= this
			;

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId		= 1;
			}

			return that.fetchBrowse(success, error, dbId, "artists");
		}

		, fetchAlbums:			function (success, error, dbId) {
			var that	= this
			;

			if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
				dbId		= 1;
			}

			return that.fetchBrowse(success, error, dbId, "albums");
		}

	}, Backbone.Events), {
		defaults: {
			attributes:		{
				protocol:		window.location.protocol.replace(":", "")
				, hostname:		window.location.hostname
				, port:			3689
				, path:			""
				, username:		""
				, password:		""
			}

			, state:			0

			, _states:	{
				failed:			-1
				, disconnected:	0
				, timedout:		1
				, connecting:	2
				, unauthorized:	3
				, connected:	4
				, loading:		5
				, loaded:		6
			}

			, collections:	{
				contentCodes:	null

				, serverInfo:	null

				, session:	{
					id:			null
				}

				, databasesInfo:null

				, databases:	[]

				, playlistsInfo:null

				, playlists:	[]

				, browser:	{
					genres:		[]

					, artists:	[]

					, albums:	[]
				}
			}
		}
	});

	//_.extend(Client.prototype, Backbone.Events);

	return Client;
});