
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: equals
	,diff: diff
	,partialDiff: partialDiff
	,kvClone: kvClone
	,simpleClone: simpleClone
	,simpleEquals: simpleEquals
	,sprintf: sprintf

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

	,format: function (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: function (value, token, digits)
	{
		return new Number (value).toFixed (parseInt (digits));
	}

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