// ********** BUG ********** //

function Bug(number) {
	this.number = number;
	this.instList = new Array();
	
	// Summary data
	this.loadedSummary = false;
	this.loadingSummary = false;
	this.status     = "";
	this.resolution = "";
	this.summary    = "";
	this.whiteboard = "";
	this.product    = "";
	this.component  = "";
	this.severity   = "";
	this.priority   = "";
	this.opsys      = "";
	this.platform   = "";
	this.version    = "";
	this.reporter   = "";
	this.assignee   = "";
	this.qa         = "";
	
	// Details data
	this.loadedDetails = false;
	this.loadingDetails = false;
	this.comments   = new Array();
}

Bug.prototype.loadSummary = function(highPriority) {
	loadBugSummary(this, highPriority || false);
}

Bug.prototype.loadDetails = function(highPriority) {
	loadBugDetails(this, highPriority || false);
}

// ********** GET BUG ********** //

var bugs = new Object();

function getBug(number) {
	if (!bugs[number]) {
		bugs[number] = new Bug(number);
	}
	return bugs[number];
}

// ********** BUG INST ********** //

function BugInst(number) {
	var bug = getBug(number);
	this.bug = bug;
	this.bug.instList.push(this);
}

BugInst.prototype.remove = function buginst_remove() {
	for (var i = 0; i < this.bug.instList.length; i++) {
		if (this.bug.instList[i] == this) {
			this.bug.instList.splice(i, 1);
			this.bug = null;
			return;
		}
	}
}

// ********** USERS CACHE ********** //

var users = new Object();

function getUser(email, name) {
	if (!users[email]) {
		users[email] = new User(email);
	}
	if (typeof name == "string") {
		users[email].name = name;
	}
	return users[email];
}

function User(email) {
	this.email = email;
	this.name = email;
}

// ********** BUG SUMMARY DISPLAY ********** //

function insertBugSummaryLine(container, type, number) {
	debugLog("insertBugSummaryLine(" + number + ")");
	var inst = new BugInst(number);
	
	inst.bugBox = document.createElement("div");
	inst.bugBox.className = type + "-bug-box bug-box";
	
	inst.titleBox = document.createElement("a");
	inst.titleBox.className = "bug-number";
	inst.titleBox.href = "javascript:showBugInfo(" + number + ")";
	inst.titleBox.appendChild(document.createTextNode(number));
	
	inst.statusBox = document.createElement("span");
	inst.statusBox.className = "bug-status";
	inst.statusBox.appendChild(document.createTextNode("\u00A0"));
	
	inst.patchesBox = document.createElement("span");
	inst.patchesBox.className = "bug-patches";
	inst.patchesBox.appendChild(document.createTextNode("\u00A0"));
	
	inst.assigneeBox = document.createElement("span");
	inst.assigneeBox.className = "bug-assignee";
	inst.assigneeBox.appendChild(document.createTextNode("\u00A0"));
	
	inst.summaryBox = document.createElement("span");
	inst.summaryBox.className = "bug-data";
	inst.summaryBox.appendChild(document.createTextNode("Loading..."));
	
	inst.bugBox.appendChild(inst.titleBox);
	inst.bugBox.appendChild(inst.statusBox);
	inst.bugBox.appendChild(inst.patchesBox);
	inst.bugBox.appendChild(inst.assigneeBox);
	inst.bugBox.appendChild(inst.summaryBox);
	
	container.appendChild(inst.bugBox);
	
	showBugSummary(inst);
	showBugDetails(inst);
	
	return inst;
}

