////////////////////////////////////////////////////////////////////////////////////////////////////
// Phos
//
// © 2009, 2010, 2011 David J. Goehrig <dave@nexttolast.com>
// 
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////////////////////////

Object.prototype.a = Object.prototype.an = function(x,v) { 
	return x.can('init') ? x.clone().init(v): x.clone() 
};

// Constructor Global
A = An = {
	object: function() { return a({}) },
	string: function() { return a('') },
	array: function() { return a([]) },
	process: function() { return Function.spawn() }
};

Object.prototype.the = function(x) { return x };

// Singletons
The = {};

Object.prototype.value = function() { return this; }

Object.prototype.clone = function() {
	var Proto = function() {};
	Proto.prototype = this;
	return new Proto() 
}

Object.prototype.copy = function(o) {
	var me = this;
	o.each(function(v,k) { me[k] = v });
	return this 
}

Object.prototype.does = function(v,f) { this[v] = f; return this }

String.prototype.capitalize = function() { return this.toString()[0].toUpperCase() + this.substr(1) }

Object.prototype.has = function() {
	var me = this;
	for ( var i = 0; i < arguments.length; ++i)  {
		(function(name) {
			var prop = name.capitalize();
			me[name] = function() {
				if (arguments.length) { me[prop] = arguments[0]; return me }
				return me[prop]
			};
		})(arguments[i]);
	}
	return me;
}

Object.prototype.from = function() {
	this['init'] = function() { return this };
	for (var i = 0; i < arguments.length; ++i) this.copy(arguments[i]);
	return this 
}

Object.prototype.contains = function(e) {
	var retval = false;
	this.each(function(v,k) { if (k == e) return retval = true });
	return retval 
}

Object.prototype.owns = function(k)  {
	return this.hasOwnProperty(k)
}

Object.prototype.each = function(f) {
	for (var k in this) if (this.owns(k) && k != 'prototype') f(this[k],k);
	return this 
}

Object.prototype.all = function(f) {
	for (var k in this) if (k != 'prototype') f(this[k],k);
	return this 
}

Object.prototype.which = function(f) {
	var w = [];
	this.each(function(v,k) { if (f(v,k)) w.push(v) });
	return w 
}

Object.prototype.can = function(k) { return (typeof(this[k]) == "function") }

Object.prototype.slots = function() {
	var i = 0;
	this.each(function(v,k) { if (k && v) ++i });
	return i 
}

Object.prototype.its = function(k) { return k.last() != '*' ? this[k.append('*')] : this[k] }

Object.prototype.of = function(x,k) { 
	var args = [ arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ];
	var me = this;
	x.parts().every(function(p,i) { if (p.is(me) && p.can(k)) p[k](args[1],args[2],args[3],args[4])});
	return this 
}

Object.prototype.names = function() {
	var retval = [];
	var me = this;
	window.each(function(v,k) { if (v === me) retval.push(k) });
	return retval 
}

Object.prototype.name = function() { return this.names()[0] }
Object.prototype.plural = function(x) { return window[x] = this }

Object.prototype.named =  function(x) {
	var lc = x.name().toLowerCase();
	var me = this;
	An[lc]= function(y) { return a(me,y) };
	return window[x.name()] = this 
}

Object.prototype.is = function(x) {
	var me = this;
	var retval = true;
	x.all(function(v,k) { if (x.can(k) && !me.can(k)) return retval = false });
	return retval 
}

Object.prototype.implements = function() {
	var me = this;
	var retval = [];
	Objects.each(function(v,k) { if (me.is(v)) retval.push(k) });
	return retval 
}

Object.prototype.any = function(f) {
	var retval = null;
	this.each(function(v,k) { if (f(v,k)) return retval = v });
	return retval;
}

Object.prototype.module = function() {
	var ots = Object.prototype.toString;
	Object.prototype.toString = function() {
		var retval = '{ ';
		this.each(function(v,k) { 
			if (typeof(v) == 'function') retval = retval.append(k,': ',v,', '); 
			if (typeof(v) == 'string') retval = retval.append(k,': "', v, '", ');
			if (typeof(v) == 'number') retval = retval.append(k,': ', v, ', ');
			if (typeof(v) == 'boolean') retval = retval.append(k,': ', v, ', ');
			if (typeof(v) == 'object' && v.can('every')) 
				retval = retval.append(k, ': [', v.join(',') , '], ');
			if (typeof(v) == 'object') retval = retval.append(k, ': ', v.name(), ', ');
		});
		return retval.append('}') };
	var retval = 'function() { return '.append( this.toString(), ' }');
	Object.prototype.toString = ots;
	return retval 
}

Object.prototype.use = function() {
	var modules = [];
	modules.push.apply(modules,arguments);
	var module = modules.shift();
	var url = document.location.append(module);
	var cb = function(txt) {
		if (!txt) alert('Failed to load '.append(url));
		try { 
			eval('( function () { '.append(txt,' } )'))(); 
		} catch(e) { alert('Load error: '.append(e,':',txt)) }
		if (modules.length > 0 ) {
			var module = modules.shift();
			var url = document.location.append(module);
			url.get(cb);
		}
	};
	return url.get(cb) 
}
	
