function MortgageCalc() {
}
MortgageCalc.prototype = {
  ClassName: 'MortgageCalc',
  
  initialize: function(container, type, options)
  {
    if (!container) return;
    this.container = container;
    this.type = type;
    this.inputs = {};
    this.outputs = {};
    this.options = options || {};
    this.container.innerHTML = '';
    if (this.type == 'amortization' || this.type == 'biweekly_vs_monthly')
      this.createAmortizationCalc();
    else if (this.type == 'annuity')
      this.createAnnuityCalc();
  },

  createCalc: function()
  {
    var curRow = null, curCell = null;
    this.inputTable = document.createElement('table');
    this.inputTable.setAttribute('class', 'inputs');
    var tBody = document.createElement('tbody');
    this.inputTable.appendChild(tBody);
    for (var input in this.inputs)
      tBody.appendChild(this.createInputRow(this.inputs[input]));
    tBody.appendChild(this.createButtonRow());
    this.container.appendChild(this.inputTable);
    this.outputContainer = document.createElement('div');
    this.outputContainer.setAttribute('class', 'outputs');
    this.container.appendChild(this.outputContainer);
  },

  createAmortizationCalc: function()
  {
    this.inputs.amount = this.createTextInput('Loan Amount', 'amount', '100000', '8');
    this.inputs.term = this.createTextInput('Term (years)', 'term altrow', '30', '3');
    this.inputs.rate = this.createTextInput('Interest Rate (APR)', 'interest', '6.0', '5');
    this.createCalc();
  },

  createAnnuityCalc: function()
  {
    this.inputs.amount = this.createTextInput('Loan Amount', 'amount', '100000', '8');
    this.inputs.term = this.createTextInput('Term (years)', 'term altrow', '30', '3');
    this.inputs.rate = this.createTextInput('Quoted Interest Rate', 'interest1', '7.5', '5');
    this.inputs.rate1 = this.createTextInput('Quoted APR', 'interest2 altrow', '7.8', '5');
    this.createCalc();
  },

  createTextInput: function(name, className, value, length)
  {
    var input = document.createElement('input');
    input.setAttribute('type', 'text');
    if (className) input.setAttribute('class', className);
    if (name) input.setAttribute('name', name);
    if (value) input.setAttribute('value', value);
    if (length) input.setAttribute('length', length);
    return input;
  },

  createInputRow: function(input)
  {
    if (!input) return;
    var curRow = document.createElement('tr');
    var curCell = document.createElement('td');
    if (input.name) curCell.appendChild(document.createTextNode(input.name));
    curRow.appendChild(curCell);
    curCell = document.createElement('td');
    curCell.appendChild(input);
    curRow.appendChild(curCell);
    return curRow;
  },

  createButtonRow: function()
  {
    var curRow = document.createElement('tr');
    var curCell = document.createElement('td');
    curCell.setAttribute('colspan', '2');
    var curBut = document.createElement('button');
    curBut.appendChild(document.createTextNode('Calculate'));
    curBut.setAttribute('class', 'calculate');
    curBut.calc = this;
    if (curBut.addEventListener) curBut.addEventListener('click', this.calculate, false);
    else curBut.attachEvent('onclick', this.calculate);
    curCell.appendChild(curBut);
    curRow.appendChild(curCell);
    var curBut = document.createElement('button');
    curBut.appendChild(document.createTextNode('Clear'));
    curBut.setAttribute('class', 'reset');
    curBut.calc = this;
    if (curBut.addEventListener) curBut.addEventListener('click', this.hideOutput, false);
    else curBut.attachEvent('onclick', this.hideOutput);
    curCell.appendChild(curBut);
    curRow.appendChild(curCell);
    return curRow;
  },

  calculate: function(calc)
  {
    var calc = calc && calc.ClassName == 'MortgageCalc' ? calc : (this.calc ? this.calc : window.event.srcElement.calc);
    calc.outputContainer.innerHTML = '';
    var outResult = '';
    if (calc.type == 'amortization')
    {
      var result = MortgageCalc.getAmortization(
                    calc.inputs.amount.value,
                    calc.inputs.term.value,
                    calc.inputs.rate.value);
      if (calc.options.TableOut)
        outResult = MortgageCalc.getAmortizationTable(result);
      else
        outResult = MortgageCalc.getAmortizationText(result);
    }
    else if (calc.type == 'biweekly_vs_monthly')
    {
      var result = MortgageCalc.getAmortization(
                    calc.inputs.amount.value,
                    calc.inputs.term.value,
                    calc.inputs.rate.value);
      if (calc.options.TableOut)
        outResult = MortgageCalc.getComparisonTable(result);
      else
        outResult = MortgageCalc.getComparisonText(result);
    }
    else if (calc.type == 'annuity')
    {
      var result = MortgageCalc.getAnnuity(
                    calc.inputs.amount.value,
                    calc.inputs.term.value,
                    calc.inputs.rate.value,
                    calc.inputs.rate1.value)
      if (calc.options.TableOut)
        outResult = MortgageCalc.getAnnuityTable(result);
      else
        outResult = MortgageCalc.getAnnuityText(result);
    }

    if (calc.options.TableOut)
      calc.outputContainer.appendChild(outResult);
    else
    {
      var textarea = document.createElement('textarea');
      textarea.setAttribute('wrap', 'off');
      textarea.value = outResult;
      calc.outputContainer.appendChild(textarea);
    }
  },

  hideOutput: function(calc)
  {
    var calc = calc && calc.ClassName == 'MortgageCalc' ? calc : (this.calc ? this.calc : window.event.srcElement.calc);
    calc.outputContainer.innerHTML = '';
  },

  toggleOutputType: function(calc)
  {
    var calc = calc && calc.ClassName == 'MortgageCalc' ? calc : (this.calc ? this.calc : window.event.srcElement.calc);
    if (!calc.options.TableOut)
      calc.options.TableOut = true;
    else
      calc.options.TableOut = false;
    calc.calculate(calc);
  }

}


