var req;
var watchlistURL = "/inline-add-watchlist.php";
var watchIconOff = "/info_images/checkmark.png";
var watchIconOn = "http://www.macupdate.com/images/load.gif";

var DEBUG = false;

/// this assumes that both actions (add, del) are directed to the same destination...
/// if this is not the case (if they need to be processed on separate pages),
/// this should be additionally parametrized to allow addProd...() and removeProd...()
/// to pass destination arguments individually.
function old_requestWatchlistChange(mode, pid) {
	req = false;
	try {
		req = new XMLHttpRequest();
	} catch (e) {
		req = false;
	}
	
	if (req) { // we were able to instantiate
//		alert("Opening connection and sending request to " + watchlistURL + "\nWith params: pid=" + pid);
//		var arg = "pid="+pid;
		req.onreadystatechange = processRequest; // assign response handler (processRequest should be function object)
/* 		req.open("GET", watchlistURL+"?pid="+pid, true); // establish connection */
/* 		req.send(null); // should be send(null) if no arguments and open not of type 'POST' */
		req.open('POST', watchlistURL, true);
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		req.send('pid='+pid+'&mode='+mode);
		
		return true; // could dispatch
	} else {
		return false; // could not dispatch
	}
}

function errorsAsString(errs) {
	var errMesg = "";
	for (var e=0; e<errs.length; e++) {
		var mesg = errs[e].getElementsByTagName("mesg");
		for (var m=0; m<mesg.length; m++) {
			errMesg += mesg[m].firstChild.nodeValue;
		}
	}
	return errMesg;
}

function requestListChange(target, args, callback) {
	req = false;
	try {
		req = new XMLHttpRequest();
	} catch (e) {
		req = false;
	}
	
	if (req) { // we were able to instantiate
//		alert("Opening connection and sending request to " + watchlistURL + "\nWith params: pid=" + pid);
//		var arg = "pid="+pid;
		req.onreadystatechange = callback; // assign response handler (processRequest should be function object)
/* 		req.open("GET", watchlistURL+"?pid="+pid, true); // establish connection */
/* 		req.send(null); // should be send(null) if no arguments and open not of type 'POST' */
		req.open('POST', target, true);
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//		req.send('pid='+pid+'&mode='+mode);
		req.send(args);
		
//		alert("Requesting list change to: " + target + "\nWith args: " + args);
		
		return true; // could dispatch
	} else {
		return false; // could not dispatch
	}
}

function requestWatchlistChange(mode, pid) {
	var args = 'pid='+pid+'&mode='+mode+'&list=watch';
	return requestListChange(watchlistURL, args, processRequest);
}

function requestWishlistChange(mode, pid) {
	var args = 'pid='+pid+'&mode='+mode+'&list=wish';
	return requestListChange(watchlistURL, args, processWishRequest);
}

/// for both of these (add/del behavior), it may be better to expect
/// requestWatchlistChange() to return a useful value (e.g., ref to req
/// instance) for error-handling
function submitAddWatchlistRequest(pid) {
	submitWatchlistRequest(pid, "add", "Adding...");
}

function submitRemoveWatchlistRequest(pid) {
	submitWatchlistRequest(pid, "del", "Removing...");
}

	/// general watchlist-action request. pid should be product.id, mode in {add, del}, statText what to update link text with 
function submitWatchlistRequest(pid, mode, statText) {
	var ret = requestWatchlistChange(mode, pid);
	if (ret) {
		// update status (eg, to 'adding...')
		changeLink(	document.getElementById("watchlistLink"),
						'', // remove link action
						statText);
//		turnIconOn(document.getElementById("watchIcon"));
	} else {
		alert("Could not submit request");
	}
}

function addProductToWatchlist(pid, login) {
	if (login) { // submit addition request
		submitAddWatchlistRequest(pid);
	} else {
		placeDialog(createDialogListLogin(pid, "lightbox"), getOverlay());
	}
}

function removeProductFromWatchlist(pid) {	
		// is this sensitive to login? (technically, we only know that the item should be removed if the 
		// user is already logged in...)
	submitRemoveWatchlistRequest(pid);
// 	var ret = requestWatchlistChange("del", pid);
// 	if (ret) {
// 		// update status to 'removing...'
// 		changeLink(	document.getElementById("watchlistLink"),
// 						'', // remove link action
// 						'Removing...');
// 		turnIconOn(document.getElementById("watchIcon"));
// 	} // else gracefully handle error...
}

