
function MortgageCalculator()
{
	this.loaded = false;
	this.drawChart = {'mortgage_repayments':false,'interest_rate':false};
	this.scheduleInfo = {};
}

MortgageCalculator.prototype.validateInt = function(id, min, max)
{
	var e = document.getElementById(id);
	this[id] = e.value.replace(/[£$,\s]/g, '');
	
	if (!this[id].match(/^\d+$/))
	{
		alert('error ' + id + '\n' + this[id]);
		return false;
	}
	
	this[id] = parseInt(this[id], 10);
	return true;
}

MortgageCalculator.prototype.validateFloat = function(id, min, max)
{
	var e = document.getElementById(id);
	this[id] = e.value.replace(/£$,/, '');
	
	// todo
	
	this[id] = parseFloat(this[id]);
	return true;
}

MortgageCalculator.prototype.validateDate = function(id)
{
	var e = document.getElementById(id);
	this[id] = e.value;
	
	// todo
	var date = this[id].split('/');
	this[id] = new Date();
	this[id].setFullYear(date[2], date[1] - 1, date[0]);
	
	return true;
}

MortgageCalculator.prototype.set = function(id, value)
{
	document.getElementById(id).innerHTML = '';
	document.getElementById(id).appendChild(document.createTextNode(value));
}

MortgageCalculator.prototype.calculate = function()
{
	var valid = true;
	
	// Mortgage Information
	valid = valid && this.validateInt('loanAmount', 1000, 10000000);
	valid = valid && this.validateFloat('air');
	valid = valid && this.validateInt('termLength', 1, 50);
	valid = valid && this.validateDate('firstPaymentDate');
	
	// Balance at a Specified Year
	valid = valid && this.validateInt('balanceAtYear', 0, 50);
	
	// Fixed Rate or ARM
	valid = valid && this.validateInt('yearsRateRemainsFixed', 0, 100);
	valid = valid && this.validateFloat('interestRateCap', 0, 50);
	valid = valid && this.validateFloat('interestRateMinimum', 0, 50);
	valid = valid && this.validateInt('periodsBetweenAdjustments', 1, 100);
	valid = valid && this.validateFloat('estimatedAdjustment', -50, 50);

	//Extra Payments
	valid = valid && this.validateFloat('extraPayment', 0, 100000);
	valid = valid && this.validateInt('paymentInterval', 1, 12);
	valid = valid && this.validateFloat('extraAnnualPayment', 0, 100000);
	
	if (!valid)
		return;
	
	var e = document.getElementById('compoundPeriod');
	this.compoundPeriod = e.options[e.selectedIndex].value;
	
	var e = document.getElementById('monthOfAnnualPayment');
	this.monthOfAnnualPayment = e.options[e.selectedIndex].value;
	
	var e = document.getElementById('variableOrFixedRate');
	this.variableOrFixedRate = e.options[e.selectedIndex].value;
	
	
	// Mortgage Information
	var cp = this.compoundPeriod;
	var nper = this.termLength * 12;
	
	var rate = Math.pow(1 + (.01 * this.air / cp), cp / 12) - 1;
	this.payment = (-this.pmt(rate, nper, this.loanAmount)).toFixed(2);
	this.set('payment', this.money(this.payment));
	
	this.schedule(false);
	this.schedule(true);
	
	// Balance at a Specified Year
	var date = new Date();
	date.setFullYear(this.firstPaymentDate.getFullYear() + this.balanceAtYear);
	this.set('balanceDate', date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear());
	
	var interestPaid = this.scheduleInfo.interestPaidAtSpecifiedYear;
	this.set('balanceInterestPaid', this.money(interestPaid.toFixed(0)));
	
	var principalPaid = this.scheduleInfo.principalPaidAtSpecifiedYear;
	this.set('balancePrincipalPaid', this.money(principalPaid.toFixed(0)));
	
	this.set('balanceOutstanding', this.money((this.loanAmount - principalPaid).toFixed(0)));
	
	// Fixed Rate or ARM
	var highestMonthlyPayment = this.scheduleInfo.maxPayment;
	this.set('highestMonthlyPayment', this.money(highestMonthlyPayment.toFixed(2)));
	
	var disabled = this.variableOrFixedRate == 'fixed';
	document.getElementById('yearsRateRemainsFixed').disabled = disabled;
	document.getElementById('interestRateCap').disabled = disabled;
	document.getElementById('interestRateMinimum').disabled = disabled;
	document.getElementById('periodsBetweenAdjustments').disabled = disabled;
	document.getElementById('estimatedAdjustment').disabled = disabled;
	
	// Extra Payments
	var totalExtraPayments = this.scheduleInfo.totalExtra;
	this.set('totalExtraPayments', this.money(totalExtraPayments.toFixed(2)));
	
	// Mortgage Summary
	var totalPayments = this.scheduleInfo.totalPayments;
	this.set('totalPayments', this.money(totalPayments.toFixed(2)));
	
	var totalInterest = totalPayments - this.loanAmount;
	this.set('totalInterest', this.money(totalInterest.toFixed(2)));
	
	var yearsUntilPaidOff = Math.round(this.scheduleInfo.monthsUntilPaidOff / 12);
	this.set('yearsUntilPaidOff', yearsUntilPaidOff.toFixed(0));
	
	var lastPaymentDate = new Date(this.scheduleInfo.lastPaymentDate);
	this.set('lastPaymentDate', lastPaymentDate.getDate() + '/' + (lastPaymentDate.getMonth() + 1) + '/' + lastPaymentDate.getFullYear());
	
	var totalPaymentsNoExtra = this.scheduleInfo.totalPaymentsNoExtra;
	var totalInterestNoExtra = totalPaymentsNoExtra - this.loanAmount;
	
	var interestSavings = totalInterestNoExtra - totalInterest
	interestSavings = Math.max(0, interestSavings);
	this.set('interestSavings', this.money(interestSavings.toFixed(0)));
	
	this.loaded = true;
	
	this.loadRepaymentsChart();
	this.loadInterestChart();
	
	return false;
}