function showBugSummary(inst) {
	debugLog("showBugSummary(" + inst.bug.number + ")");
	var bug = inst.bug;
	
	if (!bug.loadedSummary) {
		bug.loadSummary();
		return;
	}
	
	//if (inst.titleBox) {
	//	inst.titleBox.href = "javascript:showBugInfo(" + bug.number + ")";
	//}
	
	if (inst.bugBox) {
		inst.bugBox.className = inst.bugBox.className.replace(/ bug-(status|resolution)-\w+/g, "");
		inst.bugBox.className += " bug-status-" + bug.status.toLowerCase() +
				" bug-resolution-" + bug.resolution.toLowerCase();
	}
	
	//if (inst.titleBox) {
	//	showTextIn(inst.titleBox, bug.number);
	//}
	
	if (inst.statusBox) {
		if (bug.resolution) {
			if (bug.resolution == "WORKSFORME") {
				showTextIn(inst.statusBox, "WFM");
			} else if (bug.resolution == "DUPLICATE") {
				showTextIn(inst.statusBox, "DUP");
			} else if (bug.resolution == "INVALID") {
				showTextIn(inst.statusBox, "INV");
			} else if (bug.resolution == "WONTFIX") {
				showTextIn(inst.statusBox, "WONT");
			} else {
				showTextIn(inst.statusBox, bug.resolution);
			}
			inst.statusBox.title = bug.status + " / " + bug.resolution;
		} else {
			showTextIn(inst.statusBox, bug.status.substr(0, 4));
			inst.statusBox.title = bug.status;
		}
	}
	
	if (inst.summaryBox) {
		showTextIn(inst.summaryBox, bug.summary || "<no data found>");
		inst.summaryBox.title = bug.summary;
	}
	
	if (inst.whiteboardBox) {
		showTextIn(inst.whiteboardBox, bug.whiteboard);
		inst.whiteboardBox.title = bug.whiteboard;
	}
	
	if (inst.productBox) {
		showTextIn(inst.productBox, bug.product);
		inst.productBox.title = bug.product;
	}
	
	if (inst.componentBox) {
		showTextIn(inst.componentBox, bug.component);
		inst.componentBox.title = bug.component;
	}
	
	if (inst.severityBox) {
		showTextIn(inst.severityBox, bug.severity);
		inst.severityBox.title = bug.severity;
	}
	
	if (inst.priorityBox) {
		showTextIn(inst.priorityBox, bug.priority);
		inst.priorityBox.title = bug.priority;
	}
	
	if (inst.opsysBox) {
		showTextIn(inst.opsysBox, bug.opsys);
		inst.opsysBox.title = bug.opsys;
	}
	
	if (inst.platformBox) {
		showTextIn(inst.platformBox, bug.platform);
		inst.platformBox.title = bug.platform;
	}
	
	if (inst.versionBox) {
		showTextIn(inst.versionBox, bug.version);
		inst.versionBox.title = bug.version;
	}
	
	if (inst.reporterBox) {
		showTextIn(inst.reporterBox, bug.reporter.name);
		inst.reporterBox.title = bug.reporter.name;
	}
	
	if (inst.assigneeBox) {
		showTextIn(inst.assigneeBox, bug.assignee.name);
		inst.assigneeBox.title = bug.assignee.name;
	}
	
	if (inst.qaBox) {
		showTextIn(inst.qaBox, bug.qa.name);
		inst.qaBox.title = bug.qa.name;
	}
	
	showBugDetails(inst);
}

// ********** BUG DETAILS DISPLAY ********** //

function insertBugComments(container, number) {
	debugLog("insertBugComments(" + number + ")");
	var inst = new BugInst(number);
	inst.commentsContainer = container;
	showBugDetails(inst);
}

function showBugDetails(inst) {
	debugLog("showBugDetails(" + inst.bug.number + ")");
	var bug = inst.bug;
	
	if (!bug.loadedDetails) {
		bug.loadDetails();
		return;
	}
	
	if (inst.patchesBox) {
		if (bug.patchStatus) {
			showTextIn(inst.patchesBox, [bug.patchStatus.reviewQ, bug.patchStatus.reviewP, bug.patchStatus.reviewM].join("/"));
			inst.patchesBox.title = "Non-obsolete patches with: review? / review+ / review-";
		} else {
			showTextIn(inst.patchesBox, "");
			inst.patchesBox.title = "";
		}
	}
	
	if (inst.commentsContainer) {
		while (inst.commentsContainer.lastChild) {
			inst.commentsContainer.removeChild(inst.commentsContainer.lastChild);
		}
		
		for (var i = 0; i < bug.comments.length; i++) {
			var comBox = document.createElement("div");
			comBox.className = "comment";
			
			var dateBox = document.createElement("div");
			dateBox.className = "date";
			dateBox.appendChild(document.createTextNode(bug.comments[i].date));
			comBox.appendChild(dateBox);
			
			var authorBox = document.createElement("div");
			authorBox.className = "author";
			authorBox.appendChild(document.createTextNode(bug.comments[i].author.name));
			comBox.appendChild(authorBox);
			
			for (var j = 0; j < bug.comments[i].lines.length; j++) {
				if (j > 0) {
					comBox.appendChild(document.createElement("br"));
				}
				comBox.appendChild(document.createTextNode(bug.comments[i].lines[j]));
			}
			inst.commentsContainer.appendChild(comBox);
		}
	}
}

