define(['backbone', 'underscore'], function(Backbone, _) {
	'use strict';

	function applyConstructor(Constructor, args) {
		return new (Function.prototype.bind.apply(
			Constructor,
			[null].concat(args)
		))();
	}

	var singletons = {};

	return function(Constructor) {
		var singleton,
			args = Array.prototype.slice.call(arguments, 1);

		if (Constructor && _.isFunction(Constructor)) {
			var id = Constructor.singleton || Constructor.prototype.singleton;
			switch (true) {
				case typeof id === 'boolean':
					singleton = applyConstructor(Constructor, args);
					Constructor.singleton = id = 's' + (Math.random() * 10000000 | 0);

					singletons[id] = singleton;
					return singleton;

				case typeof id === 'string':
					singleton = singletons[id];
					if (_.isFunction(singleton.set)) {
						singleton.set(args[0]);
					}
					return singleton;

				default:
					return applyConstructor(Constructor, args);
			}
		} else {
			return {};
		}
	};
});