MortgageCalc.getSpaces = function(n)
{
  var i = 0;
  var sSpaces = "";
  for (i = 0; i < n; i++) { sSpaces += " "; }
  return sSpaces;
}

MortgageCalc.getMonthlyPayment = function(amount, term, rate)
{
  /* Calculates the monthly payment from annual percentage
  rate, term of loan in years and loan amount. **/
  var acc = 0;
  var base = 1 + (rate / 1200);
  for (i = 1; i <= term * 12; i++)
  {
    acc += Math.pow(base, -i);
  }
  return amount / acc;
}

MortgageCalc.getBiWeeklyPayment = function(amount, term, rate)
{
  /* Calculates the bi-weekly payment from annual percentage
  rate, term of loan in years and loan amount. **/
  var acc = 0;
  var base = 1 + (rate / 2600);
  for (i = 1; i <= term * 26; i++)
  {
    acc += Math.pow(base, -i);
  }
  return amount / acc;
}

MortgageCalc.getAnnuity = function(amount, term, rate1, rate2)
{
  var annuity = {
    Amount: amount,
    Term: term
  };
  annuity.Amortization1 = MortgageCalc.getAmortization(amount, term, rate1);
  annuity.Amortization2 = MortgageCalc.getAmortization(amount, term, rate2);
  annuity.Difference = MortgageCalc.getAmortization(amount, term, rate2 - rate1);
  //annuity.Costs = 
  return annuity;
}

MortgageCalc.getAmortization = function(amount, term, rate)
{
  amount *= 1; term *= 1; rate *= 1;
  var amortization = {
    Amount: amount,
    MonthlyPayments: [],
    BiWeeklyPayments: [],
    Rate: rate,
    Term: term,
    TotalInterest: 0,
    MonthlyPayment: 0,
    BiWeeklyPayment: 0
  };
  amortization.MonthlyPayment = MortgageCalc.getMonthlyPayment(amount, term, rate);
  var balance = amount;
  var interest = 0.0;
  var principal = 0.0;
  var i = 0;
  while (Math.round(balance) > 0)
  {
    interest = balance * rate / 1200;
    amortization.TotalInterest += interest;
    principal = amortization.MonthlyPayment - interest;
    balance -= principal;
    var payment = {
      Balance: balance,
      Interest: interest,
      Principal: principal,
      InterestToDate: amortization.TotalInterest
    };
    amortization.MonthlyPayments.push(payment);
    i++;
  }
  amortization.BiWeeklyPayment = amortization.MonthlyPayment / 2;
  balance = amount;
  interest = 0.0;
  principal = 0.0;
  i = 0;
  while (Math.round(balance) > 0)
  {
    interest = balance * rate / 2600;
    amortization.TotalInterest += interest;
    principal = amortization.BiWeeklyPayment - interest;
    balance -= principal;
    var payment = {
      Balance: balance,
      Interest: interest,
      Principal: principal,
      InterestToDate: amortization.TotalInterest
    };
    amortization.BiWeeklyPayments.push(payment);
    i++;
  }
  amortization.TotalInterest = amortization.TotalInterest;
  return amortization;
}

