/* Array modifications
Object.prototype was the devil. 
Array prototype not so much (except when things that "look" like Arrays aren't.
There is XUtil.toArray() for that )
*/

if(is(Array.prototype.filter,'undefined'))
{
	//This prototype is provided by the Mozilla foundation and
	//is distributed under the MIT license.
	//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
	
	if (!Array.prototype.filter)
	{
	  Array.prototype.filter = function(fun /*, thisp*/)
	  {
		var len = this.length;
		if (typeof fun != "function")
		  throw new TypeError();
	
		var res = new Array();
		var thisp = arguments[1];
		for (var i = 0; i < len; i++)
		{
		  if (i in this)
		  {
			var val = this[i]; // in case fun mutates this
			if (fun.call(thisp, val, i, this))
			  res.push(val);
		  }
		}
	
		return res;
	  };
	}
}


XO.x(Array.prototype,
{
	/* just a numer to help next() and prev() */
	pointer:0,
	
	/**
	map an array though a function. You can also pass an additional object as a reference
	that you'll during each pass (like, the original array).
	Your callback then could take 0-2 arguments.
		callback = function(i,ref)
		{
			this == the value of the array at the index
			i == the index
			ref == whatever else you wanted to pass along
		}
	@param function callback
	@param mixed
	*/
	each:function(callback,reference)
	{
		var ret;
		if(is(callback,'function'))
		{
			loop:for(var i = 0; i < this.length; i++)
			{
				ret=callback.call(this[i],i,reference);
				if(ret===false)
					break loop;
			}
			return ret;
		}
		else
			Error.IllegalArgument(new Error, 'callback function', callback);
	},
	
	/*
	resets the array pointer
	@return void
	*/
	reset:function()
	{
		this.pointer = 0;
	},
	
	/*
	the value at the current position
	@return mixed
	*/
	current:function()
	{
		return this[this.pointer];
	},
	
	/*
	From the current position, has next item?
	@return boolean
	*/
	hasNext:function()
	{
		return is(this[this.pointer+1]);
	},
	
	/*
	From the current position, has previous item?
	@return boolean
	*/
	hasPrev:function()
	{
		return is(this[this.pointer-1]);
	},
	
	/*
	Increments the pointer and returns the new current value
	@return mixed
	*/
	next:function()
	{
		if(this.hasNext() == false)
			return false;
		else
		{
			this.pointer++;
			return this.current();
		}
	},
	
	/*
	Decrements the pointer and returns the new current value
	@return mixed
	*/
	prev:function()
	{
		if(this.hasPrev() == false)
			return false;
		else
		{
			this.pointer--;
			return this.current();
		}
	},
	
	/*
	Returns the first array index containing the value.
	equiv to PHP: current(array_keys($A,$val))
	@param mixed
	@return int
	*/
	key:function(val)
	{
		var ret = false;
		for(var index=0; index<this.length; index++)
		{
			if(this[index] == val)
			{
				ret = index;
				break;
			}
		}
		return ret;
	},
	
	/*
	Array contains this value?
	equiv to PHP: in_array($A,$val)
	@param mixed
	@return boolean
	*/
	has:function(val)
	{
		return is(this.key(val),'number');
	},
	
	/* 
	Filter out falsey values, [0,undefined,'']
	equiv to PHP : array_filter($A);
	@return array
	*/
	filterFalse:function()
	{
		return this.filter(function(i)
		{
			return is(i);
		});
	},
	
	/*
	Filter out values given as arguments
	@param mixed (any number of params)
	@return array
	*/
	filterOut:function()
	{
		var args = XUtil.toArray(arguments);
		return this.filter(function(value,index)
		{
			return !is(args.has(value));
		});
	},
	
	/*
	Return an array containing every other item starting with the first [0]
	@return array
	*/
	even:function()
	{
		return this.filter(function(a,i)
		{
			return i%2==0;
		});
	},
	
	/*
	Return an array containing every other item starting with the next [1]
	@return array
	*/
	odd:function()
	{
		return this.filter(function(a,i)
		{
			return i%2==1;
		});
	}
});





XO.x(Math,
{
	range:function(a,b)
	{
		if(!is(a,'number') || !is(b,'number'))
			return false;
		
		var ret = [], c = a>b?[b,a]:[a,b], i;
		for(i = Math.floor(c[0]); i<=Math.floor(c[1]); i++)
			ret.push(i);
		
		return ret;
	},
	rand:function(a,b)
	{
		if(!is(a,'number') || !is(b,'number'))
			return false;
		
		if(a==b)
			return a;
		
		var c = a>b?[b,a]:[a,b]
		return Math.floor((Math.random()*((c[1]+1)-c[0])) + c[0]);
	},
	
	/* add up values
	@param mixed  array:sums array values , or sums up numeric arguments to any end
	@return int
	Math.sum(1,2,3,4);  // same as
	Math.sum([1,2,3,4]);
	*/
	sum:function(args)
	{
		if(!is(args,'array'))
			args = XUtil.toArray(arguments);
		
		var s=0, vals = args.filter(function(i){ return is(i,'number'); });
		for(var i = 0; i<vals.length;i++)
			s+=vals[i];
		
		return s;
	},
	
	/* average values
	@param mixed  array:avg array values , or avg numeric arguments to any end
	@return float
	*/
	avg:function(args)
	{
		if(!is(args,'array'))
			args = arguments;
		
		if(!is(args.filter,'function'))
			args.filter = [].filter;
		
		var s=0, vals = args.filter(function(i){ return is(i,'number'); });
		if(vals.length==0)
			return 0;
		for(var i = 0; i<vals.length;i++)
			s+=vals[i];
		
		return s/vals.length;
	}
});
