// todo: add cookies to remember state http://code.google.com/p/cookies/wiki/Documentation#Test_if_browser_is_accepting_cookies

// scroll the iPhone browser past the toolbar
addEventListener("load", function() { setTimeout(SCHideURLbar, 0); }, false);
function SCHideURLbar(){
	window.scrollTo(0,1);
}

// define the maximum number of subcaucuses
var maxSubcaucusValue = getQuerystring('max');
if (maxSubcaucusValue == 0) {
	var numberOfSubcaucuses = 30;
} else {
	var numberOfSubcaucuses = maxSubcaucusValue;
}

var message = "";
SCNotify("Hello world.\n");

$(document).ready(SCReady);

function getQuerystring(key, default_)
{ // http://www.bloggingdeveloper.com/post/JavaScript-QueryString-ParseGet-QueryString-with-Client-Side-JavaScript.aspx
	if (default_==null) default_=""; 
	key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
	var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
	var qs = regex.exec(window.location.href);
	if(qs == null)
		return default_;
	else
	return qs[1];
}

function trim(s) {
	return s.replace(/^\s+|\s+$/g,"");
}

function SCNotify(note) {
	message = message + note;
	// no messages for now // $("#message").html("<pre>" + message + "</pre>");
}

function SCReady() {
	SCNotify("DOM ready.\n");
	SCPopulateTable();
	//$("#delegatesallowed").addClass("red");
	//$(".scrow").find(".scrowcount").addClass("blue");
	$("input").blur(SCBlur);
	//$("#precinct-note").html(" start here");
	$("#precinct").blur();
	$("#precinct").focus();
	
}

function SCPopulateTable() {
	var codeForTable = "<tr><td>&nbsp;</td><td class='sctableheader'>Subcaucus name</td><td class='sctableheader'>Count</td><td class='sctableheader'><span id='scheader-note'></span></td></tr>";
	for (i=1; i <= numberOfSubcaucuses; i++) {
		codeForTable += "<tr id='scrow-"+i+"' class='scrow'>";
		codeForTable += "<td class='scrownumber'>"+i+"</td>";
		codeForTable += "<td><input id='scrow-"+i+"-name' class='scrowname' type='text' tabindex='"+(numberOfSubcaucuses+i+2)+"' /></td>";
		codeForTable += "<td><input id='scrow-"+i+"-count' class='scrowcount numeric' type='tel' tabindex='"+(i+2)+"' /></td>";
		codeForTable += "<td><span id='scrow-"+i+"-count-note' class='note'></span></td>";
		codeForTable += "</tr>";
	}
	codeForTable += "<tr><td>&nbsp;</td><td class='sctabletotal'>total participants</td><td class='sctabletotal'><span id='sctotal'></span></td><td class='sctabletotal fineprint'><span id='sctotal-note'></span></td></tr>";
	codeForTable += "<tr><td>&nbsp;</td><td class='sctabletotal'>total viable</td><td class='sctabletotal'><span id='scviabletotal'></span></td><td class='sctabletotal fineprint'><span id='scviabletotal-note'></span></td></tr>";
	$("#sctable").html(codeForTable);
}

function SCClearNotes() {
	$(".note").html(""); // clear all the notes before we check
	$(".red").removeClass("red"); // clear all the red highlights
	$(".green").removeClass("green"); // clear all the green highlights
}

function SCBlur() { // losing focus, check the value entered and then run calculations
	SCNotify("Blur " + this.id + " " + this.value + ". ");
	SCClearNotes();
	$(this).val(trim(this.value));
	if ($(this).hasClass("numeric")) { // validate the eligible item
		SCNotify("Numeric. ");
		if (! /^\d*$/.test(this.value)) { // is it not numeric or blank?
			SCNotify("Bad. ");
			$(this).focus();
			$(this).addClass("red");
			$("#"+this.id+"-note").html(" just a number, please");
			$("#"+this.id+"-note").addClass("red");
			return;
		} else {
			SCNotify("Good. ");
		}
	}
	SCDistributeDelegates();
	SCNotify("\n");
}

function SCRemainderRankObject(subcaucus,remainder) {
	this.subcaucus = subcaucus;
	this.remainder = parseFloat(remainder);
	if (isNaN(this.remainder)) this.remainder = 0;
}

function SCRemainderRankDescendingHandler(thisObject,thatObject) {
	if (thisObject.remainder > thatObject.remainder)
	{
		return -1;
	}
	else if (thisObject.remainder < thatObject.remainder)
	{
		return 1;
	}
	return ((Math.floor(Math.random()*3)-1) ? -1 : 1); // generate a random 1 or -1
}

