//this is the constructor, accessor and mutator methods for AmortScheduleCalculator, 
//parsed from Java to JavaScript
//Note:  The calculate method is NOT overloaded

//constants
var MAX_TERM = 360;
var COLUMN_DELIMITER = "\t";
var ROW_DELIMITER = "\r\n";
var HEADER = "paymentNumber" + COLUMN_DELIMITER + "month" + COLUMN_DELIMITER + "year" + COLUMN_DELIMITER + "principal" + COLUMN_DELIMITER + "interest" + COLUMN_DELIMITER + "PMI" + COLUMN_DELIMITER + "monthlyPayment" + COLUMN_DELIMITER + "beginningBalance" + COLUMN_DELIMITER + "endingBalance" + COLUMN_DELIMITER + "interestRate" + COLUMN_DELIMITER + "marginRate" + COLUMN_DELIMITER + "prepaidPrincipal" + COLUMN_DELIMITER + "prepaidInterest" + COLUMN_DELIMITER + "prepaidBeginBalance" + COLUMN_DELIMITER + "prepaidEndBalance" + COLUMN_DELIMITER + "taxSavings" + COLUMN_DELIMITER;



/**
 * AmortScheduleCalculator calculates the expected Amortization Schedule for
 * the given loan parameters.
 *
 * @author Bill Buermeyer
 * Modified: 6/3/03, Buermeyer, Added support for Balloons, Interest Only, and ARM rounding
 * Modified: 1/26/2004, Buermeyer, Added support for blended rate programs, improved IO programs
 */
function AmortScheduleCalculator() {

	var returnObject = new Object();

	//Declare instance variables for result set
	returnObject.payCount = new Number(0);
	returnObject.paymentNumber = new Array(MAX_TERM);
	returnObject.month = new Array(MAX_TERM);
	returnObject.year = new Array(MAX_TERM);
	returnObject.principal = new Array(MAX_TERM);
	returnObject.interest = new Array(MAX_TERM);
	returnObject.beginningBalance = new Array(MAX_TERM);
	returnObject.endingBalance = new Array(MAX_TERM);
	returnObject.interestRate = new Array(MAX_TERM);
	returnObject.prepaidPrincipal = new Array(MAX_TERM);
	returnObject.prepaidInterest = new Array(MAX_TERM);
	returnObject.prepaidBeginBalance = new Array(MAX_TERM);
	returnObject.prepaidEndBalance = new Array(MAX_TERM);
	returnObject.taxSavings = new Array(MAX_TERM);
	returnObject.marginRate = new Array(MAX_TERM);
	returnObject.PMI = new Array(MAX_TERM);
	returnObject.monthlyPayment = new Array(MAX_TERM);
	returnObject.totalTaxSavings = new Number();
	returnObject.totalNormalPrincipal = new Number();
	returnObject.totalPrepaidSavings = new Number();
	returnObject.totalNormalInterest = new Number();
	returnObject.totalPMI = new Number();

	returnObject.className = "AmortScheduleCalculator";


	//methods
	returnObject.calculate = AmortScheduleCalculatorCalculate;
	returnObject.getARMPayments = getARMPayments;
	returnObject.checkIndexRange = checkIndexRange;
	returnObject.getPaymentCount = getPaymentCount;
	returnObject.getMonth = getMonth;
	returnObject.getYear = getYear;
	returnObject.getPrincipal = getPrincipal;
	returnObject.getInterest = getInterest;
	returnObject.getMonthlyPayment = getMonthlyPayment;
	returnObject.getInterestRate = getInterestRate;
	returnObject.getBeginningBalance = getBeginningBalance;
	returnObject.getEndingBalance = getEndingBalance;
	returnObject.getPrepaidPrincipal = getPrepaidPrincipal;
	returnObject.getPrepaidInterest = getPrepaidInterest;
	returnObject.getPrepaidBeginBalance = getPrepaidBeginBalance;
	returnObject.getPrepaidEndBalance = getPrepaidEndBalance;
	returnObject.getTotalNormalPrincipal = getTotalNormalPrincipal;
	returnObject.getTotalNormalInterest = getTotalNormalInterest;
	returnObject.getTaxSavings = getTaxSavings;
	returnObject.getMarginRate = getMarginRate;
	returnObject.getTotalOwnershipSavings = getTotalOwnershipSavings;
	returnObject.getTotalPMI = getTotalPMI;
	returnObject.getMonthlyPayments = getMonthlyPayments;
	returnObject.getTotalTaxSavings = getTotalTaxSavings;
	returnObject.getTotalPrepaidSavings = getTotalPrepaidSavings;
	returnObject.getPMI = getPMI;
	returnObject.getPaymentNumber = getPaymentNumber;
	returnObject.getHeader = getHeader;  
	returnObject.getSchedule = getSchedule;
	returnObject.round = round;
	
	
	return returnObject;

}