function createDialogListLogin(pid, dlg) {
	var formIdent = "inlineListLogin";
	var theDialog = createDlgComp_contain(dlg);
		/// create and display login/email form
	var theContents = document.createDocumentFragment();
	var theForm = document.createElement("form");
	theForm.id = formIdent;
//	theForm.onsubmit = function () { submitVoteRequest(pid, theForm); return false; }
	var theLoginEmail = buildLabeledField(buildTextField("loginemail"), "MU account email:");
	var theLoginPass = buildLabeledField(buildField("loginpass", "password"), "MU account password:");
	var theLoginContain = createFieldset("MU Login");
	theLoginContain.appendChild(theLoginEmail);
	theLoginContain.appendChild(theLoginPass);

	var theAlternate = document.createElement("div");
	theAlternate.className = "note";
	theAlternate.appendChild(document.createTextNode("Please login to your account, or create a new account (paid members have unlimited lists; others are limited to seven items)"));

	var theNewEmail = buildLabeledField(buildTextField("newemail"), "New account email:");
	var theNewPass = buildLabeledField(buildField("newpass", "password"), "New account password:");
	var theNewConfirm = buildLabeledField(buildField("newconfirm", "password"), "Confirm password:");
	var theNewContain = createFieldset("Create New Account");
	theNewContain.appendChild(theNewEmail);
	theNewContain.appendChild(theNewPass);
	theNewContain.appendChild(theNewConfirm);
	
		/// we need to handle actually logging in / creating new account in addition to the watchlist action
	var theSubmitButton = createDlgComp_button("Submit", function() {
		if (processLogin(theForm)) {
			submitAddWatchlistRequest(pid); 
			cancelDialog(theDialog);
		}
	});
	theSubmitButton.className = "submitButton";
	var theCancelButton = createDlgComp_button("Cancel", function() {cancelDialog(theDialog);});
	theCancelButton.className = "cancelButton";

	theForm.appendChild(theAlternate);
	theForm.appendChild(theLoginContain);
	theForm.appendChild(theNewContain);
	theForm.appendChild(theSubmitButton);
	theForm.appendChild(theCancelButton);
	theContents.appendChild(theForm); // this can be cleaned up so we don't need the containing DIV (but get working first)
	theDialog.appendChild(createDlgComp_mat(theContents, null)); // really, we probably want spiffy lightbox effect here
	return theDialog;
}

function processLogin(fm) {
		// cache control values
	var controls = new Array();
	if (fm && fm.elements) {
		for (var elt=0; elt<fm.elements.length; elt++) {
			controls[fm.elements[elt].name] = fm.elements[elt].value;
		}
	}
	var isNewAccount = false;
	var result = null;
	var couldLogin = false;
	if (controls["loginemail"] != "") {
		if (controls["loginpass"] != "") {
			result = requestExistingLogin(controls["loginemail"], controls["loginpass"]);
		} else {
			alert("Could not submit login: please provide a password");
		}
	} else if (controls["newemail"] != "") {
		if (controls["newpass"] != "") {
			if (controls["newpass"] == controls["newconfirm"]) {
				result = requestNewLogin(controls["newemail"], controls["newpass"]);
			} else {
				alert("Password does not match confirmation");
			}
		} else {
			alert("You must provide a password for this new account");
		}
	} else {
		alert("You must provide an email address, or create a new account");
	}
	// parse response (if false, display error--existing account, etc.--and return false to prevent other actions from continuing)
	if (result && result.status == 200) {
		var errs = result.responseXML.getElementsByTagName("err");
		if (errs.length > 0) {
			// there were errors in processing
			alert("Error Response: " + errorsAsString(errs));
		} else {
			// success?
			couldLogin = true;
		}
	} else {
//		console.log("Response from server not good: %o", result);
	}
//	console.log("Return from processLogin: %o", couldLogin);
	return couldLogin;
}

function requestNewLogin(email, pass) {
	return requestLogin("new", email, pass);
}

