/*
 (C) Copyright 2000 Chartered Trust plc
	All rights reserved.

 
 File: rules.js

 Javascript rules for simple offline quoting mechanism
 
 Author: Andy Pearce & Adam Fraser

 Date : January 2000

*/


function raisePower(x,y) {   
	return Math.pow(x,y)
}

// round to two decimal places (nearest penny)
function round( num ) {
	return ( Math.round(num * 100 ) ) / 100;
}

// round to twelve decimal places (nearest 10th of a penny!)
function round12( num ) {
	return ( Math.round(num * 1000000000000 ) ) / 1000000000000 ;
}

// round to fifteen decimal places (nearest 13th of a penny!)
function round15( num ) {
	return ( Math.round(num * 1000000000000000 ) ) / 1000000000000000 ;
}

//Calculate the deposit amount given target instalment, rate, term cashprice and part exchange
function RollbackDeposit( inst, rate, term, price, partex) {
	return ((price - (( inst * term) / (1 + ((term * rate) / 1200))) - partex));
}

//Calculate the Cash Price affordable given target instalment, rate, term ,part exchange and deposit
function RollbackCashPrice( inst, rate, term, deposit, partex) {
	return ((( inst * term) / (1 + ((term * rate) / 1200)) + deposit + partex));
}

//Calculate the rate amount given target instalment, term, cashprice and deposit
function RollbackRate ( inst, term, adv) {
	return (((inst * term) - adv) * 1200) / (term * adv);
}

//Calculate the term given target instalment, rate, term cashprice and deposit
function RollbackTerm ( inst, rate, adv) {
	return ((adv / (inst - ((adv * rate) / 1200))) +2);
	
}


//Function to calculate the advance amount
function calcAdvance(cp,cd,pe) {

var advance
var totaldeposit
	if ( ( cp ) == 0 ) {
				
		advance = 0;
		totaldeposit = 0;	
		
	} 	else {
		totaldeposit = round( cd + pe );
		advance = cp - totaldeposit;
	}
return advance;
}

//function to calculate the standard monthly instalment (NB Chartered Trust use an equalised payment system)
function calcPayment(term, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission) {

var instalment = 0;
//rate =  Val(rate) / 100;
var yield = rate;
var uterm;
var lterm;
var powercalc;
var powercalcplus;
var powercalclow;
var plusyield;
var insuranceelement = 0;
var charges1;
var finalinst;
	
uterm = term -1;
lterm = term -2;

if ( rate == 0 ) {
	charges1 = 0;
	of = 0;
	af = 0;
} else {
	charges1 = round( ( adv * rate * term ) /1200 );
	if (deferred != 0) {
		if (rate < 12) {
			rate = 10.25;
		}
	}
	yield = rate /(12 * 100);
	plusyield = yield + 1;
}


if (deferred == 0) {
	//HP Calculation rules

	// Calculate inurance element
	if (PPP == 3) {
		insuranceelement = 0;
	} else {
		insuranceelement = getDealerCommissionLevel(PPP, term, PPPScheme);
	}
	
	if ( charges1 == 0 ) {
		// If interest free deal then simply divide advance by term
		instalment = round(adv / term);
		finalinst = instalment + of;
		document.quote.APR.value = 0;
		instalment = instalment + ((instalment * insuranceelement) / 100);
	} else {
		// interest bearing flat rate calculation
		instalment = round( ((adv + af) + ((adv + af) *rate * term)/1200) / term );
		finalinst = instalment + of;
		document.quote.APR.value =  findAPR( adv, instalment, finalinst, term );
		instalment = instalment + ((instalment * insuranceelement) / 100);
	}
	//For equalised calculation, if normal instalment is less than acceptance fee, make first instalment the acceptance fee
	if ( instalment < af ) {		
		instalment = round(( (instalment * term) - af)/ (term -1));
	}

} else {
	// Balloon Payment equalised calculation
	if (PPP == 3) {
		insuranceelement = 0;
	} else {
		insuranceelement = getDealerCommissionLevel(PPP, term -1, PPPScheme);
	}

	//for balloon payment deals add in any commission amount specified by the dealer
	adv = adv + deferredcommission;

	powercalcplus = raisePower( plusyield, term);
	powercalc = raisePower( plusyield, uterm);
	instalment = ((adv + af) - (round12(deferred / powercalcplus)))/ ((round15(powercalc - 1)) / (round15(powercalc * yield))) ; 
	finalinst = deferred + of;
	if ( rate == 0 ) {
		document.quote.APR.value = 0;
	} else {
//		alert("Advance=" + adv + ",instalment=" + instalment + ",final Instalment=" + finalinst +  ",term =" + term);
		document.quote.APR.value =  findAPR( adv  - deferredcommission, instalment, finalinst, term );
	}

	instalment = instalment + ((instalment * insuranceelement) / 100);
	
	//For equalised calculation, if normal instalment is less than acceptance fee, make first instalment the acceptance fee
	if ( instalment < af ) {
		powercalclow = raisePower( plusyield, lterm);
		instalment = ((adv + ((af * yield) / plusyield)) - (round12(deferred / powercalcplus)))/ ((round15(powercalclow - 1)) / (round15(powercalc * yield))) ; 
		finalinst = deferred + of;
			if ( rate == 0 ) {
				document.quote.APR.value = 0;
			} else {
				document.quote.APR.value =  findAPR( adv - deferredcommission, instalment, finalinst, term );
			}
	instalment = instalment + ((instalment * insuranceelement) / 100);
	
	}
	
	
	}

	instalment = round(instalment);
	return instalment;

}