/**
 * The calculate() method takes the loan parameters and returns an Amortization Schedule.
 *
 * @param totalLoanAmt          Total Loan Amount
 * @param propertyValue         Sales Price or Property Value
 * @param startingInterestRate  Starting interest rate
 * @param startingMonth         Starting month on schedule (0 or 1-12) (Optional)
 * @param startingYear          Starting Year on schedule (0 or 1999-2200) (Optional)
 * @param periodStart           Starting month of schedule range (0-360) (Optional)
 * @param periodEnd             Ending month of schedule range (Optional)
 * @param monthlyPrepaidAmt     Monthly prepaid amount (Optional)
 * @param yearlyPrepaidAmt      Yearly prepaid amount (Optional)
 * @param lumpSumPrepaidAmt     One lump sum prepaid amount (Optional)
 * @param lumpSumPrepaidMonth   Month when one lump sum prepaid occurs (Optional)
 * @param federalTaxRate        Federal tax rate (Optional)
 * @param monthlyMI             Monthly mortgage insurance
 * @param isARM                 TRUE if ARM program, FALSE otherwise
 * @param scheduleTerm          Scheduled Loan Term, or desired Payoff Term (in months)
 * @param actualTerm            Actual Loan Term (in months)
 * @param firstTerm             Length of First Term (ARM only)
 * @param recurringTerm         Recurring Term (ARM only)
 * @param rateCap1              First Rate Cap (ARM only)
 * @param rateCap2              Second Rate Cap (ARM only)
 * @param marginRate            Margin rate (ARM only)
 * @param indexRate             Market Index Rate (ARM only)
 * @param secondTerm            Length of Second Term, if any (ARM only)
 * @param interestOnlyTerm      Length of any Interest Only period
 * @param amortMarginRate       Margin rate after the interest only period is over (Blended Rates only)
 * @param blendedRateIndexRate  Percentage of the index to use (normally 50%) (Blended Rates only)
 * @exception MortgageFamilyException
 */