MortgageCalc.getAmortizationText = function(amortization)
{
  var sATline = "";
  function Pad(s) { s = s || '.'; return (s.length > 2) ? s : Pad(s + '0'); }
  function RoundCents(n) { return (new String(Math.round(n * 100) / 100).replace(/(\.\d*)?$/, Pad)); };
  var sCR = String.fromCharCode(13);
  var retVal  = "Principal        = $" + RoundCents(amortization.Amount) + sCR;
      retVal += "Payment          = $" + RoundCents(amortization.MonthlyPayment) + sCR;
      retVal += "APR              = " + RoundCents(amortization.Rate) + "%" + sCR;
      retVal += "Total Interest   = $" + RoundCents(amortization.TotalInterest) + sCR;
      retVal += "Total Loan Value = $" + RoundCents(amortization.TotalInterest + amortization.Amount) + sCR + sCR;
      retVal += "Pmt    Principal   Interest   Balance" + sCR;
  for (var i = 0; i < amortization.MonthlyPayments.length; i++)
  {
    sATline = (i + 1).toString() + MortgageCalc.getSpaces(7 - (i + 1).toString().length);
    sATline += '$'+RoundCents(amortization.MonthlyPayments[i].Principal);
    sATline += MortgageCalc.getSpaces(19 - sATline.length);
    sATline += '$'+RoundCents(amortization.MonthlyPayments[i].Interest);
    sATline += MortgageCalc.getSpaces(30 - sATline.length);
    sATline += '$'+RoundCents(amortization.MonthlyPayments[i].Balance);
    sATline += sCR;
    retVal += sATline;
  }
  return retVal;
}

MortgageCalc.createOutputRow = function(data, options)
{
  data = data.constructor.toString().indexOf("Array") >= 0 ? data : [data];
  options = options || {};
  var row, cell;
  var cellType = 'td';
  if (options.isCaption)
  {
    cell = document.createElement('caption');
    cell.innerHTML = data[0];
    return cell;
  }
  row = document.createElement('tr');
  if (options.rowClass) row.setAttribute('class', options.rowClass);
  if (options.isHeader) cellType = 'th';
  for (var i = 0; i < data.length; i++)
  {
    cell = document.createElement(cellType);
    if (options.colspan)
      cell.setAttribute('colspan', options.colspan);
    cell.innerHTML = data[i];
    row.appendChild(cell);
  }
  return row;
}

MortgageCalc.getAmortizationTable = function(amortization)
{
  function Pad(s) { s = s || '.'; return (s.length > 2) ? s : Pad(s + '0'); }
  function RoundCents(n) { return (new String(Math.round(n * 100) / 100).replace(/(\.\d*)?$/, Pad)); };

  var wrapper = document.createElement('div');
  var table = document.createElement('table');
  var tbody = document.createElement('tbody');
  var tr, th, td;

  table.appendChild(MortgageCalc.createOutputRow('Loan Overview', { isCaption: true }));
  tbody.appendChild(MortgageCalc.createOutputRow(['Principal', 'Payment', 'APR', 'Total Interest', 'Total Loan Value'], { isHeader: true }));
  tbody.appendChild(
    MortgageCalc.createOutputRow([
        '$' + RoundCents(amortization.Amount),
        '$' + RoundCents(amortization.MonthlyPayment),
        amortization.Rate + ' %',
        '$' + RoundCents(amortization.TotalInterest),
        '$' + RoundCents(amortization.TotalInterest + amortization.Amount)
    ]));
  table.appendChild(tbody);
  wrapper.appendChild(table);

  table = document.createElement('table');
  tbody = document.createElement('tbody');

  table.appendChild(MortgageCalc.createOutputRow('Amortization Table', { isCaption: true }));
  tbody.appendChild(MortgageCalc.createOutputRow(['Pmt', 'Principal', 'Interest', 'Balance'], { isHeader: true }));
  for (var i = 0; i < amortization.MonthlyPayments.length; i++)
    tbody.appendChild(
      MortgageCalc.createOutputRow([
          i + 1,
          '$' + RoundCents(amortization.MonthlyPayments[i].Principal),
          '$' + RoundCents(amortization.MonthlyPayments[i].Interest),
          '$' + RoundCents(amortization.MonthlyPayments[i].Balance)
      ], {rowClass: i%2==1 ? 'altrow' : ''}));
  table.appendChild(tbody);
  wrapper.appendChild(table);

  return wrapper;
}