MortgageCalculator.prototype.pmt = function(rate, nper, pv)
{
	return -pv * (rate + (rate / (Math.pow(1 + rate, nper)-1)));
}

MortgageCalculator.prototype.schedule = function(noExtra)
{
	var balanceRecord = noExtra ? 'balanceNoExtra' : 'balance';
	this[balanceRecord] = [];
	this.dates = [];
	this.rates = [];
	
	var i = 1;
	
	var balance = this.loanAmount;
	var lastBalance = balance;
	var lastPaymentDue = -1;
	var lastRate = -1;
	
	var date = new Date(this.firstPaymentDate.valueOf());
	
	if (!noExtra)
	{
		this.scheduleInfo =
		{
			'maxPayment': 0,
			'totalExtra':0,
			'totalPayments':0,
			'totalPaymentsNoExtra':0,
			'interestPaidAtSpecifiedYear':0,
			'principalPaidAtSpecifiedYear':0,
			'monthsUntilPaidOff':0,
			'lastPaymentDate':'',
			'maxPayment':0
		};
	}
	
	if (!noExtra)
	{
		var tBody = document.getElementById('repaymentsBody');
		while (tBody.firstChild)
			tBody.removeChild(tBody.firstChild);
	}
	
	while (balance >= .01)
	{
		this[balanceRecord].push(balance);
		
		var rate = this.air;
		var monthsFixed = this.yearsRateRemainsFixed * 12;
		if ((this.variableOrFixedRate == 'variable') && (i > monthsFixed))
		{
			rate += this.estimatedAdjustment * Math.ceil((i - monthsFixed) / this.periodsBetweenAdjustments);
			rate = Math.min(rate, this.interestRateCap);
			rate = Math.max(rate, this.interestRateMinimum);
		}
		
		rate = rate.toFixed(3) * 1;
		
		if (noExtra)
		{
			this.dates.push(date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear());
			this.rates.push(rate);
		}
		
		var interestDue = Math.pow(1 + .01 * rate / this.compoundPeriod, this.compoundPeriod / 12) - 1;
		interestDue = (interestDue * lastBalance).toFixed(2);
		
		if (i == this.termLength * 12)
		{
			var paymentDue = (lastBalance + lastBalance * .01 * rate / 12).toFixed(2);
		}
		else
		{
			var a = this.payment;
			if (rate != this.air)
			{
				if (rate == lastRate)
					a = lastPaymentDue;
				else
					a = -this.pmt(.01 * rate / 12, this.termLength * 12 - i + 1, lastBalance);
			}
			
			var b = lastBalance + lastBalance * .01 * rate / 12
			var paymentDue = Math.min(a, b).toFixed(2);
		}
		
		var extra = '0.00';
		if (!noExtra && (lastBalance > paymentDue))
		{
			extra = (i % this.paymentInterval) ? 0 : this.extraPayment;
			extra += (date.getMonth() == this.monthOfAnnualPayment) ? this.extraAnnualPayment : 0;
			
			extra = Math.min(lastBalance + interestDue * 1 - paymentDue, extra).toFixed(2);
			
			this.scheduleInfo.totalExtra += extra * 1;
		}
		
		var principalPaid = (paymentDue - interestDue + extra * 1).toFixed(2);
		var balance = (lastBalance - principalPaid).toFixed(2);
		
		if (noExtra)
		{
			this.scheduleInfo.totalPaymentsNoExtra += interestDue * 1 + principalPaid * 1;
		}
		else
		{
			this.scheduleInfo.totalPayments += interestDue * 1 + principalPaid * 1;
			this.scheduleInfo.lastPaymentDate = date.valueOf();
			this.scheduleInfo.monthsUntilPaidOff++;
			this.scheduleInfo.maxPayment = Math.max(this.scheduleInfo.maxPayment, paymentDue * 1);
			
			if (i <= this.balanceAtYear * 12)
			{
				this.scheduleInfo.interestPaidAtSpecifiedYear += interestDue * 1;
				this.scheduleInfo.principalPaidAtSpecifiedYear += principalPaid * 1;
			}
			
			var year = i % 12 ? '' : Math.round(i / 12);
			var tr = document.createElement('tr');
			tr.className = 'year' + (Math.floor((i - 1) / 12) % 2);
			
			this.addCell(tr, i);
			this.addCell(tr, date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear());
			this.addCell(tr, rate.toFixed(3) + '%');
			this.addCell(tr, interestDue);
			this.addCell(tr, paymentDue);
			this.addCell(tr, extra);
			this.addCell(tr, principalPaid);
			this.addCell(tr, balance);
			this.addCell(tr, year);
			tBody.appendChild(tr);
		}
		
		date.setMonth(date.getMonth() + 1);
		i++;
		
		lastBalance = balance * 1;
		lastPaymentDue = paymentDue * 1;
		lastRate = rate * 1;
	}
}