// Function to calculate the initial instalment
function calcFirstPayment(inst, af) {
var installment1
	
if (inst < af) {
	installment1 = af;
} else {
	installment1 = formatPositiveToTwoPlaces(round(inst));
}

return installment1;
}

//Function to calculate the final instalment
function calcFinalPayment(inst,of, deferred) {
var installmentfinal;

if (deferred == 0) {
	installmentfinal = formatPositiveToTwoPlaces(round(inst + of));
} else {
	installmentfinal = deferred + of;
}

return installmentfinal;
}	


function calculate() {
//read variables from HPQUOTE2.HTM 	
	var of = Number( document.quote.optionfee.value );
	var af = Number( document.quote.acceptfee.value );
	var deferred = Number( document.quote.Deferred.value);
	var term = Number( document.quote.term.value );
//Note the rate read in from the system is a flat rate for HP and a yield if there is a balloon payment
	var rate = document.quote.flatrate.value;
	var adv = Number( document.quote.advance.value);
	var inst = Number( document.quote.installment.value );
	var price = Number( document.quote.cashprice.value );
	var deposit = Number( document.quote.cashdeposit.value );
	var partex = Number( document.quote.partexchange.value );
	var PPP = Number( document.quote.selPPP.value);
	var PPPScheme = Val(document.quote.PPPScheme.value)/100;
	var GFV = Number( document.quote.GFVValue.value);
	var deferredcommission = Number( document.quote.DeferredScheme.value);
	var calcwhat;
	var i;
	var insuranceelement;
	var finalinst;
	var notregulated = 0;


rate =  Val(rate) / 100;


// Check through the radio button array seeing which one is checked and set value accordingly
	for ( i = 0; i < 4; i++ ) {
		if  (document.quote.chkRecalcType[i].checked == true ) {
			calcwhat = document.quote.chkRecalcType[i].value;
		}
	}
	document.quote.advance.value = calcAdvance(price, deposit, partex);
	adv = Number( document.quote.advance.value);


//if cashprice radiobutton perform this calc
	if (calcwhat == 'C' )  {
	if (deferred !== 0) {
		alert("You cannot perform rollback for deals with a deferred payment");
		document.quote.chkRecalcType[3].checked = true;	
		return false;
	}
		if (Number(document.quote.selPPP.value) == 3) {
			document.quote.cashprice.value  = Math.ceil(RollbackCashPrice (inst, rate, term , deposit, partex));
		} else {
			insuranceelement = getDealerCommissionLevel(PPP, term, PPPScheme);
			document.quote.cashprice.value  = Math.ceil(RollbackCashPrice ((inst - ((inst * insuranceelement) / 100)), rate, term , deposit, partex));
		}
	}

//if deposit radiobutton perform this calc
	if (calcwhat == "D")  {
	if (deferred !== 0) {
		alert("You cannot perform rollback for deals with a deferred payment");
		document.quote.chkRecalcType[3].checked = true;	
		return false;
	}	
		if (Number(document.quote.selPPP.value) == 3) {
			document.quote.cashdeposit.value  = Math.ceil(RollbackDeposit (inst, rate, term , price, partex));
		} else {
			insuranceelement = getDealerCommissionLevel(PPP, term, PPPScheme);
			document.quote.cashdeposit.value  = Math.ceil(RollbackDeposit ((inst - ((inst * insuranceelement) / 100)), rate, term , price, partex));
		}
	}

//ensure the correct values are in the variables after any possible rollbacks
	price = Number( document.quote.cashprice.value );
	deposit = Number( document.quote.cashdeposit.value );
	partex = Number( document.quote.partexchange.value );
	document.quote.advance.value = calcAdvance(price, deposit, partex);

//if period radiobutton perform this calc
	if (calcwhat == 'P')  {
	if (deferred !== 0) {
		alert("You cannot perform rollback for deals with a deferred payment");
		document.quote.chkRecalcType[3].checked = true;	
		return false;
	}

		if (Number(document.quote.selPPP.value) == 3) {
			document.quote.term.value  = Math.floor(RollbackTerm (inst, rate, adv));
			term = Number( document.quote.term.value );
		} else {
			insuranceelement = getDealerCommissionLevel(PPP, term, PPPScheme);
			document.quote.term.value  = Math.floor(RollbackTerm ((inst - ((inst * insuranceelement) / 100)), rate, adv));
		}
	}
document.quote.chkRecalcType[3].checked = true;

// If there is a balloon payment then perform the following checks;
if (deferred !== 0) {	

	if (rate ==0) {
		alert("This system will not calculate interest free with a deferred payment!");
	}

	if (deferred > adv) {
		alert("You cannot a deferred value greater than the amount to finance. Please check your figures");
		document.quote.Deferred.value = adv;
	}


//	if (Number(document.quote.selPPP.value) != 3) {
//		alert("You cannot quote Insurance for deals with a deferred value");
//		document.quote.selPPP.value = 3;
//	}

	if (((GFV < deferred)) && (GFV != 0)) {
		alert("The deferred amount cannot be greater than the GFV");
		document.quote.Deferred.value = document.quote.GFVValue.value;
	}

	if ((term != 25) && (term != 37) && (GFV !=0)) {
		alert("The term can only be 25 or 37 months with a deferred payment deal");
		document.quote.term.value = 37;
		term = 37;
	}
	
}

//ensure all variables are correct
deferred = Number( document.quote.Deferred.value);
term = Number( document.quote.term.value );
inst = Number( document.quote.installment.value );
price = Number( document.quote.cashprice.value );
deposit = Number( document.quote.cashdeposit.value );
PPP = Number( document.quote.selPPP.value);
PPPScheme = Val(document.quote.PPPScheme.value)/100;
document.quote.advance.value = formatPositiveToTwoPlaces(calcAdvance(price, deposit, partex));
adv = Number( document.quote.advance.value);

//if not regulated then add VAT onto option to purchase fee
if (adv > 25000) {
	of = of + ((of * 17.5) /100);
	PPPScheme = 20;
	document.quote.PPPScheme.value = "A2000T";
	notregulated = 1;
}

//if interest fee deal then remove all fees
if ( rate == 0 ) {
	of = 0;
	af = 0;
}

// Checks to see if the calculation has a balloon - if so calculate only 25 and 37 otherwise 12,24,36,48,60
if (GFV != 0) {	
	document.quote.pay24.value = formatPositiveToTwoPlaces(calcPayment(25, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay36.value = formatPositiveToTwoPlaces(calcPayment(37, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay12.value = "N/A";
	document.quote.pay48.value = "N/A";
	document.quote.pay60.value = "N/A";
} else {
	
	document.quote.pay12.value = formatPositiveToTwoPlaces(calcPayment(12, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay24.value = formatPositiveToTwoPlaces(calcPayment(24, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay36.value = formatPositiveToTwoPlaces(calcPayment(36, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay48.value = formatPositiveToTwoPlaces(calcPayment(42, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
	document.quote.pay60.value = formatPositiveToTwoPlaces(calcPayment(48, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission));
}
	
document.quote.installment.value = formatPositiveToTwoPlaces(calcPayment(term, rate, of, af, adv, deferred, PPP, PPPScheme, GFV, deferredcommission)); 

inst = Number( document.quote.installment.value );
document.quote.installment1.value = formatPositiveToTwoPlaces(calcFirstPayment(inst,af));
finalinst = formatPositiveToTwoPlaces(calcFinalPayment(inst, of, deferred));


document.quote.installmentfinal.value = formatPositiveToTwoPlaces(finalinst);

//Calculate total amount payable:
document.quote.total_payable.value = formatPositiveToTwoPlaces((parseFloat(document.quote.installment.value) * parseFloat(document.quote.term.value)) + parseFloat(document.quote.optionfee.value) + parseFloat(document.quote.acceptfee.value));

//Write monthly payment to top box:
document.getElementById('dispfinancemonth').innerHTML = document.quote.installment.value;

//Blank APR should be displayed for deals Not regulated by CCA
if (notregulated == 1) {
	document.quote.APR.value = "";
	notregulated = 0
}

if ( document.quote.offline.value == "Y" )  {
	return true;
} else {
	return false
}		
}


function formatPositiveToTwoPlaces(x) {
	// If given a valid positive number, format it to two decimal places,
	// else return an empty string
	var strReturn;
	var iIntPart = getIntegerPart(x);
	var iDecPart = Math.round (100 * getDecimalPart(x));
	// if anything is negative or NaN return null string
	if ((iIntPart < 0) || (iDecPart < 0) || isNaN(iIntPart) || isNaN(iDecPart)){
		strReturn = '';
		return strReturn;
	}
	// so we must now have two positive integers to make the string
	// turn both parts into strings
	var strIntPart = iIntPart.toString();
	var strDecPart = iDecPart.toString();
	// if decimal part is zero make decimal string "00"
	if (strDecPart == "0") {strDecPart = '00';}
	// if decimal part is less than 10 give decimal string a leading "0"
	if (iDecPart > 0 & iDecPart < 10) {strDecPart = '0' + strDecPart;}
	strReturn = strIntPart + '.' + strDecPart;
	return strReturn;
}

function calcTotalFromAPR( MonthlyInstallment, FinalInstallment, Term, APR ) {
	var sum;
	
	sum = 0;
	
	for ( i = 1; i < (Term); i++ ) {
		sum = sum + MonthlyInstallment / (Math.pow( (1 + (APR / 100)), i / 12 ));
	}
	
	sum = sum + FinalInstallment / (Math.pow( (1 + (APR / 100) ), Term / 12 ));

	return sum;
}


// Search for correct APR for specifed increment with specified start...
function searchForAPR(Total, MonthlyInstallment, FinalInstallment, Term, Inc, Start ) {
	
	var oldAPR, APR;
	var oldDiff, diff;
	var TotalWithThisAPR;
	
	APR = Start;
	
	TotalWithThisAPR = calcTotalFromAPR( MonthlyInstallment, FinalInstallment, Term, APR );
	diff = Math.abs( TotalWithThisAPR - Total );
	
	oldAPR = APR;
	olddiff = diff + 1;
	
	while (olddiff > diff) {
		oldAPR = APR;
		olddiff = diff;
		APR = APR + Inc;
		TotalWithThisAPR = calcTotalFromAPR( MonthlyInstallment, FinalInstallment, Term, APR );
		diff = Math.abs( TotalWithThisAPR - Total );
	}	
	
	return APR - Inc;  // return the last but one we found.
}


// Main function
// Relatively inefficient way of calculation as should use a doubling of rate as the base to do search.
// This assumes that you can do downward searches as well as upwards though and my process may be more efficient 
// for low APRs - Adam Fraser, 30/03/2000
function findAPR( Total, MonthlyInstallment, FinalInstallment, Term ) {
	var APR;
	
	// Do iterative search with increment of 1
	APR = searchForAPR( Total, MonthlyInstallment, FinalInstallment, Term, 1, 0 );

	// Do iterative search with increment of -0.1
	APR = searchForAPR( Total, MonthlyInstallment, FinalInstallment, Term, -0.1, APR );
	
	// Do iterative search with increment of 0.1
	APR = searchForAPR( Total, MonthlyInstallment, FinalInstallment, Term, 0.1, APR );
	
	// Do iterative search with increment of -0.01 Just in case
	APR = searchForAPR( Total, MonthlyInstallment, FinalInstallment, Term, -0.01, APR );

	// Do iterative search with increment of 0.01 Just in case
	APR = searchForAPR( Total, MonthlyInstallment, FinalInstallment, Term, 0.01, APR );
	
	
	// Round to 1 decimal place
	APR = Math.round(APR * 10 ) / 10;
	
	// If APR is whole number make it a .9 for Chartered Trust
	if ( APR == Math.round( APR ) ) {
		APR = APR - 0.1;
	}
	return APR;
}