MortgageCalc.getComparisonTable = function(amortization)
{
  function Pad(s) { s = s || '.'; return (s.length > 2) ? s : Pad(s + '0'); }
  function RoundCents(n) { return (new String(Math.round(n * 100) / 100).replace(/(\.\d*)?$/, Pad)); };

  var wrapper = document.createElement('div');
  var table = document.createElement('table');
  var tbody = document.createElement('tbody');
  var tr, th, td;

  table.appendChild(MortgageCalc.createOutputRow('You Entered:', { isCaption: true }));
  tbody.appendChild(MortgageCalc.createOutputRow(['Principal:', '$' + RoundCents(amortization.Amount)]));
  tbody.appendChild(MortgageCalc.createOutputRow(['Interest Rate:', amortization.Rate + ' %']));
  tbody.appendChild(MortgageCalc.createOutputRow(['Term:', amortization.Term + ' years']));
  table.appendChild(tbody);
  wrapper.appendChild(table);

  table = document.createElement('table');
  tbody = document.createElement('tbody');

  table.appendChild(MortgageCalc.createOutputRow('Results:', { isCaption: true }));
  tbody.appendChild(MortgageCalc.createOutputRow(['Year', 'Standard Plan Balance', 'Bi-Weekly Plan Balance'], { isHeader: true }));
  for (var i = 1; i <= amortization.Term; i++)
    tbody.appendChild(
      MortgageCalc.createOutputRow([
          i,
          '$' + RoundCents(amortization.MonthlyPayments[(i*12)-1].Balance),
          '$' + (amortization.BiWeeklyPayments[(i*26)-1] ? RoundCents(amortization.BiWeeklyPayments[(i*26)-1].Balance) : '0.00')
      ], {rowClass: i%2==1 ? 'altrow' : ''}));
  table.appendChild(tbody);
  wrapper.appendChild(table);

  return wrapper;
}

MortgageCalc.getComparisonText = function(amortization)
{
  var sATline = "";
  function Pad(s) { s = s || '.'; return (s.length > 2) ? s : Pad(s + '0'); }
  function RoundCents(n) { return (new String(Math.round(n * 100) / 100).replace(/(\.\d*)?$/, Pad)); };
  var sCR = String.fromCharCode(13);
  var retVal = "Monthly Payment = " + RoundCents(amortization.MonthlyPayment) + sCR;
  retVal += "Total Payment = " + RoundCents(amortization.TotalInterest + amortization.Amount) + sCR + sCR;
  retVal += "Year   Monthly Plan Balance    Bi-Weekly Plan Balance" + sCR;
  for (var i = 1; i <= amortization.Term; i++)
  {
    sATline = (i).toString() + MortgageCalc.getSpaces(7 - (i).toString().length);
    sATline += '$'+(amortization.MonthlyPayments[(i*12)-1] ? RoundCents(amortization.MonthlyPayments[(i*12)-1].Balance) : '0.00');
    sATline += MortgageCalc.getSpaces(31 - sATline.length);
    sATline += '$'+(amortization.BiWeeklyPayments[(i*26)-1] ? RoundCents(amortization.BiWeeklyPayments[(i*26)-1].Balance) : '0.00');
    sATline += sCR;
    retVal += sATline;
  }
  return retVal;
}

MortgageCalc.getAnnuityTable = function(annuity)
{
  var wrapper = document.createElement('div');
  wrapper.innerHTML = MortgageCalc.getAnnuityText(annuity).replace(/\r/g, '<br />');
  return wrapper;
}

MortgageCalc.getAnnuityText = function(annuity)
{
  var sATline = "";
  function Pad(s) { s = s || '.'; return (s.length > 2) ? s : Pad(s + '0'); }
  function RoundCents(n) { return (new String(Math.round(n * 100) / 100).replace(/(\.\d*)?$/, Pad)); };
  var sCR = String.fromCharCode(13);
  var retVal = "$" + annuity.Amount + " loan at " + annuity.Amortization1.Rate + " of " + annuity.Term + " Months." + sCR;
  retVal += "Principal & Interest Monthly Payment: $" + RoundCents(annuity.Amortization1.MonthlyPayment) + sCR;
  retVal += "But at " + annuity.Amortization2.Rate + "% that means a loan of: $" + RoundCents(annuity.Amount - annuity.Difference.TotalInterest) + sCR;
  retVal += "So your upfront costs are: $" + RoundCents(annuity.Difference.TotalInterest) + sCR;
  return retVal;
}

MortgageCalc.getTypeAsTitle = function(type)
{
  var title = '';
  if (type == 'amortization')
    title = 'Amortization';
  else if (type == 'biweekly_vs_monthly')
    title = 'Bi-Weekly vs Monthly';
  else if (type == 'annuity')
    title = 'Annuity';
  return title;
}