function requestExistingLogin(email, pass) {
	return requestLogin("exist", email, pass);
}

function requestLogin(mode, email, pass) {
	req = false;
//	var resp = null;
	try {
		req = new XMLHttpRequest();
	} catch (e) {
		req = false;
	}
	if (req) { // we were able to instantiate
		var args = ["mode="+mode,
						"email="+email,
						"pass="+pass];
		req.open('POST', "/inline-login.php", false); // synchronous, for contingent continuation
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		req.send(args.join('&'));
//		resp = req; // can just return req directly, at least in this context
	}
	return req;
}


	//// NB: wishlist code is now out of date (deprecated?)
function addProductToWishlist(pid) {
	var ret = requestWishlistChange("add", pid);
	if (ret) {
		// update status to 'adding...'
		changeLink(	document.getElementById("wishlistLink"),
						'', // remove link action
						'Adding...');
		turnIconOn(document.getElementById("wishIcon"));
	} // else handle error somehow
}

function removeProductFromWishlist(pid) {
	var ret = requestWishlistChange("del", pid);
	if (ret) {
		// update status to 'removing...'
		changeLink(	document.getElementById("wishlistLink"),
						'', // remove link action
						'Removing...');
		turnIconOn(document.getElementById("wishIcon"));
	} // else gracefully handle error...
}

/// we may want this to be updateForRequest(requestObject, statusElt); then
/// we can define processWatchlistRequest and processWishlistRequest:
/// procWatchReq() { updateForRequest(req, "idOfWatchlistLink"); } (and sim.)
function processRequest() {
	if (req.readyState == 4) { // readyState == 4 -> complete
		// this means the transaction has been completed (note, this doesn't mean that the response was OK... only that it's done)
		if (req.status == 200) { // response from the server was good
			// do stuff
			var errs = req.responseXML.getElementsByTagName("err");
			if (errs.length > 0) {
				// there were errors in processing
				alert("Response: " + errorsAsString(errs));
				if (req.responseXML.documentElement.hasAttribute("mode")) {
					var theAction = req.responseXML.documentElement.getAttribute("mode");
					if (theAction == "add") {
						changeLink(document.getElementById("watchlistLink"), "", "Not Added");
					} else if (theAction == "del") {
						changeLink(document.getElementById("watchlistLink"), "", "Not Removed");
					} else {
						changeLink(document.getElementById("watchlistLink"), "", "Could Not Change");
					}
				}
			} else {
				var stat = req.responseXML.getElementsByTagName("succ")[0]; // should only be a single SUCC elt.
				if (stat) { // there was a success node
					stat.normalize();
					// change status to reflect added item
//					changeStatus("watchlistLink", "Remove From My Watch List");
					var theAction = stat.getElementsByTagName("act")[0];
					var theId = stat.getElementsByTagName("pid")[0];
					if (theAction && theId) { // maybe more extensive error-checking here
						var theActValue = theAction.firstChild.nodeValue; // should be text of action node
						var theProdId = theId.firstChild.nodeValue; // should be product id number
						if (theActValue == 'add') {
							changeLinkToDelete(document.getElementById("watchlistLink"), theProdId);
						} else if (theActValue == 'del') {
							changeLinkToAdd(document.getElementById("watchlistLink"), theProdId);
						}
					}
					if (DEBUG) {
						var theDebugMessage = stat.getElementsByTagName("mesg")[0];
						alert("Success: " + theDebugMessage.firstChild.nodeValue);
					}
				}
			}
			/// in particular, if change UI to reflect response
/* 			alert("Response: " + req.responseText); */
		} else {
			alert("Transaction completed, but response from the server was not OK (" + req.status + "): " + req.statusText);
		}
			/// not turning icon _on_ anymore, so nothing to turn off
//		turnIconOff(document.getElementById("watchIcon"));
	}
	// we may want to do things for other values of req.readyState (e.g., if connection is ongoing, update status)
}

