
var loadingImageHTML ='<img class="loading-image" src="/images/ajax-loader.gif" alt="Loading.." style="vertical-align: middle;" />';

var worksheet_tainted = false;

var lastKnownGoodValue = 0.00;

window.onbeforeunload = function(e) {
	if (worksheet_tainted == true) {
		var msg = "You have unsaved changes. To save your changes, stay on this page and click the 'Save' button";
		e.returnValue = msg;
		return msg;
	}
};

jQuery(document).ready(function($)
{
	// Set up bindings for showing tips
	setupCellBindings(".section-row");


	// do an initial calcDiff for all rows when page loads
	$.each($("table.section tr.section-row"), function(i, element) {
		var $tr = $(element);
		calcDiff($tr);
	});


	// Give fields "quick edit" ability
	$("a.editable").live("click", function(e) {
		e.preventDefault();
		var hiddenInputID = $(this).attr("rel");
		var newval = quickEditField(hiddenInputID, false);
		$(this).find("span.editable-content").text(newval);
		$(this).blur();
	});


	// show or hide 'help/instructions' box
	$("#btn_show_instructions").click(function(e){ // SHOW HELP
		$('#worksheet_customize_instructions').show();
		e.preventDefault();
		$.get($(this).attr("href"));
	});
	$("#btn_hide_instructions").click(function(e) { // HIDE HELP
		$("#worksheet_customize_instructions").hide();
		e.preventDefault();
		$.get($(this).attr("href"));
	});


}); // end jquery document.ready()


/**
 * Given DOM element oElement, determine offset from
 * top of #worksheet_wrapper
 * @return integer
 */
function _getYOffsetFromWrapper( oElement )
{
	var iReturnValue = 0;
	while( oElement != null ) {
		iReturnValue += oElement.offsetTop;
		oElement = oElement.offsetParent;

		if (oElement != null && oElement.id == 'worksheet_wrapper') {
			// Got offset from worksheet_wrapper. Break loop and return.
			break;
		}
	}
	return iReturnValue;
}


/**
 * set up necessary jquery bindings for each spreadsheet cell
 *
 * @param sel jquery selector
 * @return void
 */
function setupCellBindings(sel)
{
	$(sel + " input:text").focus(function() { // CELL GAINS FOCUS
		/*
		 * When a cell in a worksheet gains focus
		 */

		$(this).select(); // highlight content for easy editing

		// user is inside a text element in a worksheet row
		// 1. give the row the 'highlighted' class
		// and remove from others
		$("tr.section-row").removeClass("highlighted");
		$(this).closest("tr.section-row").addClass("highlighted");

		// 2. Load the tip into the bubble for this cell
		//
		var $tr = $(this).closest("tr");

		var tipHref = $tr.find("td.tip > a:first").attr("href"); // row tip
		if (!tipHref) {
			tipHref = default_tip_href; // worksheet default tip
		}
		if (tipHref) {
			var yOffset = _getYOffsetFromWrapper(this);

			_scheduleShowTipWithUri(tipHref, yOffset);
		}

	})
	.keydown(function(event) {
		/*
		 * Make sure input is valid number
		 */

		if (event.keyCode != 46 && event.keyCode != 8) { // ignore delete/backspace

			if (isNaN($(this).val())) {
				$(this).val( lastKnownGoodValue );
			}
			else {
				// save last value
				lastKnownGoodValue = $.trim($(this).val());
			}
		}
	})
	.keyup(function(event) {
		/*
		 * When a value of a budget line cell is changed
		 *  - First do some input masking checks
		 *  - Then calculate new totals
		 */

		if ($(this).val() == '.') {
			$(this).val( '0.'); // change leading . to 0.
		}

		if (isNaN($(this).val())) {
			$(this).val( lastKnownGoodValue );
		}

		// Calculate new totals!
		var $tr = $(this).closest("tr");
		calcDiff($tr);
	})
	.blur(function() {
		// re-format data
		var val = _safeParseFloat( $.trim($(this).val()) );
		if (val && !isNaN(val)) {
			val = val.toFixed(2);
			$(this).val( val );
		}
	})
	.change(function() {
		worksheet_tainted = true;
	});


	// capture clicks to show tip link, and show via ajax in explain_bubble instead
	$(sel + " .tip a").click(function(e) {
		e.preventDefault();
		var yOffset = _getYOffsetFromWrapper(this);
		showTipWithUri($(this).attr("href"), yOffset);
	});
}


/**
 * calculate and display difference between 'budget' and 'actual' vals for a given row
 *
 * @param $tr jquery("tr") for the row in question
 * @return void
 */
function calcDiff($tr) {
	var $budgetInput = $tr.find("td.input-budget > input:text");
	var $actualInput = $tr.find("td.input-actual > input:text");

	var budgetVal = $.trim($budgetInput.val());
	var actualVal = $.trim($actualInput.val());

	if (!budgetVal) {
		$budgetInput.val("");
		budgetVal = 0.00;
	}
	if (!actualVal) {
		$actualInput.val("");
		actualVal = 0.00;
	}

	// TODO: error handling, type checking, and possibly better formatting
	var diff = (budgetVal - actualVal).toFixed(2);
	var $td_diff = $tr.find("td.calc-difference");

	$td_diff.removeClass("over-budget");
	$td_diff.removeClass("under-budget");
	if (diff >= 0) {
		// they were under budget.
		$td_diff.addClass("under-budget");
	} else {
		// they went OVER budget.
		$td_diff.addClass("over-budget");
	}

	$td_diff.find("span:first").html(diff);

	// now update the 'totals' line

	updateTotalsForSection( $tr.closest('.section-rows') );

} // end function calcDiff($tr)

