"use strict";

var partial  = require("es5-ext/function/#/partial")
  , forEach  = require("es5-ext/object/for-each")
  , pad      = require("es5-ext/string/#/pad")
  , deferred = require("./deferred");

var resolved, rStats, unresolved, uStats, profile;

exports.profile = function () {
	resolved = 0;
	rStats = {};
	unresolved = 0;
	uStats = {};
	deferred._profile = profile;
};

profile = function (isResolved) {
	var stack, data;

	if (isResolved) {
		++resolved;
		data = rStats;
	} else {
		++unresolved;
		data = uStats;
	}

	stack = new Error().stack;
	if (
		!stack.split("\n").slice(3).some(function (line) {
			if (
				line.search(/[/\\]deferred[/\\]/) === -1 &&
				line.search(/[/\\]es5-ext[/\\]/) === -1 &&
				line.indexOf(" (native)") === -1
			) {
				line = line.replace(/\n/g, "\\n").trim();
				if (!data[line]) {
					data[line] = { count: 0 };
				}
				++data[line].count;
				return true;
			}
			return false;
		})
	) {
		if (!data.unknown) {
			data.unknown = { count: 0, stack: stack };
		}
		++data.unknown.count;
	}
};

exports.profileEnd = function () {
	var total, lpad, log = "";

	if (!deferred._profile) {
		throw new Error("Deferred profiler was not initialized");
	}
	delete deferred._profile;

	log += "------------------------------------------------------------\n";
	log += "Deferred usage statistics:\n\n";

	total = String(resolved + unresolved);
	lpad = partial.call(pad, " ", total.length);
	log += total + " Total promises initialized\n";
	log += lpad.call(unresolved) + " Initialized as Unresolved\n";
	log += lpad.call(resolved) + " Initialized as Resolved\n";

	if (unresolved) {
		log += "\nUnresolved promises were initialized at:\n";
		forEach(
			uStats,
			function (data, name) { log += lpad.call(data.count) + " " + name + "\n"; },
			null,
			function (data1, data2) { return this[data2].count - this[data1].count; }
		);
	}

	if (resolved) {
		log += "\nResolved promises were initialized at:\n";
		forEach(
			rStats,
			function (data, name) { log += lpad.call(data.count) + " " + name + "\n"; },
			null,
			function (data1, data2) { return this[data2].count - this[data1].count; }
		);
	}
	log += "------------------------------------------------------------\n";

	return {
		log: log,
		resolved: { count: resolved, stats: rStats },
		unresolved: { count: unresolved, stats: uStats }
	};
};