function processWishRequest() {
	if (req.readyState == 4) { // readyState == 4 -> complete
		// this means the transaction has been completed (note, this doesn't mean that the response was OK... only that it's done)
		if (req.status == 200) { // response from the server was good
			// do stuff
			var errs = req.responseXML.getElementsByTagName("err");
			if (errs.length > 0) {
				// there were errors in processing
				alert("Response: " + errorsAsString(errs));
			} else {
				var stat = req.responseXML.getElementsByTagName("succ")[0]; // should only be a single SUCC elt.
				if (stat) { // there was a success node
					stat.normalize();
					// change status to reflect added item
//					changeStatus("watchlistLink", "Remove From My Watch List");
					var theAction = stat.getElementsByTagName("act")[0];
					var theId = stat.getElementsByTagName("pid")[0];
					if (theAction && theId) { // maybe more extensive error-checking here
						var theActValue = theAction.firstChild.nodeValue; // should be text of action node
						var theProdId = theId.firstChild.nodeValue; // should be product id number
						if (theActValue == 'add') {
							changeWishLinkToDelete(document.getElementById("wishlistLink"), theProdId);
						} else if (theActValue == 'del') {
							changeWishLinkToAdd(document.getElementById("wishlistLink"), theProdId);
						}
					}
					if (DEBUG) {
						var theDebugMessage = stat.getElementsByTagName("mesg")[0];
						alert("Success: " + theDebugMessage.firstChild.nodeValue);
					}
				}
			}
			/// in particular, if change UI to reflect response
/* 			alert("Response: " + req.responseText); */
		} else {
			alert("Transaction completed, but response from the server was not OK (" + req.status + "): " + req.statusText);
		}
		turnIconOff(document.getElementById("wishIcon"));
	}
	// we may want to do things for other values of req.readyState (e.g., if connection is ongoing, update status)
}

function changeContent(ident, mesg) {
	var elt = document.getElementById(ident);
	elt.innerHTML = mesg; // this is a bit impure, but faster than creating and manipulating text nodes
}

	/// is there an advantage one way or the other to setting nd.innerHTML vs 
	/// cleaning out the node children and replacing with a text node containing
	/// the string? (i.e., in what sense--if any--is the innerHTML attribute fragile
	/// or abusive?)
function changeLink(nd, ref, text) { // pid should be obtained from results
		/*
		Important note about error-checking type of nd: in a perfect world, we only 
		want to make this change if the DOM node we're given as an argument is an
		anchor (<a>); the best way to do this is to test the boolean value of
		'nd instanceof HTMLAnchorElement', which works exactly as expected in Moz.
		Unfortunately, Safari does not (currently?) expose the HTML*Element objects
		in this way (nor, it seems, all of the DOM objects generally). This makes 
		the instanceof comparison terminate evaluation (not good!). Can't find a 
		good general way of making comparison for the type of nd against some string
		(possibly I just haven't found the right way to access it, or the right thing
		to compare it to); in the meantime, a partial solution is to test nd.nodeType
		against Node.ELEMENT_NODE value, although this only determines whether or
		not nd is a valid DOM node--ultimately, this doesn't guarantee that the
		particular attributes of HTMLAnchorElement (particularly, nd.href) will 
		be available!
		
		Best thing might be to try{} the instanceof comparison, and fall back on 
		the nodeType comparison if necessary (possibly, in that case, set the 
		attributes more carefully). For the moment, this just uses the partial
		test (against nodeType) and assumes that it is only ever really given 
		valid anchor objects (or something _really_ wrong).
		*/
	if (nd.nodeType == Node.ELEMENT_NODE) { // this should always be true (better to handle at this level, or higher--e.g., in changeLinkToAdd()?)
//	if (nd instanceof HTMLAnchorElement) { // this should always be true (better to handle at this level, or higher--e.g., in changeLinkToAdd()?)
		nd.href = ref;
		nd.innerHTML = text;
	} else if (DEBUG) {
		alert("Cannot change link: argument was not a node (" + nd + ")");
	}
}

function changeLinkToAdd(nd, pid) {
	changeLink(nd, "javascript:addProductToWatchlist("+pid+");", "Add to Watch List");
//	changeContent("watchPreposition", "to"); // "Add _to_ ..."
}

function changeLinkToDelete(nd, pid) {
	changeLink(nd, "javascript:removeProductFromWatchlist("+pid+");", "Remove from Watch List");
//	changeContent("watchPreposition", "from"); // "Remove _from_ ..."
}

