var benchmark = function() {
	var self = arguments.callee;
	self.run.apply(self, arguments);
};

benchmark.config = {
	Logger: 'layer',
	Debug: false,
	IgnoreError: false
};

benchmark.logger = function(obj){for (var name in obj) this[name] = obj[name]};

benchmark.logger.prototype = {
	debug: function() {
		if (benchmark.config.Debug) this._debug.apply(this, arguments);
	},
	log: function() {
		this._log.apply(this, arguments);
	},
	error: function() {
		if (!benchmark.config.IgnoreError) this._error.apply(this, arguments);
	}
};

benchmark.logger.firebug = new benchmark.logger({
	_debug: function() {
		if(console && console.debug) console.debug.apply(console, arguments);
	},
	_log: function() {
		if(console && console.log) console.log.apply(console, arguments);
	},
	_error: function() {
		for (var i = 0, length = arguments.length; i < length; i ++) arguments[i] = '!! ' + arguments[i];
		if(console && console.log) console.log.apply(console, arguments);
	},
	clear: function() {
		clear();
	}
});

benchmark.logger.layer = new benchmark.logger({
	init: function() {
		var body    = document.getElementsByTagName('body')[0];
		if(!body) return;
		var div     = this.div = document.createElement('div');
		var style   = div.style;

		style.position    = 'absolute';
		style.top         = '0px';
		style.left        = '0px';
		style.fontWeight  = 'bold';

		body.appendChild(div);
		this.queue = [];
		this.inited = true;
	},
	_print: function(text, color) {
		var element  = document.createElement('div');
		element.style.color = color;
		var textNode = document.createTextNode(text);
		element.appendChild(textNode);
		this.div.appendChild(element);
		this.queue.push(element);
	},
	_debug: function(text) {
		if(!this.inited) this.init();
		if(this.inited) this._print(text, 'gray');
	},
	_log: function(text) {
		if(!this.inited) this.init();
		if(this.inited) this._print(text, 'black');
	},
	_error: function(text) {
		if(!this.inited) this.init();
		if(this.inited) this._print(text, 'red');	
	},
	clear: function() {
		var q = this.queue; while (q.length) this.div.removeChild(q.pop());
	}
});

benchmark.clear = function() {
	if (this.logger && this.logger.clear) this.logger.clear();
};

benchmark.at = function(logger) {
	var self = this;
	return function(obj, cb) {
		var oldLogger = self.config.Logger;
		self.config.Logger = logger;
		self(obj, function() {
			if(cb) cb.apply(null,arguments);
			self.config.Logger = oldLogger;
		});
	}
};

benchmark.dummy = function() {};

benchmark.run = function(obj, cb) {
	var logger = this.logger[this.config.Logger];

	if (this.busy) {
		logger.error('benchmark engine is busy ...');
		return;
	}
	else this.busy = true;

	var self = this;
	var results = [];

	var queue = [

		function() {
			logger.log('preparing ...');
		},
		function() {
			for (var i = 0; i < 100000; i ++) {
				var dummy = 'dummy';
				document.getElementsByTagName('html');
				dummy += 'dummy';
				dummy = 1;
				dummy = dummy++ + 1;
			}
		},

		function() {
			if (self.forCost) return;
			var times = 1;
			while (true) {
				var start = new Date().getTime();
				for (var i = 0; i < times; i ++) ;
				var stop = new Date().getTime();
				if ((stop - start) > 50) break;
				else times *= 10;
			}
			logger.debug('for cost : ' + (self.forCost = (stop - start)/ times) + '[ms]');
		},

		function() {
			if (self.timeCost) return;
			var times = 1;
			while (true) {
				var start = new Date().getTime();
				for (var i = 0; i < times; i ++) new Date().getTime();
				var stop = new Date().getTime();
				if ((stop - start) > 50) break;
				else times *= 10;
			}
			logger.debug('time cost : ' + (self.timeCost = (stop - start) / times) + '[ms]');
		},
/*
 *		function() {
 *			if (self.propCost) return;
 *			var times = 1;
 *			while (true) {
 *				var start = new Date().getTime();
 *				for (var i = 0; i < times; i ++) self._dummy ++;
 *				var stop = new Date().getTime();
 *				if ((stop - start) > 50) break;
 *				else times *= 10;
 *			}
 *			logger.debug('prop set : ' + (self.propCost = (stop - start) / times) + '[ms]');
 *		},
 */
		function() {
			if (self.funcCost) return;
			var dummy = self.dummy;
			var times = 1;
			while (true) {
				var start = new Date().getTime();
				for (var i = 0; i < times; i ++) dummy();
				var stop = new Date().getTime();
				if ((stop - start) > 50) break;
				else times *= 10;
			}
			logger.debug('call cost : ' + (self.funcCost = (stop - start) / times) + '[ms]');
		},
		function() {
			logger.log('let\'s go!');
		},
		function() {
			var o = obj;
			var queue = [];
			for(var name in o) {
				queue.push([name, o[name]]);
			}
			(function(queue) {
				var f = arguments.callee;
				var id = setTimeout(function() {
					var set = queue.shift();
					var name = set[0];
					var func = set[1];
					var result;
					var result = name.match(/(.*)x(\d)/);
					var mag;
					if(result) {
						name = result[1];
						mag = eval(result[2]);
					}

					logger.log('.');
					logger.log('*** ' + name + ' ***');

					var times = 1;
					while (true) {
						var start = new Date().getTime();
						for (var i = 0; i < times; i ++) func();
						var stop = new Date().getTime();
						if ((stop - start) > 100) break;
						else times *= 10;
					}
					var t  = (stop - start) / times;
					var fn = self.funcCost;
					var fr = self.forCost;
					var tm = self.timeCost * 2 / times;
					var r;
					logger.debug('total' + t + '[ms] - (call' + fn + '[ms] + time' + tm + '[ms] + for' + fr + '[ms])');
					logger.log('result : ' + (t - (fn + fr + tm)) + '[ms]');

					results.push(t- (fn + fr + tm));
					if(queue.length) f(queue);
					else {
						logger.log('.');
						logger.log('finish!');
					}
				}, 10);
			})(queue);
		},
		function() {
			if(cb) cb(results);
			delete self.busy;
		}
	];
	(function() {
		var f = arguments.callee;
		var id = setTimeout(function() {
			queue.shift()();
			if(queue.length) f();
		}, 10);
	})();
};

