if (Calculators == undefined) {
	var Calculators = {};
}
if (Calculators.loan == undefined) {
	Calculators.loan = {};
}
Calculators.loan.Loan = function Loan(rate, nper, pv, prepayment) {
	this.PAYMENTS_PER_YEAR = 12;
	this.MONTHS = 12;
	
	this.getInterest = function(balance, rate) {
		return new Calculators.type.Money(rate.monthly() * balance).rounded();
	}
	
	this.getPrincipal = function(payment, interest) {
		return new Calculators.type.Money(payment - interest).rounded();
	}
	
	this.getRemainingBalance = function(balance, principal) {
		return new Calculators.type.Money(balance - principal).rounded();
	}
	
	this.getMonthlyNper = function() {
		return Math.ceil(this.nper / this.PAYMENTS_PER_YEAR);
	}
	
	this.setPaymentsPerYear = function(payments) {
		this.PAYMENTS_PER_YEAR = payments;
	}

	this.statements = [];
	this.rate = new Calculators.type.Rate(rate);
	this.nper = nper;
	this.pv = new Calculators.type.Money(pv);

	var formula = new Calculators.math.Formulas();
	this.monthly = new Calculators.type.Money(-1 *
		(formula.pmt(this.rate.monthly(), this.nper, this.pv.getAmount(), 0.00, false)));

	// prepayments
	if (prepayment == undefined) {
		var frequency = new Calculators.loan.PaymentFrequency();
		this.prepayment = new Calculators.loan.Prepayment(0.0, frequency.getPaymentFrequency(new Calculators.loan.PaymentFrequency().NONE), 0);
	}
	else {
		this.prepayment = prepayment;
	}
	var startmonth = this.prepayment.getStartmonth();
	var schedule = this.prepayment.getSchedule();
	var balance = this.pv.getAmount();
	var interest = 0.0;
	var principal = 0.0;
	
	var total = this.nper + 1;
	for (var month = 0; month < total; ++month) {
		if (schedule.isPrepaymentMonth(startmonth, month) && balance > 0.0) {
			balance = balance - this.prepayment.getPrepayment().getAmount();
			principal = principal + this.prepayment.getPrepayment().getAmount();
		}
		
		if (balance < 0.0 && month > 0) {
			var previous = this.statements[month - 1];
			principal = previous.getBalance().rounded();
			balance = 0.0;
		} else if (month == this.nper) {
			balance = new Calculators.type.Money(balance + this.monthly.rounded()).rounded();
			principal = this.getPrincipal(balance, interest);
			balance = 0.0;
		}
		
		this.statements[month] = new Calculators.loan.Statement(balance, interest, principal);
		
		interest = this.getInterest(balance, this.rate);
		principal = this.getPrincipal(this.monthly.getAmount(), interest);
		balance = this.getRemainingBalance(balance, principal);
	}
	
	this.getMonthlyPayment = function() {
		return this.monthly;
	}
	
	this.getRate = function() {
		return this.rate;
	}
	
	this.getNper = function() {
		return this.nper;
	}
	
	this.getPv = function() {
		return this.pv;
	}
	
	this.getPrepayment = function() {
		return this.prepayment;
	}
	
	this.getMonthlyStatement = function(month) {
		if ((month < 0) || (month > this.nper)) {
			throw month + " is not a valid month for this mortgage.  " + 
			"Month must be between 0 and " + this.nper;
		}
		
		return this.statements[month];
	}
	
	this.getYearlyStatement = function(year) {
		if ((year < 0) || (year > this.getMonthlyNper())) {
			throw year + " is not a valid year for this mortgage.  " +
			"Year must be between 0 and " + this.getMonthlyNper();
		}
		
		var balance = 0.0;
		var interest = new Calculators.type.Money(0.0);
		var principal = new Calculators.type.Money(0.0);
		
		var end = (year == 0) ? 1 : year * this.PAYMENTS_PER_YEAR + 1;
		var start = (year == 0) ? 0 : end - this.PAYMENTS_PER_YEAR;
		
		for (var month = start; month < end; ++month) {
			if (month <= this.nper) {
				var stmt = this.getMonthlyStatement(month);
				balance = stmt.getBalance();
				interest = interest.add(stmt.getInterest());
				principal = principal.add(stmt.getPrincipal());
			}
		}
		
		return new Calculators.loan.Statement(
			new Calculators.type.Money(balance), interest, principal);
	}
	
	this.getTotalStatement = function() {
		var interest = new Calculators.type.Money(0.0);
		var principal = new Calculators.type.Money(0.0);
		
		var total = this.getMonthlyNper();
		for (var year = 0; year <= total; ++year) {
			var stmt = this.getYearlyStatement(year);
			interest = interest.add(stmt.getInterest());
			principal = principal.add(stmt.getPrincipal());
		}
		return new Calculators.loan.Statement((new Calculators.type.Money(0.0)).rounded(), interest.rounded(), principal.rounded());
	}
	
	this.toString = function() {
		var sb = "";
		var end = this.nper + 1;
		for (var i = 0; i < end; ++i) {
			var stmt = this.getMonthlyStatement(i);
			sb = sb + "month " + i + " " + stmt.getPayment().formatted() + " " + stmt + "\n";

			if (i != 0 && i % this.PAYMENTS_PER_YEAR == 0) {
				var year = 1 / this.PAYMENTS_PER_YEAR;
				sb = sb + "  Year " + year + "     " + this.getYearlyStatement(year) + "\n";
			}
		}
		sb = sb + "Totals: " + this.getTotalStatement();
		
		return sb;
	}
}

