var VnDate = require('./date');

/**
 * Clones a simple value. A simple value is any value that is not an array,
 * object or function. If non-simple value is passed, the same object reference
 * is returned.
 *
 * @param {*} value The value to be copied
 * @return {*} The value copy
 */
function simpleClone(value) {
	if (value instanceof Date)
		return value.clone();

	return value;
}

/**
 * Checks if two simple values are equal using the strict equality operator. For
 * information about simple values see simpleClone() function.
 *
 * @param {*} a Value to compare to
 * @param {*} b Value to compare with
 * @return {boolean} %true if they are equal, %false otherwise
 */
function simpleEquals(a, b) {
	if (a === b)
		return true;
	if (a instanceof Date && b instanceof Date)
		return a.getTime() === b.getTime();

	return false;
}

/**
 * Calculates differences between two simple key-value objects.
 *
 * @param {Object} orgObject Value to compare to
 * @param {Object} newObject Value to compare with
 * @return {Object} The differences or %null if there are no differences
 */
function diff(orgObject, newObject) {
	var diff = {};

	for (var key in orgObject)
	if (!simpleEquals(orgObject[key], newObject[key]))
		diff[key] = simpleClone(newObject[key]);

	for (var key in newObject)
	if (orgObject[key] === undefined && newObject[key] !== undefined)
		diff[key] = simpleClone(newObject[key]);

	if (Object.keys(diff).length > 0)
		return diff;

	return null;
}

/**
 * Calculates new differences between two simple key-value objects.
 *
 * @param {Object} orgObject Value to compare to
 * @param {Object} newObject Value to compare with
 * @return {Object} The differences or %null if there are no differences
 */
function partialDiff(orgObject, newObject) {
	var diff = {};

	for (var key in newObject)
	if (!simpleEquals(orgObject[key], newObject[key]))
		diff[key] = simpleClone(newObject[key]);

	if (Object.keys(diff).length > 0)
		return diff;

	return null;
}

/**
 * Clones a simple key-value object in wich properties are simple values. For
 * information about simple values see simpleClone() function.
 * 
 * @param {*} object The object to be cloned
 * @return The cloned object
 */
function kvClone(object) {
	var copy = {};

	for (var key in object)
		copy[key] = simpleClone(object[key]);

	return copy;
}

/**
 * Checks if two values are equal, it also checks objects. Basic values are
 * compared using the strict equality operator.
 *
 * @param {*} a Value to compare to
 * @param {*} b Value to compare with
 * @return {boolean} %true if they are equal, %false otherwise
 */
function equals(a, b) {
	if (a === b)
		return true;
	if (a instanceof Date && b instanceof Date)
		return a.getTime() === b.getTime();
	if (a && b && (typeof a === 'object') && (typeof b === 'object')) {
		for (var key in a)
		if (!equals(a[key], b[key]))
			return false;

		for (var key in b)
		if (a[key] === undefined && b[key] !== undefined)
			return false;

		return true;
	}

	return false;
}

/**
 * Returns a formated string.
 *
 * @param {Object} formatString The base string template
 * @param {...} arguments Format parameters
 * @return {string} The formated string
 */
function sprintf(formatString) {
	var args = arguments;

	if (args.length <= 1)
		return formatString;
	
	var i = 1;
	return formatString.replace(/%[s|d]/g, function() {
		return args[i++];
	});
}

module.exports = {
	 regexpNumber: /%\.([0-9]+)d/g
	,regexpString: /%s/g

	,equals
	,diff
	,partialDiff
	,kvClone
	,simpleClone
	,simpleEquals
	,sprintf

	,compare(a, b) {
		if (a === b)
			return true;
		if (a instanceof Date && b instanceof Date)
			return a.getTime() === b.getTime();
			
		return false;		
	}

	,format(value, format) {
		if (value === null || value === undefined)
			return '';
	
		if (format)
		switch (typeof value) {
			case 'number':
				return format.replace(this.regexpNumber,
					this.replaceNumber.bind(null, value));
			case 'string':
				return format.replace(this.regexpString,
					this.replaceString.bind(null, value));
			case 'object':
			if (value instanceof Date)
				return VnDate.strftime(value, format);
		}
		
		return value;
	}

	,replaceNumber(value, token, digits) {
		return new Number(value).toFixed(parseInt(digits));
	}

	,replaceString(value) {
		return value;
	}
};