function AmortScheduleCalculatorCalculate(totalLoanAmt, propertyValue, startingInterestRate, startingMonth, startingYear, periodStart, periodEnd, monthlyPrepaidAmt, yearlyPrepaidAmt, lumpSumPrepaidAmt, lumpSumPrepaidMonth, federalTaxRate, monthlyMI, isARM, scheduleTerm, actualTerm, firstTerm, recurringTerm, rateCap1, rateCap2, marginRate, indexRate, secondTerm, interestOnlyTerm, amortMarginRate, blendedRateIndexRate) {

//Initialize local variables
	var payCounter = 0;
	var isBalloon = false;
	var prepaying = false;
	var normalBeginningBalance = 0.0;
	var prepaidBeginningBalance = 0.0;
	var normalEndingBalance = 0.0;
	var prepaidEndingBalance = 0.0;
	var normalInterest = 0.0;
	var prepaidInterest = 0.0;
	var normalPrincipal = 0.0;
	var prepaidPrincipal = 0.0;
	var normalPI = 0.0;
	var monthlyPI = 0.0;
	var currMonthPMI = 0.0;
	var LTV = 0.0;
	var monthlyRate = 0.0;
	var taxSavings = 0.0;
	var tempMonthlyRate = 0.0;
	var tempMarginRate = 0.0;
	var currentAmt = 0.0;
	var totalPaymentsCheck = 0.0;
	var totalPayments = 0.0;
	var currentMonth = startingMonth;
	var currentYear = startingYear;

	var amortSchedule = new AmortScheduleCalculator();

	if (isARM == false && scheduleTerm > actualTerm) {
		isBalloon = true;
	}

	//Set actual term to schedule term, if not passed in
	if (actualTerm == 0)
		actualTerm = scheduleTerm;

	// Automatically correct the ending month if it is calculated to be greater than the loan term
	if ((periodEnd > 0) && (periodEnd > actualTerm)) {
		periodEnd = actualTerm;
	}

	//Check for prepay information
	prepaying = (lumpSumPrepaidAmt + yearlyPrepaidAmt + monthlyPrepaidAmt) > 0;

	//DETERMINE THE MONTHLY P&I PAYMENTS
	monthlyRate = (startingInterestRate / 12);

	//Check for an Adjustable Program
	var armPymts = "";
	if (isARM == true) {
		//Get the variable monthly payments
		armPymts = getARMPayments(totalLoanAmt, startingInterestRate, scheduleTerm, firstTerm, recurringTerm, rateCap1, rateCap2, marginRate, indexRate, secondTerm, interestOnlyTerm, amortMarginRate, blendedRateIndexRate);
	} else {
		//It is a fixed interestRate
		//Get the fixed monthly payments
		if (interestOnlyTerm > 0) {
			monthlyPI = totalLoanAmt * startingInterestRate/12;
		} else {
			var tempMonthly = new MonthlyPayment();
			monthlyPI = tempMonthly.calculate(scheduleTerm,startingInterestRate,totalLoanAmt);
		}
	}

	//Loop through each payment to create the AmortScheduleCalculator
	for (paymentNumber = 1; paymentNumber <= actualTerm; paymentNumber++) {
		if (paymentNumber == 1) {
			normalBeginningBalance = totalLoanAmt;
			prepaidBeginningBalance = totalLoanAmt;
			
			var today = new Date();

			//If we have a propertyValue add PMI to the monthly payment
			if ((propertyValue > 0) && (totalLoanAmt > 0) && (monthlyMI > 0)) {
				//Only calculate PMI if we get a propertyValue and PMI
				LTV = totalLoanAmt / propertyValue;
				currMonthPMI = (LTV > 0.80) ? monthlyMI : 0.0;
			}

			if (currentMonth == 0) {
				// Get the current month
				currentMonth = today.getMonth() + 1;
			}

			if (currentYear == 0) {
				// Get the current year
				currentYear = today.getFullYear();
			}
		} else {
			normalBeginningBalance = normalEndingBalance;
			prepaidBeginningBalance = prepaidEndingBalance;

			if ((propertyValue > 0) && (normalBeginningBalance > 0) && (monthlyMI > 0)) {
				//Only calculate PMI if we get a propertyValue
				LTV = normalBeginningBalance / propertyValue;
				currMonthPMI = (LTV > 0.80) ? monthlyMI : 0.0;
			}

			currentMonth++;

			if (currentMonth > 12) {
				currentMonth = 1;
				currentYear++;
			}
		}

		//Check for fixed or variable
		if (isARM == true && armPymts != "") {
			var armPymt = new ARMPayment();
			//armPymt = armPymt.elementAt(paymentNumber-1);

			normalPrincipal = armPymt.principal;
			normalInterest = armPymt.interest;
			normalPI = normalInterest + normalInterest;
			tempMonthlyRate = armPymt.monthlyRate;
			tempMarginRate = armPymt.marginRate;
		} else {
			// fixed
			normalPI = monthlyPI;
			tempMonthlyRate = monthlyRate;
			normalInterest = normalBeginningBalance * tempMonthlyRate;
			normalPrincipal = normalPI - normalInterest;
		}

		if (Math.round(normalBeginningBalance) == 0) {
			normalInterest = 0;
			normalPrincipal = 0;
		} else {
			//Start calculating tax savings per month
			if (federalTaxRate > 0) {
				taxSavings = normalInterest * federalTaxRate;
			}

			if (Math.round(prepaidBeginningBalance) == 0) {
				prepaidInterest = 0;
				prepaidPrincipal = 0;
			} else {
				prepaidInterest = prepaidBeginningBalance * tempMonthlyRate;

				//Add any monthly Prepayment
				prepaidPrincipal = normalPI + monthlyPrepaidAmt - prepaidInterest;

				//Add any yearly Prepayment
				if (yearlyPrepaidAmt > 0 && paymentNumber % 12 == 0) {
					prepaidPrincipal = prepaidPrincipal + yearlyPrepaidAmt;
				}

				//Add any Lump Sum Payment
				if (lumpSumPrepaidMonth == paymentNumber) {
					prepaidPrincipal = prepaidPrincipal + lumpSumPrepaidAmt;
				}

				// Make sure we stop after the loan has been paid off
				if (prepaidPrincipal > prepaidBeginningBalance) {
					prepaidPrincipal = prepaidBeginningBalance;
				}
			}
		}

		normalEndingBalance = normalBeginningBalance - normalPrincipal;
		prepaidEndingBalance = prepaidBeginningBalance - prepaidPrincipal;

		if (Math.round(normalEndingBalance) == 0) {
			normalEndingBalance = 0;
		}

		if (Math.round(prepaidEndingBalance) == 0) {
			prepaidEndingBalance = 0;
		}

		if (prepaying == true) {
			prepaidPrincipal = 0;
			prepaidInterest = 0;
			prepaidBeginningBalance = 0;
			prepaidEndingBalance = 0;
		}

		//Only send back result set requested by user
		if ((paymentNumber < periodStart) || (paymentNumber > periodEnd)) {
			continue;
		} else {
			if (isBalloon && paymentNumber == actualTerm ) {
				// final payment
				normalPrincipal = totalLoanAmt - amortSchedule.totalNormalPrincipal;
				normalEndingBalance = 0;
			}

			currentAmt = round (normalPrincipal + normalInterest + currMonthPMI,0.01);
			totalPayments += normalPrincipal + normalInterest + currMonthPMI;

			//Build an object of named arrays as the result
			amortSchedule.paymentNumber[payCounter] = paymentNumber;
			amortSchedule.month[payCounter] = currentMonth;
			amortSchedule.year[payCounter] = currentYear;
			amortSchedule.principal[payCounter] = normalPrincipal;
			amortSchedule.interest[payCounter] = normalInterest;
			amortSchedule.beginningBalance[payCounter] = normalBeginningBalance;
			amortSchedule.endingBalance[payCounter] = normalEndingBalance;
			amortSchedule.interestRate[payCounter] = tempMonthlyRate * 12;
			amortSchedule.marginRate[payCounter] = tempMarginRate;
			amortSchedule.prepaidPrincipal[payCounter] = prepaidPrincipal;
			amortSchedule.prepaidInterest[payCounter] = prepaidInterest;
			amortSchedule.prepaidBeginBalance[payCounter] = prepaidBeginningBalance;
			amortSchedule.prepaidEndBalance[payCounter] = prepaidEndingBalance;
			amortSchedule.taxSavings[payCounter] = taxSavings;
			amortSchedule.PMI[payCounter] = currMonthPMI;

			// Make sure the last payment is correct
			if (paymentNumber == actualTerm && !isBalloon && !(interestOnlyTerm > 0 && !isARM) && (totalPaymentsCheck + currentAmt != totalPayments)) {
				currentAmt = round (totalPayments - totalPaymentsCheck,0.01);
			}
			amortSchedule.monthlyPayment[payCounter] = currentAmt;

			totalPaymentsCheck += currentAmt;

			amortSchedule.totalTaxSavings += taxSavings;
			amortSchedule.totalNormalPrincipal += normalPrincipal;
			amortSchedule.totalNormalInterest += normalInterest;
			amortSchedule.totalPMI += currMonthPMI;

			amortSchedule.totalPrepaidSavings += (normalPrincipal + normalInterest) - (prepaidPrincipal + prepaidInterest);

			++payCounter;
		}
	}

	amortSchedule.payCount = actualTerm;

	return amortSchedule;
}

