
var Promix = require('promix');

function standardizeArguments(args) {
	if (!TypeHelper.isArray(args)) {
		args = Utility_ArrayHelper.toArray(args);
	}

	var
		index = args.length,
		arg;

	if (index === 1 && TypeHelper.isArray(args[0])) {
		return [
			args[0].join(' ')
		];
	}

	while (index--) {
		arg = args[index];

		if (arg && TypeHelper.isFunction(arg.toJSON)) {
			// if this is argument exposes a .toJSON() method,
			// (in other words, if it's a Backbone model),
			// we should just extract the attributes:
			args[index] = arg.toJSON();
		}
	}

	return args;
}

function serializeObject(object, seen, levels) {
	if (!levels) {
		levels = 0;
	}

	levels++;

	if (levels === 4) {
		return;
	}

	var
		key,
		result = { };

	for (key in object) {
		result[key] = serializeValue(object[key], seen, levels);
	}

	return JSON.stringify(result);
}

function serializeError(error) {
	var error_message = error.toString();

	if (!error.stack) {
		return error_message;
	}

	var lines = error.stack.split('\n');

	if (_.isServer()) {
		lines = lines.slice(0, Config.logger.error_line_limit);
	}

	if (error.stack.indexOf(error_message) !== 0) {
		lines.unshift(error_message);
	}

	return lines.join('\n');
}

function serializeValue(value, seen, levels) {
	if (TypeHelper.isFunction(value)) {
		if (value.name) {
			return '[function ' + value.name + ']';
		} else {
			return '[anonymous function]';
		}
	}

	if (TypeHelper.isError(value)) {
		return serializeError(value);
	}

	if (!TypeHelper.isArray(seen)) {
		if (TypeHelper.isString(value)) {
			return value;
		}

		seen = [ ];
	} else if (seen.indexOf(value) !== -1) {
		return '[circular]';
	}

	if (TypeHelper.isString(value)) {
		return '"' + value + '"';
	} else if (TypeHelper.isArray(value)) {
		return JSON.stringify(value.map(function map(item) {
			return serializeValue(item, seen, levels);
		}));
	} else if (TypeHelper.isPrimitive(value)) {
		return '' + value;
	} else if (TypeHelper.isDate(value)) {
		return value.toString();
	} else {
		// If the value has a .toJSON() property, use it to serialize the
		// object, but only if it isn't a promise. Promix promises have
		// an additional .toJSON() helper method, but it's async.
		if (
			    value
			&&  value !== Promix
			&&  TypeHelper.isFunction(value.toJSON)
			&& !TypeHelper.isPromise(value)
		) {
			value = value.toJSON();
		}

		return serializeObject(value, seen, levels);
	}
}

function serializeArguments(args) {
	args = standardizeArguments(args);

	var message = Array.prototype.slice.call(args).map(serializeValue).join(', ');

	return Utility_StringHelper.trimWhitespace(message);
}

module.exports = {
	serializeArguments,
	serializeValue
};
