var API = ( function ( ) {
	
	// this is the root for API XHR requests... we're using purely JSON right now
	function getApiPath ( path ) {
		return "/api/" + API.options.key + "/json" + path;
	}
	
	function getFragmentRoot ( ) {
		return "/api/" + API.options.key + "/fragments";
	}

	var handleFragment = function ( fragment, cb_function, err_function ) {
		return cb_function ( fragment );
	}
		

	var toJSON = ( function ( ) {

		function gettype ( data ) {
			if ( data == null || data == undefined ) {
				return "null";1
			}
			var t = typeof ( data );
			if ( t == "object" ) {
				if ( data instanceof Array ) return "array";
				if ( data instanceof Date ) return "date";
				return "object";
			} else {
				return t;
			}
		}

		return function ( data, type ) {
			type = type || gettype ( data );
			switch ( type ) {
			case "number":
				return data;
				break;
			case "string":
				return '"' + data.replace(/\"/g,'\\"').replace(/\n/g,'\\n') + '"';
				break;
			case "boolean":
				return data ? "true" : "false"
				break;
			case "array":
				var a = [];
				for ( var i = 0; i < data.length; i ++ ) {
					a.push ( toJSON ( data[i] ) );
				}
				return "[ " + a.join ( ", " ) + " ]";
				break;
			case "date":
				return toJSON ( data.toUTCString(), "string" );
				break;
			case "object":
				var a = [];
				for ( e in data ) {
					a.push ( toJSON ( e, "string" ) + " : " + toJSON ( data[e] ) );
				}
				return "{ " + a.join ( ", " ) + " }";
				break;
			case "null":
			default:
				return type;
				break;
			}
		}
		
	} )();
	
	// returns a function to make a basic API XHR request
	function Method ( path, args, hasBody, options ) {
		options = options || {};
		return function ( ) {
			var p = path;
			var a = args;
			for ( var i = 0; i < a; i ++ ) {
				p += "/" + arguments[i];
			}
			var b = null;
			if ( hasBody ) {
				b = arguments[args];
				a += 1;
			}
			var o = arguments[a] || {};
			for ( field in API.options ) {
				o[field] = o[field] || options[field];
			}
			return server.request(takkle.util.mergeHashes(o, { path: getApiPath(p), args: b }));
		}
	}

	// returns a Method collection
	// r - root path
	// m - method list
	// o - call options
	function Methods ( r, m, o ) {
		var methods = {};
		// if m is an array, use the listed method names, and default args
		if ( m instanceof Array ) {
			for ( var i = 0, mn; mn = m[i]; i ++ ) {
				methods[mn] = Method ( r + "/" + mn, 0, false, o );
				methods[mn].__root__ = r;
			}
		// otherwise, m is a hash, listing method names and options
		} else {
			for ( mn in m ) {
				if ( typeof m[mn] == "function" ) {
					// if the specification is a function, use it as such...
					methods[mn] = m[mn];
				} else {
					// else, if the specification is an object, extract the parts, otherwise, assume it's an args definition
					var isObj = ( typeof m[mn] == "object" );
					var args = ( isObj ? m[mn].args : m[mn] );
					var body = ( isObj ? m[mn].body : false );
					var options = ( isObj ? m[mn].options || {} : {} );
					o = o || {};
					for ( field in API.options ) {
						o[field] = o[field] || options[field];
					}
					methods[mn] = Method ( r + "/" + mn, args, body, o );
				}
				methods[mn].__root__ = r;
			}
		}
		return methods;
	}

	// returns a set of basic API XHR requests
	// namespace - the base name of the method set (eg. user, search, photo...)
	// methods - an array of method names, or a hash of method names and function options
	// default_id - if the namespace accepts an id, but can have a default, this is it
	// nullObject - if the namespace doesn't accept an id, set this to true
	function MethodSet ( namespace, methods, default_id, nullObject ) {
		var f = function ( root, options ) {
			var key = root.replace(/\/+$/,'') + ":ms";
			m = Methods ( root, methods, options );
			return m;
		}
		if ( nullObject ) {
			var b = "/" + namespace;
			return function ( options ) {
				var r = ( arguments.callee.__root__ || "" ) + b;
				return f ( r, options );
			}
		} else if ( default_id ) {
			var b = "/" + namespace + "/";
			var d = default_id;
			return function ( id, options ) {
				var r = ( arguments.callee.__root__ || "" ) + b + ( id || d );
				return f ( r, options );
			}
		} else {
			var b = "/" + namespace + "/";
			return function ( id, options ) {
				var r = ( arguments.callee.__root__ || "" ) + b + id;
				return f ( r, options );
			}
		}
	}

	// this is the actual API reference
	return {

		// function to allow us to make arbitrary requests
		__call : function ( path, cb, err, oc ) {
			return doRequest ( path, cb, err, oc );
		},

		// config options
		options : {
			key : "-",
			method:"",
			onComplete : function ( obj ) { },
			onError : function ( err ) { 
			}
		},

		// alias for external use
		getApiPath: function (path) {
			return getApiPath(path);
		},

		// used to get an HTML fragment from the server. for example
		// next/prev buttons on the ajaxy thumb galleries.
		fragments : function ( fragment, body, page, options ) {
			page = Math.max ( 1, parseInt ( page ) || 1 );
			var path = getFragmentRoot();
			path += "/" + fragment.replace ( /(^\/+|\/+$)/g, '' );
			path += "/page/" + ( page || 1 );
			return server.request(takkle.util.mergeHashes(options, { method: 'get', path: path, args: body }));
		},

// 		echo : function ( request, options ) {
// 			var url = request.url || "";
// 			var get = request.get;
// 			var post = request.post;
// 			var path = "/echo/" + url.replace(/(^\/+|\/+$)/g, "");
// 			if ( get ) {
// 				var a = [];
// 				for ( e in get ) {
// 					a.push ( escape ( e ) + "=" + escape ( get[e] ) );
// 				}
// 				path += "?" + a.join ( "&" );
// 			}
// 			XHR.request ( path, post, options );
// 		},
		
		requestPassword : function ( email, options ) {
			server.request(takkle.util.mergeHashes(options, { method: 'post', path: getApiPath('/requestpassword'), args: { 'email' : email } }));
		},

		contest : MethodSet ( "contest", {
			info: 0,
			related: 0,
			vote: {
				args: 0,
				body: true,
				options: {
				}
			},
			"add_comment" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"submit_wendys_coupon" : {
				args : 0,
				body : true,
				options : {
				}
			}
		},null,true ),
		forum : MethodSet ( "forum", {
			info: 0,
			related: 0,
			voteComment: {
				args: 0,
				body: true,
				options: {
				}			
			}
		},null,true ),
		photo : MethodSet ( "photo", {
			"info" : 0,
			"related" : 0,
			"add_comment" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"rate" : {
				args : 0,
				body : true,
				options : {
				}
			}
		} ),

		video : MethodSet ( "video", {
			"info" : 0,
			"related" : 0,
			"add_comment" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"rate" : {
				args : 0,
				body : true,
				options : {
				}
			}
		} ),
		comment : MethodSet ( "comment", {
			"add_comment" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"get_comments" : {
				args : 0,
				body : true,
				options : {
				}
			}
		}
		,null,true),
		upload : MethodSet ( "upload", {
			"claim" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"queue" : {
				args : 0,
				body : true,
				options : {
				}
			}
		}
		,null,true),
		battle : MethodSet ( "battle", {
			"categories": {
				args: 0,
				body: true,
				options: {
					method: "get"
				}
			},
			"subcategories" : {
				args: 0,
				body: true,
				options: {
					method: "get"
				}
			},
			"get_battle_results": {
				args: 0,
				body: true,
				options: {
				}
			},
			"get_external_video_id" : {
				args : 0,
				body : true,
				options : {
				}
			}
		}
		,null,true),
		school : MethodSet ( "school", [
			"info",
			"photos",
			"videos",
			"members"
		] ),

		team : MethodSet ( "team", [
			"info",
			"photos",
			"videos",
			"members"
		] ),

		group : MethodSet ( "group", {
			"info" : 0,
			"photos" : 0,
			"videos" : 0,
			"add_comment" : {
				args : 0,
				body : true,
				options : {
				}
			}
//			"members"
		} ),

		user : MethodSet ( "user", {
			"info" : 0,
			"groups" : 0,
			"teams" : 0,
			"school" : 0,
			"friends" : 0,
			"photos" : 0,
			"videos" : 0,
			"stats" : 0,
			"block" : 1,
			"add_comment" : {
				args : 1,
				body : true,
				options : {
				}
			},
			"add_photo" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"add_throwdown" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"send_invites" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"add_video" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"add_friend" : {
				args : 0,
				body : true
			},
			"remove_friend" : {
				args : 0,
				body : true
			},
			"set_primary_photo" : {
				args : 1,
				body : true
			},
			"attributes" : {
				args : 1,
				body : true
			},
			"user_attributes" : {
				args : 0,
				body : true
			},
			"trash_talk" : {
				args : 0,
				body : true
			},
			"user_status" : {
				args : 0,
				body : true
			},
			"summary_stats" : {
				args : 0,
				body : true
			},
			"rate_top_100" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"get_top_100_rating" : {
				args : 0,
				body : true,
				options : {
				}
			},
			"nominate_si" : 0,
			"profile" : MethodSet ( "profile", {
				"get" : 1,
				"set" : {
					args : 1,
					body : true,
					options : {
					}
				}
			}, null, true )
		}, "current" ),
		flash : MethodSet ( "flash", {
			"photo" : {
				args : 0,
				body : true,
					options : {
					}
			}
		},null,true),
		report : MethodSet ( "report", {
			"bug" : {
				args : 0,
				body : true,
					options : {
					}
			},
			"issue" : {
				args : 0,
				body : true,
					options : {
					}
			}
		},null,true),
		si : MethodSet ( "si", {
			"nominate_face" : {
				args : 0,
				body : true,
					options : {
					}
			}
		},null,true),
		search : MethodSet ( "search", {
			"all" : 1,
			"users" : 1,
			"schools" : 1,
			"teams" : 1,
			"groups" : 1,
			"photos" : 1,
			"videos" : 1
		}, null, true )

	};

} )();