// ********** BUG SUMMARY CODE ********** //

var bugSummaryQueue = new Array();
var bugSummaryQueueTimer = 0;

function loadBugSummary(bug, highPriority) {
	if (bug.loadingSummary) {
		return;
	}
	bug.loadingSummary = true;
	if (highPriority) {
		bugSummaryQueue.unshift(bug.number);
	} else {
		bugSummaryQueue.push(bug.number);
	}
	if (bugSummaryQueueTimer) {
		clearTimeout(bugSummaryQueueTimer);
	}
	bugSummaryQueueTimer = setTimeout("bugSummaryQueueLoader()", 5000);
}

function bugSummaryQueueLoader() {
	bugSummaryQueueTimer = 0;
	
	var bugs = new Array();
	while (bugs.length < 20) {
		bugs.push(bugSummaryQueue.shift());
	}
	
	var url = "https://bugzilla.mozilla.org/buglist.cgi?format=rdf&columnlist=all&bug_id=" + bugs.join(",");
	
	if (bugSummaryQueue.length > 0) {
		bugSummaryQueueTimer = setTimeout("bugSummaryQueueLoader()", 5000);
	}
	
	var summaryRequest = new WebRequest("GET", url, bugSummaryQueueOnload, bugSummaryQueueOnerror);
	summaryRequest.priority = 50;
	summaryRequest.load();
}

function bugSummaryQueueOnload(event, request, data) {
	debugLog("bugSummaryQueueOnload()");
	data = data.replace(/[\r\n\s]+/g, " ");
	
	function processItem(key, tag, userKey) {
		if (typeof userKey == "string") {
			ary = bugItem.match(new RegExp("<bz:" + tag + ">(.*?)<\\/bz:" + tag + ">", ""));
			if (ary) {
				var email = parseHTMLEntites(ary[1]);
			}
			ary = bugItem.match(new RegExp("<bz:" + tag + userKey + ">(.*?)<\\/bz:" + tag + userKey + ">", ""));
			if (ary) {
				var name = parseHTMLEntites(ary[1]);
			}
			bug[key] = getUser(email, name);
			
		} else {
			ary = bugItem.match(new RegExp("<bz:" + tag + ">(.*?)<\\/bz:" + tag + ">", ""));
			if (ary)
				bug[key] = parseHTMLEntites(ary[1]);
			else
				bug[key] = "";
		}
	}
	
	var bugMatch, id;
	while ((bugMatch = data.match(/^(.*?)<\/bz:bug>/))) {
		var bugItem = bugMatch[1];
		data = data.substr(bugMatch[0].length);
		
		var ary = bugItem.match(/<bz:id[^>]*>(.*?)<\/bz:id>/);
		id = 1 * ary[1];
		
		var bug = getBug(id);
		processItem("status",     "bug_status");
		processItem("resolution", "resolution");
		processItem("summary",    "short_short_desc");
		processItem("whiteboard", "status_whiteboard");
		processItem("product",    "product");
		processItem("component",  "component");
		processItem("severity",   "bug_severity");
		processItem("priority",   "priority");
		processItem("opsys",      "op_sys");
		processItem("platform",   "rep_platform");
		processItem("version",    "version");
		processItem("reporter",   "reporter", "_realname");
		processItem("assignee",   "assigned_to", "_realname");
		processItem("qa",         "qa_contact", "_realname");
		bug.loadedSummary = true;
		bug.loadingSummary = false;
		
		for (var i = 0; i < bug.instList.length; i++) {
			showBugSummary(bug.instList[i]);
		}
	}
}

function bugSummaryQueueOnerror(event, request, exception) {
	var msg = "";
	if (typeof exception != "undefined") {
		msg = "Exception loading data: " + exception;
	} else {
		try {
			msg = "Error loading data for this bug: " + request.req.status + " (" + request.req.statusText + ")";
		} catch(ex) {
			msg = "Error loading data for this bug.";
		}
	}
	debugLog("bugSummaryQueueOnerror: " + msg);
	showMessageBox(msg);
}