MortgageCalculator.prototype.addCell = function(tr, value)
{
	var textNode = document.createTextNode(value);
	var td = document.createElement('td');
	
	td.appendChild(textNode);
	tr.appendChild(td);
}


MortgageCalculator.prototype.loadRepaymentsChart = function()
{
	var self = this;
	
	if (!this.loaded || !this.drawChart.mortgage_repayments)
	{
		setTimeout(function() { self.loadRepaymentsChart(); }, 1000);
		return;
	}
	
	var len = this.dates.length;
	
	// Note: line breaks aren't allowed in the XML
	var xml = "<chart>"
		+ "<chart_type>line</chart_type>"
		+ "<axis_category orientation='vertical_down'/>"
		+ "<chart_pref point_size='0'/>"
		+ "<chart_data>";
	
	var dateInterval = Math.floor(len / 10);
	var data = [];
	for (var i = 0; i < len; i++)
		data.push("<string>" + (i % dateInterval ? "" : this.dates[i]) + "</string>");
	xml += "<row><null/>" + data.join('') + "</row>"
	
	var data = [];
	var balanceLen = this.balance.length;
	for (var i = 0; i < len; i++)
		data.push("<number>" + (i < balanceLen ? this.balance[i] : 0) + "</number>");
	xml += "<row><string>Balance (£)</string>" + data.join('') + "</row>"
	
	if (this.extraPayment || this.extraAnnualPayment)
	{
		var data = [];
		for (var i = 0; i < len; i++)
			data.push("<number>" + (this.balanceNoExtra[i] + 100) + "</number>");
		xml += "<row><string>Balance - No Extra Payments (£)</string>" + data.join('') + "</row>"
	}
	
	xml += "</chart_data>"
		+ "</chart>";
	
	document.mortgage_repayments.Update_XML(xml, true, 'normal');
}