function changeWishLinkToAdd(nd, pid) {
	changeLink(nd, "javascript:addProductToWishlist("+pid+");", "Add to Wish List");
//	changeContent("wishPreposition", "to");
}

function changeWishLinkToDelete(nd, pid) {
	changeLink(nd, "javascript:removeProductFromWishlist("+pid+");", "Remove from Wish List");
//	changeContent("wishPreposition", "from");
}

function changeIcon(nd, srcval) {
	if (nd.nodeType == Node.ELEMENT_NODE) { // this suffers from similar problem to changeLink()
		nd.src = srcval;
	}
}

function turnIconOn(nd) {
	changeIcon(nd, watchIconOn);
}

function turnIconOff(nd) {
	changeIcon(nd, watchIconOff);
}

//////// originally in info.php ///////////
var voteFormContainerId = "inlineVoteContainer";

function removeVoteForm() {
	var theFormContainer = document.getElementById(voteFormContainerId);
	if (theFormContainer) {
		var theParent = theFormContainer.parentNode;
		theParent.removeChild(theFormContainer);
	}
}

function promoVoteLink() {
		// for the moment, it's enough just to return the DOM node
	return document.getElementById("promoVoteLink");
}

function voteProductForPromo(pid, login) {
	// submit via ajax (maybe just add this to listDynamism.js)
//	alert("Will submit vote for this product (" + pid + ")\nLogged in status: " + login);
	if (login) {		
// 		var ret = requestVoteProduct(pid); // still need to handle login behavior
// 		if (ret) {
// 			// update status to 'adding...'
// 			changeLink(	promoVoteLink(),
// 							'', // remove link action
// 							'Voting ('+pid+'/'+login+') ...');
// 			turnIconOn(document.getElementById("wishIcon"));
// 		} // else handle error somehow
		submitVoteRequest(pid);
	} else {
		placeDialog(createDialogContact(pid, "lightbox"), getOverlay());
	}
}

function createDialogContact(pid, dlg) {
	var formIdent = "inlineVoteForm";
	var theDialog = createDlgComp_contain(dlg);
	/// create and display login/email form
	var theContents = document.createDocumentFragment();
//	var theFormContain = document.createElement("div");
	var theForm = document.createElement("form");
	theForm.id = formIdent;
//	theForm.onsubmit = function () { submitVoteRequest(pid, theForm); return false; }
// 			var theLoginEmail = buildLabeledField(buildTextField("loginemail"), "MU account email:");
// 			var theLoginPass = buildLabeledField(buildField("loginpass", "password"), "MU account password:");
// 			var theLoginContain = createFieldset("MU Login");
// 			theLoginContain.appendChild(theLoginEmail);
// 			theLoginContain.appendChild(theLoginPass);

	var theAlternate = document.createElement("div");
	theAlternate.className = "note";
	theAlternate.appendChild(document.createTextNode("Please provide an email address where we can notify you when this product becomes a promo"));

	var theContactEmail = buildTextField("votemail");
// 	var theContactContain = createFieldset("Notification");
// 	theContactContain.appendChild(theContactEmail);
	
// 	var theSubmitButton = buildField("subm", "button");
// 	theSubmitButton.setAttribute("value", "vote");
	var theSubmitButton = createDlgComp_button("Submit", function() {submitVoteRequest(pid, theForm); cancelDialog(theDialog);});
	theSubmitButton.className = "submitButton";
	
	var theCancelButton = createDlgComp_button("Cancel", function() {cancelDialog(theDialog);});
	theCancelButton.className = "cancelButton";
// 	var theCancelButton = buildField("cancel", "button");
// 	theCancelButton.setAttribute("value", "cancel");
// 	theCancelButton.onclick = removeVoteForm;
	
// 			theForm.appendChild(theLoginContain);
	theForm.appendChild(theAlternate);
//	theForm.appendChild(theContactContain);
	theForm.appendChild(theContactEmail);
	theForm.appendChild(theSubmitButton);
	theForm.appendChild(theCancelButton);
// 	theFormContain.appendChild(theForm);
// 	theFormContain.id = voteFormContainerId;
// 	theForm.id = formIdent;
	theContents.appendChild(theForm); // this can be cleaned up so we don't need the containing DIV (but get working first)
	theDialog.appendChild(createDlgComp_mat(theContents, null)); // really, we probably want spiffy lightbox effect here
	return theDialog;
}