// ********** BUG DETAILS CODE ********** //

var bugDetailsQueue = new Array();
var bugDetailsQueueTimer = 0;

function loadBugDetails(bug, highPriority) {
	if (bug.loadingDetails) {
		return;
	}
	bug.loadingDetails = true;
	if (highPriority) {
		bugDetailsQueue.unshift(bug.number);
	} else {
		bugDetailsQueue.push(bug.number);
	}
	if (bugDetailsQueueTimer == 0) {
		bugDetailsQueueTimer = setTimeout("bugDetailsQueueLoader()", 1000);
	}
}

function bugDetailsQueueLoader() {
	bugDetailsQueueTimer = 0;
	
	var bugNumber = bugDetailsQueue.shift();
	var url = "https://bugzilla.mozilla.org/show_bug.cgi?ctype=xml&id=" + bugNumber;
	
	var bugReq = new WebRequest("GET", url, bugDetailsQueueOnload, bugDetailsQueueOnerror);
	bugReq.priority = -50;
	bugReq.bugNumber = bugNumber;
	bugReq.load();
	
	if (bugDetailsQueue.length > 0) {
		bugDetailsQueueTimer = setTimeout("bugDetailsQueueLoader()", 1000);
	}
}

function bugDetailsQueueOnload(event, request, data) {
	debugLog("bugDetailsQueueOnload(" + request.bugNumber + ")");
	
	var bug = getBug(request.bugNumber);
	bug.patchStatus = { total: 0, reviewQ: 0, reviewP: 0, reviewM: 0 };
	
	var attachData = data;
	attachData = attachData.replace(/[\r\n\s]+/g, " ");
	
	var attachMatch;
	while ((attachMatch = attachData.match(/^(.*?)<\/attachment>/))) {
		var attachItem = attachMatch[1];
		attachData = attachData.substr(attachMatch[0].length);
		
		var patch;
		if ((patch = attachItem.match(/(<attachment.*ispatch="1"[^>]*>)(.*)/))) {
			
			if (/isobsolete="1"/.test(patch[1])) {
				continue;
			}
			bug.patchStatus.total++;
			
			var flagData = patch[2];
			var flagMatch;
			while ((flagMatch = flagData.match(/^.*?(<flag[^>]*>)/))) {
				var flagItem = flagMatch[1];
				flagData = flagData.substr(flagMatch[0].length);
				
				if (/name="review"/.test(flagItem)) {
					if (/status="\?"/.test(flagItem)) {
						bug.patchStatus.reviewQ++;
					} else if (/status="\+"/.test(flagItem)) {
						bug.patchStatus.reviewP++;
					} else if (/status="-"/.test(flagItem)) {
						bug.patchStatus.reviewM++;
					}
				}
			}
		}
	}
	
	var commentData = data;
	commentData = commentData.replace(/\r\n?/g, "<br>");
	bug.comments = new Array();
	
	var commentMatch;
	while ((commentMatch = commentData.match(/^(.*?)<\/long_desc>/))) {
		var commentItem = commentMatch[1];
		commentData = commentData.substr(commentMatch[0].length);
		
		var author = commentItem.match(/<who[^>]*>(.*?)<\/who>/)[1];
		var when = commentItem.match(/<bug_when[^>]*>(.*?)<\/bug_when>/)[1];
		var ary = commentItem.match(/<thetext[^>]*>(.*?)<\/thetext>/);
		var commentLines = ary[1].replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&").replace(/  /g, "\u00A0 ").split(/<br>/);
		
		bug.comments.push({ author: getUser(author), date: when, lines: commentLines });
	}
	bug.loadedDetails = true;
	bug.loadingDetails = false;
	
	for (var i = 0; i < bug.instList.length; i++) {
		showBugDetails(bug.instList[i]);
	}
}

function bugDetailsQueueOnerror(event, request, exception) {
	debugLog("bugDetailsQueueOnerror(" + request.bugNumber + ")");
	var msg = "";
	if (typeof exception != "undefined") {
		msg = "Exception loading data: " + exception;
	} else {
		msg = "Error loading details data for this bug: " + request.req.status + " (" + request.req.statusText + ")";
	}
	
	showMessageBox(msg);
}