/**
 * Updates the calculated totals for a section (group of rows)
 *
 * @param $sectionRows jQuery'd selector for the .section-rows element
 * @return void
 */
function updateTotalsForSection($sectionRows)
{
	var budgetTotal = 0.00;
	var actualTotal = 0.00;
	var diffTotal   = 0.00;

	// tally budget vals
	$.each($sectionRows.find(".input-budget input:text"), function(i, input) {
		if ($(input).val()) {
			budgetTotal += _safeParseFloat($(input).val());
		}
	});

	// tally actual vals
	$.each($sectionRows.find(".input-actual input:text"), function(i, input) {
		if ($(input).val()) {
			actualTotal += _safeParseFloat($(input).val());
		}
	});

	// tally diff vals (or just calculate?)
	$.each($sectionRows.find(".calc-difference span"), function(i, diffspan) {
		if ($(diffspan).text()) {
			diffTotal += _safeParseFloat($(diffspan).text());
		}
	});

//	console.debug("SECTION: budget="+budgetTotal+" actual="+actualTotal+" diff="+diffTotal);

	// update labels
	var $tfoot = $sectionRows.siblings("tfoot");
	$tfoot.find(".total-budget span:first").text(budgetTotal.toFixed(2));
	$tfoot.find(".total-actual span:first").text(actualTotal.toFixed(2));
	$tfoot.find(".total-diff span:first").text(diffTotal.toFixed(2));

	updateGrandTotal();

} // end function _updateTotalsForSection

function _safeParseFloat(val) {
	if ( !val || isNaN(val) ) {
		// not-a-number
		return 0.00;
	}

	return parseFloat(val);
}

/**
 * updates the grand totals at the bottom of the worksheet page
 * @return void
 */
function updateGrandTotal()
{
	var grandBudgetTotal = 0.0;
	var grandActualTotal = 0.0;
	var grandDiffTotal   = 0.0;

	$.each($("tfoot tr.section-totals"), function(i, row) {
		grandBudgetTotal += _safeParseFloat($(row).find(".total-budget span:first").text());
		grandActualTotal += _safeParseFloat($(row).find(".total-actual span:first").text());
		grandDiffTotal   += _safeParseFloat($(row).find(".total-diff   span:first").text());
	});

	var $grandTotalRow = $("#summary_table tr.grand-totals");
	$grandTotalRow.find(".total-budget-grand span:first").text(grandBudgetTotal.toFixed(2));
	$grandTotalRow.find(".total-actual-grand span:first").text(grandActualTotal.toFixed(2));
	$grandTotalRow.find(".total-diff-grand   span:first").text(grandDiffTotal.toFixed(2));
}

/**
 * use setTimeout and clearTimeout to show
 * tip only after focused for 2 seconds.
 * (prevents rapid changing between tip content)
 *
 * @param uri target for ajax call, passed thru to showTipWithUri
 * @param yOffset new 'top' position for #explain_bubble, passed thru to showTipWithUri
 */
function _scheduleShowTipWithUri(uri, yOffset) {
	if (typeof(_scheduleShowTipWithUri.timer_id) != 'undefined') {
		clearTimeout( _scheduleShowTipWithUri.timer_id );
	}

	var delay = 500;

	if ($("#explain_bubble").data('current_uri') == null) {
		// no tip displayed yet, don't delay
		delay = 0;
	}

	_scheduleShowTipWithUri.timer_id = setTimeout('showTipWithUri("'+uri+'", '+yOffset+')', delay);
}

/**
 * load tip content via ajax call
 * @param uri target for ajax call
 * @param yOffset new 'top' position for #explain_bubble
 */
function showTipWithUri(uri, yOffset) {
	if (uri) {
		// Don't re-display a tip if it's already showing
		if ($("#explain_bubble").data('current_uri') != uri)
		{
			$("#explain_bubble").html(loadingImageHTML);
			$("#explain_bubble").load(uri);

			$("#explain_bubble").data('current_uri', uri);

			if (typeof(yOffset) != "undefined" && yOffset !== undefined) {
				$("#explain_bubble").animate({top: yOffset});
			}
		}
	}
	else {
		//console.debug("Blank tip link!");
	}
}

/**
 * Allow edit "in place" of labels, titles, etc.
 *
 * @param field_id the DOM id of the hidden input field to update with the new value
 * @param allow_blank whether blank values are acceptable
 * @return The new value, if acceptable. Otherwise the original value.
 */
function quickEditField(field_id, allow_blank) {
	var fieldHash = '#' + field_id;

	var currVal = jQuery(fieldHash).val();

	var newVal = prompt("Enter new title", currVal);

	newVal = jQuery.trim(newVal);
	if (newVal == "" && !allow_blank) {
		// Empty value not allowed here. Send back previous val
		return currVal;
	}
	else {
		worksheet_tainted = true;
		jQuery(fieldHash).val(newVal);
		return newVal;
	}
}

/**
 * Hide all the $0.00's and then do window.print()
 */
function printWorksheet() {

	// loop thru inputs and blank them if $0
	$(".worksheet input[type='text']").each(function(i, el) {
		if ($(el).val() == '0.00' || $(el).val() == '0' || $(el).val() == '0.0') {
			$(el).val("");
		}
	});

	// loop through calculated fields and blank them if $0
	$(".calc-difference > span, .total-diff > span, .total-budget > span, .total-actual > span, .total-budget-grand > span, .total-actual-grand > span")
	.each(function(i, el) {
		var t = $(el).text();
		if (t.match(/^(\$)?0(\.00)?/)) {
			$(el).text("");
		}
	});

	// Print!
	window.print();
}