MortgageCalculator.prototype.loadInterestChart = function()
{
	var self = this;
	if (!this.loaded || !this.drawChart.interest_rate)
	{
		setTimeout(function() { self.loadInterestChart(); }, 1000);
		return;
	}
	
	var len = this.dates.length;
	
	// Note: line breaks aren't allowed in the XML
	var xml = "<chart>"
		+ "<chart_type>line</chart_type>"
		+ "<axis_category orientation='vertical_down'/>"
		+ "<chart_pref point_size='0'/>"
		+ "<chart_data>";
	
	var dateInterval = Math.floor(len / 10);
	var data = [];
	for (var i = 0; i < len; i++)
		data.push("<string>" + (i % dateInterval ? "" : this.dates[i]) + "</string>");
	xml += "<row><null/>" + data.join('') + "</row>"
	
	var data = [];
	for (var i = 0; i < len; i++)
		data.push("<number>" + this.rates[i] + "</number>");
	xml += "<row><string>Interest Rate (%)</string>" + data.join('') + "</row>"
	
	xml += "</chart_data>"
		+ "</chart>";
	
	document.interest_rate.Update_XML(xml, true, 'normal');
}

MortgageCalculator.prototype.money = function(money)
{
	x = money.toString().split('.');
	var x1 = x[0];
	
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1))
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	
	return '£' + x1 + (x.length > 1 ? '.' + x[1] : '');
}





MortgageCalculator.prototype.init = function()
{
	var hint = document.getElementById('hint');
	var ths = document.getElementById('calculator').getElementsByTagName('th');
	var len = ths.length;
	
	for (var i = 0; i < len; i++)
	{
		var title = ths[i].title;
		ths[i].title = '';
		
		if (!title)
			continue;
		
		ths[i].parentNode.onmouseover = (function(self, th, hint, title)
		{
			return function()
			{
				hint.style.display = 'block';
				hint.innerHTML = title.replace('\n', '<br>');
				
				var point = self.getOffset(th);
				point.x -= hint.offsetWidth + 2;
				
				if (point.x < 0)
				{
					point.x = 0;
					point.y += th.offsetHeight + 2;
				}
				
				hint.style.left = point.x + 'px';
				hint.style.top = point.y + 'px';
				
				th.parentNode.style.backgroundColor = '#ffffee';
			}
		})(this, ths[i], hint, title);
		
		ths[i].parentNode.onmouseout = (function(th, hint)
		{
			return function()
			{
				hint.style.display = 'none';
				th.parentNode.style.backgroundColor = '';
			}
		})(ths[i], hint);
	}
}


MortgageCalculator.prototype.getOffset = function(elm)
{
	var point = {'x': 0, 'y':0};
	
	while (elm)
	{
		point.x += elm.offsetLeft;
		point.y += elm.offsetTop;
		
		elm = elm.offsetParent;
	}
	
	return point;
}


// XML-SWF-Charts

function Loaded_Chart(id)
{
	calculator.drawChart[id] = true;
}