/**
* For ARMS only, this method uses the appropriate base Index,
* with Margins, Rate Caps, and other changes to determine the
* near-worst case rate increase.
*
* @param totalLoanAmt          Total Loan Amount
* @param initialRate		  	Starting interest rate
* @param loanTerm            Actual Loan Term (in months)
* @param firstTerm             Length of First Term (ARM only)
* @param recurringTerm         Recurring Term (ARM only)
* @param rateCap1              First Rate Cap (ARM only)
* @param rateCap2              Second Rate Cap (ARM only)
* @param marginRate            Margin rate (ARM only)
* @param indexRate             Market Index Rate (ARM only)
* @param secondTerm            Length of Second Term, if any (ARM only)
* @param interestOnlyTerm      Length of any Interest Only period
* @param amortMarginRate       Margin rate after the interest only period is over
* @param blendedRateIndexRate  Percentage of the index to use (normally 50%)
* @return Vector of monthly ARMPayment data
* @exception MortgageFamilyException
*/
function getARMPayments(totalLoanAmt, initialRate, loanTerm, firstTerm, recurringTerm, rateCap1, rateCap2, marginRate, indexRate, secondTerm, interestOnlyTerm, amortMarginRate, blendedRateIndexRate) {
	var currentChange=0;
	var currentPeriod=0;
	var remainingTerm=0;
	var totalChanges=0;
	var remainingBalance=0;
	var monthlyPI=0;
	var monthlyRate=0;
	var rateCap = 0;
	var interest = 0;
	var principal = 0;

	monthlyRate = initialRate/12;
	remainingBalance = totalLoanAmt;

	if (interestOnlyTerm > 0) {
		monthlyPI = totalLoanAmt * monthlyRate;
	} else {
		var tempMonthly = new MonthlyPayment();
		monthlyPI = tempMonthly.calculate(loanTerm,initialRate,remainingBalance);
	}

	var monthlyData = new Array();

	// First set of payments
	for(li=0;li < firstTerm; li++) {
		if (firstTerm < interestOnlyTerm) {
			interest = monthlyPI;
			principal = 0;
			remainingBalance = totalLoanAmt;
		} else {
			interest = remainingBalance * monthlyRate;
			principal = monthlyPI - interest;
			remainingBalance = remainingBalance - ( monthlyPI - ( (monthlyRate) * remainingBalance ) );
		}

		monthlyData[li] = new ARMPayment(principal, interest, monthlyRate, marginRate);
	}

	// Make adjustment for every subsequent change

	remainingTerm   = loanTerm - firstTerm; // ex. 360 - 36 = 324
	totalChanges    = remainingTerm / recurringTerm; // ex. 324 / 12 = 27
	currentPeriod   = firstTerm - 1; // ex. 36

	// Determine remaining payments
	for(currentChange=0; currentChange < totalChanges; currentChange++) {
		if (currentChange == 0 ) {
			rateCap = rateCap1;
		} else {
			rateCap = rateCap2;
		}

    		// Blended Rate Pricing at the time of the six-month adjustment (in Servicing
    		// equals ½ of six-month LIBOR index + initial margin. (WDB 2/2/2004)
    		if (blendedRateIndexRate > 0) {
			if (currentPeriod+1 < interestOnlyTerm) {
			    monthlyRate = (blendedRateIndexRate*indexRate + marginRate) / 12;
			} else {
			    marginRate = amortMarginRate;
			    monthlyRate = (indexRate + marginRate) / 12;
			}
		} else {
			monthlyRate = (monthlyRate * 12 + rateCap) / 12 ;
	
			// Cannot exceed the index + margin (as in the Spreadsheet)
			if (monthlyRate > ( indexRate + marginRate ) / 12 ) {
			    monthlyRate = ( indexRate + marginRate ) / 12;
			}
		}

		// Round rate to nearest 1/8th (WDB 5/26/03)
		monthlyRate = round (monthlyRate*12, 0.00125)/12;

		// Call the external function
		remainingTerm = loanTerm - currentPeriod - 1;

		// Recalculate the monthly payment if it can change (some programs)
    		if (blendedRateIndexRate > 0 && currentChange == 0) {
			// Recalculate the monthly payment after initial rate is over
			monthlyPI = totalLoanAmt * monthlyRate;
    		} else if (currentPeriod+1 >= interestOnlyTerm) {
			// Recalculate the monthly payment after the interest only period is over
			var tempMonthly = new MonthlyPayment();
			monthlyPI = tempMonthly.calculate(remainingTerm, monthlyRate*1200, remainingBalance);
		}

		// Second set of payments
		for (li=0; li < recurringTerm; li++) {
			currentPeriod = currentPeriod + 1;
	
			if (currentPeriod < interestOnlyTerm) {
				interest = monthlyPI;
				principal = 0;
				remainingBalance = totalLoanAmt;
			} else {
				interest = remainingBalance * monthlyRate;
				principal = monthlyPI - interest;
				remainingBalance = remainingBalance - ( monthlyPI - ( (monthlyRate) * remainingBalance ) );
			}
	
			monthlyData[li] = new ARMPayment (principal, interest, monthlyRate, marginRate);
	
			if( remainingBalance <= 0 ) {
				remainingBalance = 0;
				monthlyPI = 0;
			}
		}
	}

	return monthlyData;
}