/// Action for "cancel" button.
/// cancelDialog() should include any behavior _in addition to_ actually
/// closing the dialog window (which is what removeDialog()) does.
function cancelDialog(dlgNode) {
//	cancelRequest();
	removeDialog(dlgNode);
	removeOverlay(getOverlay());
}

function replaceNodeWithText(nd, str) {
	try {
		var theParent = nd.parentNode;
		theParent.replaceChild(document.createTextNode(str), nd);
	} catch (m) {}
}

function replaceSiblingsWithText(nd, str) {
	try {
		var theParent = nd.parentNode;
		while (theParent.hasChildNodes()) {
			theParent.removeChild(theParent.firstChild);
		}
		theParent.appendChild(document.createTextNode(str));
	} catch (m) {}
}

function getOverlay() {
	var over = document.getElementById("overlay");
	if (!over) {
		over = document.createElement("div");
		over.id = "overlay";
		document.body.appendChild(over);
	}
	return over;
}

	/// handle updating link text, cursor behavior, etc.
function submitVoteRequest(pid, fm) {
	var ret = requestVoteProduct(pid, fm);
	if (ret) {
		// update status to 'adding...'
		// (need to change parent of promoVoteLink, now)
		changeLink(	promoVoteLink(),
						'', // remove link action
						'Voting');
//		turnIconOn(document.getElementById("wishIcon"));
	} // else handle error somehow
		// remove form
	if (fm != null) {
		// might just want to removeVoteForm() here, although technically there's no guarantee that fm is equivalent to the form contained in the vote-container ref in that function
		var theParent = fm.parentNode;
		theParent.removeChild(fm);
	}
}		

function requestVoteProduct(pid, fm) {
	var args = new Array();
	args.push("pid="+pid); // should check to make sure pid is valid
	if (fm != null) {
		var fields = fm.elements;
			// for the moment, simply accumulate field values into args
		for (f=0; f<fields.length; f++) {
			args.push(fields[f].getAttribute("name")+"="+encodeURIComponent(fields[f].value));
		}
		// process login/contact form values into args
//		alert("process login/contact form values into args");
	}
	return requestListChange("/promovote.php", args.join('&'), processVoteRequest);
}

function processVoteRequest() {
	if (req.readyState == 4) { // readyState == 4 -> complete
		if (req.status == 200) { // response from the server was good
			var errs = req.responseXML.getElementsByTagName("err");
			if (errs.length > 0) {
				alert("Response: " + errorsAsString(errs));
			} else { // all clear
				/// change this to reflect XML returned by voting behavior (promovote.php)
				var stat = req.responseXML.getElementsByTagName("succ")[0]; // should only be a single SUCC elt.
				if (stat) { // there was a success node
					stat.normalize();
					// change status to reflect added item
					var theAction = stat.getElementsByTagName("act")[0];
					var theId = stat.getElementsByTagName("pid")[0];
					if (theAction && theId) { // maybe more extensive error-checking here
						var theActValue = theAction.firstChild.nodeValue; // should be text of action node
						var theProdId = theId.firstChild.nodeValue; // should be product id number
// 						if (theActValue == 'add') {
// 							changeLinkToDelete(document.getElementById("watchlistLink"), theProdId);
// 						} else if (theActValue == 'del') {
// 							changeLinkToAdd(document.getElementById("watchlistLink"), theProdId);
// 						}
							// we actually want to _replace_ the link with text "thanks for voting"
//						changeLink(promoVoteLink(), '', "Thanks for voting");
//						replaceNodeWithText(promoVoteLink(), "Thanks for voting");
						replaceSiblingsWithText(promoVoteLink(), "Thanks for voting");
					}
					if (DEBUG) {
						var theDebugMessage = stat.getElementsByTagName("mesg")[0];
						alert("Success: " + theDebugMessage.firstChild.nodeValue);
					}
				}
			}
			/// in particular, if change UI to reflect response
		} else {
			alert("Transaction completed, but response from the server was not OK (" + req.status + "): " + req.statusText);
		}
//		turnIconOff(document.getElementById("wishIcon"));
	}
}
