/**
 * Copyright (c) 2006, Opera Software ASA
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Opera Software ASA nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY OPERA SOFTWARE ASA AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL OPERA SOFTWARE ASA AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @fileoverview
 * Generic functions used by the counterLib library
 *
 * @author Magnus Kristiansen
 * @version 1.0
 */

/**
 * Gets the array index of the last element with the given value
 * @param {Object} val The value to locate
 * @return The index of the element, null if none found
 * @type Number
 */
Array.prototype.getIndexByValue = function(val) {
	for (var i = this.length; i--;) {
		if (this[i] == val) {
			return i;
		}
	}
	return null;
}
/**
 * Gets the array index of the last element with the property equal to the given value
 * @param {Object} val The value to locate
 * @param {String} prop The property to check
 * @return The index of the element, null if none found
 * @type Number
 */
Array.prototype.getIndexByPropValue = function(val, prop) {
	for (var i = this.length; i--;) {
		if (this[i] && this[i][prop] == val) {
			return i;
		}
	}
	return null;
}
/**
 * Removes the last element with the given value from the array
 * @param {Object} val The value to remove
 * @return The element removed, null if none found
 * @type Object
 */
Array.prototype.removeByValue = function(val) {
	var idx = this.getIndexByValue(val);
	if (idx != null) return this.splice(idx, 1);
	return null;
}
/**
 * Shifts the date object in time.
 *
 * Valid units are: years, months, days, hours, minutes, seconds, milliseconds.
 * Default length is 0, default unit is milliseconds.
 *
 * @param {Number} length The number of units to shift by
 * @param {String} unit The unit type
 * @param {Boolean} clone Creates a new Date object, instead of modifying original
 * @return A time-shifted date object
 * @type Date
 */
Date.prototype.shift = function( length, unit, clone ) {
	var d = clone ? new Date(this) : this;
	if (! length) return d;
	switch (unit) {
		case 'years' :
			d.setFullYear( d.getFullYear() + length );
			break;
		case 'months' :
			d.setMonth( d.getMonth() + length );
			break;
		case 'days' :
			length *= 24;
		case 'hours' :
			length *= 60;
		case 'minutes' :
			length *= 60;
		case 'seconds' :
			length *= 1000;
		case 'milliseconds' :
		default :
			d.setTime( d.getTime() + length );
	}
	return d;
}
/**
 * [mod] Extended typeof for detailed object types
 *
 * Modified for counterLib
 * @param {Object} obj Object to type
 * @return The object's type
 * @type String
 */
function typeofDetailed( obj ) {
	var t = typeof obj;

	if (t == 'object') {
		if (obj) {
			switch (obj.constructor) {
				case Object :
					break;
				case Array : 
					t = 'array';
					break;
				case Date :
					t = 'date';
					break;
				case Counter :
					t = 'counter';
					break;
				default :
					//opera.postError('line 96 in util: ' + obj.constructor);
					t = 'customobject';
					break;
			}
		} else {
			t = 'null';
		}
	}
	return t;
}
/**
 * [mod] Extended toString for detailed object string
 *
 * Modified for counterLib
 * @param {Object} obj Object to serialize
 * @return The serialized string
 * @type String
 */
function toStringDetailed( obj, first ) {
	var ret;

	switch ( typeofDetailed(obj) ) {
		case 'undefined' :
		case 'null' :
		case 'number' :
		case 'boolean' :
			ret = obj;
			break;
		case 'string' :
			ret = '"' + obj.replace(/"/g,'\\"') + '"';
			break;
		case 'date' :
			ret = 'new Date(' + obj.getTime() + ')';
			break;
		case 'array' :
			var retsub = [];
			for (var i = 0, len = obj.length; i < len; i++) {
				retsub.push( toStringDetailed( obj[i] ) );
			}
			ret = '[' + retsub.join(',') + ']';
			break;
		case 'counter' :
			if (! first) {
				return obj.id;
				break;
			}
		case 'object' :
			var retsub = [];
			for (var i in obj) {
				if (obj.hasOwnProperty(i)) {
					retsub.push( i + ' : ' + toStringDetailed( obj[i] ) );
				}
			}
			ret = '{' + retsub.join(',') + '}';
			break;
		case 'function' :
			ret = '#ERROR# function #ERROR#';
			break;
		case 'customobject' :
			ret = '#ERROR# custom object #ERROR#';
			break;
		default :
			ret = '#ERROR# default value #ERROR#';
			break;
	}
	
	return ret;
}