/**
  * This method returns true if the index is greater than payCount
  * - 1, false otherwise
  */
function checkIndexRange(index) {
	if (index > (this.payCount - 1))
		return false;
	else
		return true;
}

/**
  * Returns the payment count for this calculator instance
  */
function getPaymentCount() {
	return (this.payCount);
}

/**
  * Returns the month number for the month at the given index
  * @param index the index into the month array
  * @return the month number for the month at the given index, or 0
  * if the index is out of range
  */
function getMonth(index) {
	if (this.checkIndexRange(index))
		return (month[index]);
	else
		return 0;
}

/**
  * Returns the year number for the year at the given index
  * @param index the index into the year array
  * @return the year number for the year at the given index, or 0
  * if the index is out of range
  */
function getYear(index) {
	if (this.checkIndexRange(index))
		return (year[index]);
	else
		return 0;
}

/**
  * Returns the principal amount for the period at the given index
  * @param index the index into the principal array
  * @return the principal amount for the period at the given
  * index, or 0 if the index is out of range
  */
function getPrincipal(index) {
	if (this.checkIndexRange(index))
		return (principal[index]);
	else
		return 0;
}

/**
  * Returns the interest amount for the period at the given index
  * @param index the index into the interest array
  * @return the interest amount for the period at the given
  * index, or 0 if the index is out of range
  */
