/**
 * @var form mixed  The ID or javascript object for the form containing inputs for entering calculator data
 */
var Calculator = Class.create({
	initialize: function (form) {
		this.form = (typeof(form) === 'string') ? $(form) : Element.extend(form);
		this.form.getInputs('text').each(function (element) {
			element.value = element.value.replace(/,/, '');
		});
		this.form.getInputs('text').invoke('observe', 'keyup', function(event){
			var element = event.element();
			if (element.present() && element.hasClassName('error')){
				element.removeClassName('error');

				// Check to see if this element was part of a required set.
				// NOTE: this does not properly implement required quantities
				// for a set.
				if (typeof(element._set) !== 'undefined') {
					element._set.fields.each( function(id) {
						$(id).removeClassName('error');
					});
				}

				if (element.form.select('input.error, select.error, textarea.error').length === 0) {
					Effect.Fade('calculator-output', {duration: 0.4});
					// Note:  the 'error' class on the 'calculator-output' element
					// will be removed next time show_output( ) gets run.
				}
			}
		});
	},

	calculate: function () {
		var errors, output = '';

		errors = this.check_required();
		if (errors.length === 0){
			output = this.template(this.algorithm());
		}
		else {
			output = this.error_template(errors);
		}
		this.show_output(output, errors);
	},

	check_required: function () {
		var errors = [];
		var label;

		// Check over the list of "required_fields"
		// specified by the calculator
		if (typeof(this.required_fields) !== 'undefined') {
			this.required_fields.each(function(field) {
				if ( ! $(field).present()) {
					errors.push(field);
					$(field).addClassName('error');
				}
			});
		}

		// Check for each required set, where any one (or more)
		// field in a particular set of fields is mandatory.
		var num_required, num_found;
		if (typeof(this.required_field_sets) !== 'undefined') {
			this.required_field_sets.each(function(set) {
				// Count the number of fields in the set that
				// have contents.  If this is less than the number
				// of required elements in the set, then set an
				// appropriate error
				num_required = set.quantity || 1;
				num_found = set.fields.inject(0, function(sum, id) {
					return sum + ( ($(id).present()) ? 1 : 0 );
				});
				if (num_found < num_required) {
					errors.push(set);
					set.fields.each( function(id) {
						$(id).addClassName('error');
						$(id)._set = set;
					});
				}
			});
		}

		return (errors) ;
	},

	show_output: function ( output, errors ) {
		if (typeof(errors) !== 'undefined' && errors.length > 0) {
			$('calculator-output').addClassName('error');
		}
		else {
			$('calculator-output').removeClassName('error');
		}
		$('calculator-output').innerHTML = output;
		Effect.Appear('calculator-output', {duration: 0.4});
	},

	template: function ( value ) {
		// Should be overwritten by subclasses
		return value;
	},

	error_template: function ( errors ) {
		// Can be overwritten if needed
		return 'Please fill in the missing information above.'
	},

	algorithm: function () {
		// Should be overwritten by subclasses
	}
});


var SavingsCalculator = Class.create( Calculator, {
	required_fields: ['total-invested', 'months-years', 'interest-rate'],

	template: function ( value ) {
		return 'Total Estimated Savings: <strong>$' + value.format() + '</strong>';
	},

	algorithm: function () {
		// Copied straight from the old fbtonline.
		// Not entirely sure what's going on here...

		if($('month-years-menu').options[1].selected == true) {
			m = $('months-years').value * 12;
		}
		else {
			m = $('months-years').value;
		}
		i = Math.pow((($('interest-rate').value/100)+1),.0833333)-1;
		den = i / (i+1);
		f = Math.pow((i+1),m)-1;
		f /= den;
		return f * $('total-invested').value;
	}
});