function SCDistributeDelegates() {
	
	// "Step No. 1: Add up the total number of members of all the subcaucuses." (room)
	var room = 0;
	var members = new Object;
	for (var i=1; i <= numberOfSubcaucuses; i++) {
		members[i] = parseInt($("#scrow-"+i+"-count").val());
		if (isNaN(members[i])) members[i] = 0;
		room += members[i];
	}
	if (room > 0) {
		$("#sctotal").html(room);
	} else {
		return; // nothing else to do until some people are in the room
	}
	
	// make sure we know the "number of delegates to be elected" (allowed)
	var allowed = parseInt($("#delegatesallowed").val());
	if (isNaN(allowed)) {
		$("#delegatesallowed").focus();
		$("#delegatesallowed").addClass("red");
		$("#delegatesallowed-note").html(" just a number, please");
		$("#delegatesallowed-note").addClass("red");
		return;
	}

	// calculate the viability number (viability)
	// "Step No. 2: Divide the result of Step No. 1" (room)
	// "by the total number of delegates to be elected," (allowed)
	// "round the result up to the next whole number." (wholeViability)
	// "This is the viability number." 
	// (this contradicts the example, which uses viability rather than wholeViability)
	var viabilityStatement = "";
	var viability = room / allowed;
	var wholeViability = Math.ceil(viability); // since people are not easily divided
	viabilityStatement += "<p>";
	if (room <= allowed) { // can everyone be a delegate?
		viabilityStatement += "You are allowed enough delegates ("+allowed+") that anyone who wants to be a delegate can be a delegate. Just sign them up!";
	} else { // we have to figure out the viability number
		viabilityStatement += "The \"viability number\" is <span id='viabilitynumber'>"+viability.toFixed(3)+"</span>, ";
		viabilityStatement += "so any subcaucus with fewer than <span id='wholeviability'>"+wholeViability+" people</span> is not viable. ";
	}
	viabilityStatement += "</p>";
	$("#viability").html(viabilityStatement);
	
	// determine which subcaucuses are viable (viabilityScore >= 1)
	// and calculate the total number viable people in the room (viableRoom)
	var viabilityScore = new Object; // the raw score for the delegation
	var viableRoom = 0; // the total number of people in viable subcaucuses
	for (var i=1; i <= numberOfSubcaucuses; i++) {
		viabilityScore[i] = members[i] / viability;
		if (viabilityScore[i] >= 1) {
			viableRoom += members[i];
		} else {
			if (members[i] > 0) { // only highlight subcaucuses with members
				$("#scrow-"+i+"-count-note").html(
					"<span class='largeprint'>0<span> <span class='fineprint'>(" 
					+ viabilityScore[i].toFixed(2) 
					+ " not viable)</span>"
				);
				$("#scrow-"+i+"-count-note").addClass("red");
			}
		}
	}
	if (viableRoom > 0) {
		$("#scviabletotal").html(viableRoom);
	} else {
		return; // no viable participants in the room yet
	}
	// calculate the viability number for the delegate allocation process
	var delegateViability = viableRoom / allowed;
	viabilityStatement += "<p>Each delegate requires "+delegateViability.toFixed(3)+" people in a viable subcaucus.</p>";
	$("#viability").html(viabilityStatement);
	
	// calculate how many delegates each viable subcaucus gets
	var delegation = new Object; // the number of delegates they get
	var totalDelegation = 0;
	var remainder = new Object; // the remainder for use in allocating leftover delegates
	var remainderRank = new Array(); // will become a rank-order list of the remainders
	var delegateScore = new Object; // the raw score for the delegation
	for (var i=1; i <= numberOfSubcaucuses; i++) {
		if (viabilityScore[i] >= 1) { // this is a viable subcaucus
			delegateScore[i] = members[i] / delegateViability;
			delegation[i] = Math.floor(delegateScore[i]);
			remainder[i] = delegateScore[i] - delegation[i];
			remainderRank.push(new SCRemainderRankObject(i,remainder[i]));
			totalDelegation += delegation[i];
		}
	}
		
	// now sort and assign the remainders
	var ranked = new Object;
	var addon = new Object;
	remainderRank.sort(SCRemainderRankDescendingHandler);
	if (totalDelegation > 0) { // to deal with an empty form
		//while (totalDelegation < allowed) { // still some to distribute
			for(i=0; i < remainderRank.length; i++) {
				var sub = remainderRank[i].subcaucus;
				ranked[sub] = i+1; // recorded to view later
				if (isNaN(addon[sub])) { // dealing with js NaN issue
					addon[sub] = 0;
				}
				// if we still have delegates to be assigned
				if (totalDelegation < allowed) { 
					delegation[sub]++; // hand out one delegate
					addon[sub]++;
					totalDelegation++;
				}
			}
		//}
	}
	
	// post statements about the delegations
	totalDelegation = 0; // start a fresh count, now including remainders
	for (var i=1; i <= numberOfSubcaucuses; i++) {
		if (viabilityScore[i] >= 1) {
			if (viability > 1) {
				// var s = delegation[i] > 1 ? "s" : ""; // deal with plural
				var delegationStatement = "<span class='largeprint'>" + delegation[i] 
					+ "</span> <span class='fineprint'>(" 
					+ delegateScore[i].toFixed(2);
				if (addon[i] != 0) delegationStatement += "+" + addon[i];
				delegationStatement += " r" + ranked[i] + ")</span>";
				$("#scrow-"+i+"-count-note").html(delegationStatement);
				$("#scrow-"+i+"-count-note").addClass("green");
				totalDelegation += delegation[i];
			} else {
				$("#scrow-"+i+"-count-note").html(
					"All!"
				);
				totalDelegation += members[i];
			}
		}
	}
	$("#sctotal-note").html(totalDelegation + " delegates in all")
	
	if (totalDelegation < allowed) {
		$("#scheader-note").html("Delegates");
	} else {
		$("#scheader-note").html("Delegates");
	}

}