function getInterest(index) {
	if (this.checkIndexRange(index))
		return (this.interest[index]);
	else
		return 0;
}

/**
  * Returns the monthly payment amount for the period at the given index
  * @param index the index into the interest array
  * @return the monthly payment amount for the period at the given
  * index, or 0 if the index is out of range
  */
function getMonthlyPayment(index) {
	if (this.checkIndexRange(index))
		return (this.monthlyPayment[index]);
	else
		return 0;
}

/**
  * Returns the interest rate for the period at the given index
  * @param index the index into the interest rate array
  * @return the interest rate for the period at the given
  * index, or 0 if the index is out of range
  */
function getInterestRate(index) {
	if (this.checkIndexRange(index))
		return (this.interestRate[index]);
	else
		return 0;
}

/**
  * Returns the beginning balance for the period at the given index
  * @param index the index into the beginning balance array
  * @return the beginning balance for the period at the given
  * index, or 0 if the index is out of range
  */
function getBeginningBalance(index) {
	if (this.checkIndexRange(index))
		return (this.beginningBalance[index]);
	else
		return 0;
}

/**
  * Returns the ending balance for the period at the given index
  * @param index the index into the ending balance array
  * @return the ending balance for the period at the given
  * index, or 0 if the index is out of range
  */
function getEndingBalance(index) {
	if (this.checkIndexRange(index))
		return (this.endingBalance[index]);
	else
		return 0;
}