var LoanPaymentCalculator = Class.create( Calculator, {
	required_fields: ['principal', 'payments', 'interest-rate'],

	template: function ( value ) {
		return 'Monthly Payment: <strong>$' + value.format() + '</strong>';
	},

	algorithm: function () {
		var i, j, pow, monthly;

		i = $('interest-rate').value;
		if ( i < 1 ) {
			i *= 100;
			$('interest-rate').value = i;
		}
		i /= 1200;

		pow = 1;
		for (j = 0; j < $('payments').value; j++) {
			pow = pow * (1 + i);
		}
		monthly = ($('principal').value * pow * i) / (pow - 1);
		return Math.ceil(100 * monthly) / 100;
	}
});


/**
 * Calculates the monthly payment needed on a mortgage given the
 * loan amount, interest rate, and duration of the mortgage.
 */
var MortgagePaymentCalculator = Class.create( Calculator, {
	required_fields: ['years', 'interest-rate', 'loan-amount'],

	template: function ( value ) {
		return 'Monthly Principle Interest: <strong>$' + value.format() + '</strong>';
	},

	algorithm: function () {
		var output = '';

		// Simple Interest formula:
		// http://en.wikipedia.org/wiki/Compound_interest#Simple
		//
		//  FV = PV ( 1+i )^n
		//
		//  Calculates the future value (FV) of an investment's
		//  present value (PV) accruing at a fixed interest rate (i)
		//  for n periods.

		var payments = $('years').value * 12;
		var monthly_interest = $('interest-rate').value / 1200;
		var base = Math.pow(1 + monthly_interest, payments);
		var principle_interest = $('loan-amount').value * monthly_interest / (1 - 1/base);

		// Instead of rounding, truncate our calculation to the cent.
		principle_interest = Math.floor( 100 * principle_interest ) / 100;

		return principle_interest;
	}
});

var DebtPayoffCalculator = Class.create( Calculator, {
	required_fields: ['credit-balance', 'interest-rate'],

	required_field_sets: [
		{ quantity: 1, fields: ['monthly-payment', 'desired-months'] }
	],

	template: function ( params ) {
		// Yeah, it's a switch statement... I know.
		var message = '';
		switch(params.template) {
			case 'monthly-payment':
				message = "It will cost $" + params.payment.format() + " a month to pay off this card and will cost you a total of $" + params.total.format() + ".";
				break;
			case 'months':
				message = "It will take " + params.months + " month" + ((params.months != 1) ? 's' : '') + " to pay off this card and will cost you a total of $" + params.total.format() + ".";
				break;
			default:
				break;
		}
		return 'Payment: <strong>' + message + '</strong>';
	},

	algorithm: function () {
		var params = {
			template: '',
			payment: '',
			months: '',
			total: ''
		};

		var monthly_rate = ($('interest-rate').value / 100) / 12;
		if ( !$('monthly-payment').present() )
		{
			var payment = $('credit-balance').value * monthly_rate / ( 1 - Math.pow((1 + monthly_rate),( -$('desired-months').value)) );
			payment = Math.round(payment * 100) / 100;

			params.template = 'monthly-payment';
			params.payment = payment;
			params.total = payment * $('desired-months').value;
		} else {
			var remainingBalance = $('credit-balance').value;
			var minPayment = monthly_rate * $('credit-balance').value;
			var months = 0;
			var lastPayment;
			if (minPayment > $('monthly-payment').value) {alert ('Your monthly payment is less than the monthly interest charged by this card.'); return;}

			while (remainingBalance>0)
			{
			        months++;
			        remainingBalance=remainingBalance*(1 + monthly_rate)-$('monthly-payment').value;
			}

			params.template = 'months';
			params.months = months;
			params.total = $('monthly-payment').value * months;
		}
		return params;
	}
});

Number.prototype.format = function ( decimals, dec_point, thousands_sep ) {
	// A Javascript implementation of PHP's number_format() function
	//
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://crestidg.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // *     example 1: number_format(1234.5678, 2, '.', '');
    // *     returns 1: 1234.57

    var n = this, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
    var d = dec_point == undefined ? "." : dec_point;
    var t = thousands_sep == undefined ? "," : thousands_sep, s = n < 0 ? "-" : "";
    var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;

    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};