Object.prototype.download = function() {
	document.location.href = "data:application/javascript,".append(this.toString().encode());
	return this;
}

Object.prototype.when = HTMLElement.prototype.when = function(e,f) {
	this.addEventListener(e,f,false);
	return this;
}



Function.prototype.spawn = function() { 
	var fun = function() { 
		if (fun.caller != null) return fun.send.apply(fun,arguments);
		for (var i = 0; i < (fun.Delay < 1 && fun.Delay > 0 ? 1/fun.Delay: 1); ++i) {
			if (!fun.Mailbox.length) return;
			var message = fun.Mailbox.shift();
			var op = message.shift();
			if (typeof(fun[op]) == "function")
				return fun[op].apply(fun,message);
			else fun["_"].apply(fun,[ op, message]);
		}
	};
	fun.Mailbox = [];
	return fun.start();
};

Fun = function() { return Function.spawn() };

Function.prototype.does = function(K,X) {
	this[K] = X;
	return this;
}

Function.prototype.send = function() {
	var args = [];
	for (var i = 0; i < arguments.length; ++i) args.push(arguments[i]);
	this.Mailbox.push(args);
	return this;
}

Function.prototype.delay = function() {
	if (arguments.length) {
		this.Delay = arguments[0];
		return this;
	}
	return this.delay;
}

Function.prototype.start = function() {
	var fun = this;
	if (fun.Pid) return;
	this.Pid = setInterval( fun, fun.Delay < 1 ? 1 :fun.Delay );
	return this;
}

Function.prototype.stop = function() {
	clearInterval(this.Pid);
	this.Pid = false;
	return this;
}

Function.prototype.clear = function() {
	this.Mailbox = [];
}

Function.prototype._ = function(op) {
	console.log(this.name + " does not understand " +  op);
}

Function.prototype.then = function(f) {
	var me = this;
	this.Pending = function() { 
		var results = f.apply(f, arguments); 
		if (!results) results = [];
		f.process(results);
	}
	return f;
}

Function.prototype.process = function() {
	if (typeof(this.Pending) == "function") 
		this.Pending.apply(this.Pending,arguments);
}

Array.prototype.every = function(f) {
	for (var i = 0; i < this.length; ++i) f(this[i],i);
	return this;
}

Array.prototype.map = function(f) {
	var retval = [];
	this.every(function(x,i) { retval.push(f(x)) });
	return retval;
}

Array.prototype.apply = function(f,o) {
	var retval = o;
	this.every(function(x,i) { retval = f(retval,x) });
	return retval;
}
	
Array.prototype.contains = function(e) {
	var retval = false;
	this.every(function(x,i) { if (x == e) return retval = true });
	return retval;
}

Array.prototype.append = function(a) {
	this.push.apply(this,a);
	return this
}

Array.prototype.and = function() {
	this.push.apply(this,arguments);
	return this
}

Array.prototype.except = function (e) { 
	for (var i = 0; i < this.length; ++i) if (this[i] == e) this.splice(i,1);	
}

Array.prototype.join = function(s) {
	var retval = '';
	for (var i = 0; i < this.length - 1; ++i) retval += this[i] + s;
	return retval + this[i];
}

String.prototype.last = function() { 
	return this.substring(this.length-1) 
}

String.prototype.first = function() { 
	return this.substring(0,1) 
}

String.prototype.decode = function() { 
	return unescape(this) 
}

String.prototype.encode = function() { 
	return escape(this) 
}

String.prototype.append = function() {
	var retval = this;
	for (var i = 0; i < arguments.length; ++i) retval += arguments[i].toString();
	return retval 
}

String.prototype.contains = function(s) { 
	return 0 <= this.indexOf(s) 
}

String.prototype.name = function() { 
	return this 
}

String.prototype.join = function() {
	var retval = '';
	for (var i = 0; i < arguments.length - 1; ++i) retval += arguments[i] + this;
	return retval + arguments[i];
}

String.prototype.request = function(method,data) {
	var _request = XMLHttpRequest ? new XMLHttpRequest(): _doc.createRequest();
	var cb = function () {
		if (this.readyState != 4) return;
		if (this.status == 404) cb.process(null);
		if (this.status == 200) cb.process(this.responseText);
	};
	_request.onreadystatechange =  cb;
	_request.open(method,this,true);
	_request.setRequestHeader('Content-Type','application/json');
	_request.send(data ? data : '');
	return cb;
}

String.prototype.get = function() { return this.request("GET") }
String.prototype.post = function(data) { return this.request("POST",data) }
String.prototype.put = function(data) { return this.request("PUT",data) }
String.prototype.del = function() { return this.request("DELETE") }