/**
  * Returns the prepaid principal for the period at the given index
  * @param index the index into the prepaid principal array
  * @return the prepaid principal for the period at the given
  * index, or 0 if the index is out of range
  */
function getPrepaidPrincipal(index) {
	if (this.checkIndexRange(index))
		return (this.prepaidPrincipal[index]);
	else
		return 0;
}

/**
  * Returns the prepaid interest for the period at the given index
  * @param index the index into the prepaid interest array
  * @return the prepaid interest for the period at the given
  * index, or 0 if the index is out of range
  */
function getPrepaidInterest(index) {
	if (this.checkIndexRange(index))
		return (this.prepaidInterest[index]);
	else
		return 0;
}

/**
  * Returns the prepaid beginning balance for the period at the given index
  * @param index the index into the prepaid beginning balance array
  * @return the prepaid beginning balance for the period at the given
  * index, or 0 if the index is out of range
  */
function getPrepaidBeginBalance(index) {
	if (this.checkIndexRange(index))
		return (this.prepaidBeginBalance[index]);
	else
		return 0;
}

/**
  * Returns the prepaid ending balance for the period at the given index
  * @param index the index into the prepaid ending balance array
  * @return the prepaid ending balance for the period at the given
  * index, or 0 if the index is out of range
  */
function getPrepaidEndBalance(index) {
	if (this.checkIndexRange(index))
		return (this.prepaidEndBalance[index]);
	else
		return 0;
}

/**
  * Returns the total normal principal paid for this instance
  * @return the total normal principal paid for this instance
  */
function getTotalNormalPrincipal() {
	return (this.totalNormalPrincipal);

}

/**
  * Returns the total normal interest paid for this instance
  * @return the total normal interest paid for this instance
  */
function getTotalNormalInterest() {
	return (this.totalNormalInterest);
}

/**
  * Returns the tax savings for the period at the given index
  * @param index the index into the tax savings array
  * @return the tax savings for the period at the given
  * index, or 0 if the index is out of range
  */
function getTaxSavings(index) {
	if (this.checkIndexRange(index))
		return (this.taxSavings[index]);
	else
		return 0;
}

/**
* Returns the margin for the period at the given index (ARM only)
* @param index the index into the margin array
* @return the margin for the period at the given
* index, or 0 if the index is out of range
*/
function getMarginRate(index) {
	if (this.checkIndexRange(index))
		return (this.marginRate[index]);
	else
		return 0;
}

/**
  * Returns the total ownership savings for this calculator
  * @deprecated Not implemented yet.
  * @return the total ownership savings for this calculator
  */
function getTotalOwnershipSavings() {
	return 0; // not supported
}

/**
  * Returns the total PMI for this calculator
  * @return the total PMI for this calculator
  */
function getTotalPMI() {
	return (this.totalPMI);
}

/**
  * Returns the monthly payments schedule for this calculator
  * @return the monthly payments schedule for this calculator
  */
function getMonthlyPayments() {
	return (this.monthlyPayment);
}

/**
 * The getTotalTaxSavings() method returns the total tax benefit over
 * the schedule.
 */
function getTotalTaxSavings() {
	return (this.totalTaxSavings);
}

/**
 * Returns the total benefit of prepaying the mortgage over the schedule.
 * @return the total benefit of prepaying the mortgage over the schedule.
 */
function getTotalPrepaidSavings() {
	return (this.totalPrepaidSavings);
}

/**
  * Returns the PMI for the period at the given index
  * @param index the index into the PMI array
  * @return the PMI for the period at the given
  * index, or 0 if the index is out of range
  */
function getPMI(index) {
	if (this.checkIndexRange(index))
		return (this.PMI[index]);
	else
		return 0;
}

/**
  * Returns the payment number for the period at the given index
  * @param index the index into the payment number array
  * @return the payment number for the period at the given
  * index, or 0 if the index is out of range
  */
function getPaymentNumber(index) {
	if (this.checkIndexRange(index))
		return (this.paymentNumber[index]);
	else
		return 0;
}

/**
 * The getHeader() method returns the schedule order.
 */
function getHeader() {
	return HEADER;
}

/**
 * Returns the entire amortization schedule as one large, tab
 * delimited, newline separated string.  This string is of the form:
 *
 * <p>
 * <pre>
 * \tmonth\tyearprincipal\tinterest\tbeginningBalance\tendingBalance\tinterestRate\tprepaidPrincipal\tprepaidInterest\tprepaidBeginBalance\tprepaidEndBalance\ttaxSavings\tPMI\r\n
 * \tmonth\tyearprincipal\tinterest\tbeginningBalance\tendingBalance\tinterestRate\tprepaidPrincipal\tprepaidInterest\tprepaidBeginBalance\tprepaidEndBalance\ttaxSavings\tPMI\r\n
 * \tmonth\tyearprincipal\tinterest\tbeginningBalance\tendingBalance\tinterestRate\tprepaidPrincipal\tprepaidInterest\tprepaidBeginBalance\tprepaidEndBalance\ttaxSavings\tPMI\r\n
 * ...
 * </pre>
 * </p>
 *
 * @return amortization schedule as a tab delimited, newline
 * separated string
 */
function getSchedule() {
	if (payCount <= 0)
		return "";

	var tempBuffer = "";

	for (i = 0; i < payCount; i++) {
		tempBuffer + (this.paymentNumber[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.month[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.year[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.principal[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.interest[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.PMI[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.monthlyPayment[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.beginningBalance[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.endingBalance[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.interestRate[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.marginRate[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.prepaidPrincipal[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.prepaidInterest[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.prepaidBeginBalance[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.prepaidEndBalance[i]) + (COLUMN_DELIMITER);
		tempBuffer + (this.taxSavings[i]) + (COLUMN_DELIMITER) + (ROW_DELIMITER);
	}

	return tempBuffer;
}

/**
 * Round the value to the nearest desired accuracy.
 * (e.g. 1/8th)
 * Note: copied logic from lower_c.c in CA$H calcs.dll
 *
 * @param value the value to be rounded
 * @param nearest the nearest accuracy to be rounded to
 * @return the rounded value
 */
function round(value, nearest) {

	if (nearest == 0.01)
		return (Math.round (value * 100.00))/100.00;

	var rtn;
	value = value;
	nearest = nearest;
	var temp = value - Math.floor (value);
	var units = Math.floor (temp/nearest);
	if ((temp - nearest*units) >= (nearest*(units+1) - temp))
		rtn = Math.floor (value) + nearest*(units+1);
	else
		rtn = Math.floor (value) + nearest*units;

	return rtn;
}

// Inner class to hold monthly ARM data
function ARMPayment (principal, interest, monthlyRate, marginRate){
	var returnArmPaymentObject = new Object();

	returnArmPaymentObject.principal = principal;
	returnArmPaymentObject.interest = interest;
	returnArmPaymentObject.monthlyRate = monthlyRate;
	returnArmPaymentObject.marginRate = marginRate;
	
	return returnArmPaymentObject;
}
