/* ------------------------------------------------------------------
    Filename:        calculator.js
    Author:          AKQA
    Description:     Savings Calculator using mootools.js
  -------------------------------------------------------------------
 */

// calculator fields
sf.calcObj = {
	init: function(){
	
		// form inputs
		this.$savingsProduct = $('savingsProduct');
		this.$targetAmount = $('targetAmount');
		this.$initialDeposit = $('initialDeposit');
		this.$monthlyPayment = $('monthlyPayment');
		this.$years = $('years');
		this.$months = $('months');
		
		// container divs
		this.$calculationType = $('calculationType');
		this.$calcInputs = $('calcInputs');
		this.$target = $('target');
		this.$deposit = $('deposit');
		this.$goal = $('goal');
		this.$monthly = $('monthly');
		this.$monthlyLbl = $('monthlyLbl');
		this.$period = $('period');
		this.$periodLbl = $('periodLbl');
		this.$calculator = $('calculator');
		this.$calcButton = $('calcButton');

		
	}
}

sf.calcInputs = {
    accountName: function(){
        return sf.calcObj.$savingsProduct.options[sf.calcObj.$savingsProduct.selectedIndex].innerHTML
    },
	accountId : function(){
		return sf.calcObj.$savingsProduct.value
	},
    target: function(){
        return sf.calcObj.$targetAmount.value
    },
	setTarget: function(value) {
		sf.calcObj.$targetAmount.value = value;
	},
    initialDeposit: function(){
        return sf.calcObj.$initialDeposit.value
    },
	setInitialDeposit: function(value) {
		sf.calcObj.$initialDeposit.value = value;
	},
	monthlyPayment: function(){
	    return sf.calcObj.$monthlyPayment.value
	},
	setMonthlyPayment: function(value) {
		sf.calcObj.$monthlyPayment.value = value;
	},
	years: function(){
	    return sf.calcObj.$years.options[sf.calcObj.$years.selectedIndex].value
	},
	setYears: function(value) {
		sf.calcObj.$years.selectedIndex = value;
	},
	months: function(){
	    return sf.calcObj.$months.options[sf.calcObj.$months.selectedIndex].value
	},
	setMonths: function(value) {
		sf.calcObj.$months.selectedIndex = value;
	}
};

// error messages
sf.calcErrorMessages = {
    invalidTextField: 'Please make sure you enter a number &amp; that it contains no spaces or commas.',
    invalidTarget: 'Please enter a valid target amount (&pound;1 - &pound;2,000,000).',
    invalidDeposit: 'Please ensure your initial deposit is less than your target amount.',
    invalidPayment: 'Please enter a valid monthly payment (&pound;1 - &pound;100,000).',
    invalidSavingPeriod: 'Please select a valid number of months to save for.',
    invalidProduct: 'Please select a valid product.'
};

// runs when the page has completed loading
window.addEvent('domready', function() {
    sf.savingsCalc.init();
});

sf.savingsCalc = function(){
	var xmlDoc;
	var oldCalculation;
	var _this = {
		init : function(){ // Load xml and initialize fields and events
            var xmlSource = '/library/default/xml/accounts.xml';

			var xmlRequest = new XHR({
				method: 'get',
				onSuccess: function(){
					xmlDoc = this.response.xml.documentElement;
					sf.calcObj.init();
					sf.savingsCalc.setProducts();
					sf.savingsCalc.setYears();
					sf.savingsCalc.setMonths();
					sf.savingsCalc.attachEvents();
					sf.savingsCalc.setCalc('totalamount');
				}}).send(xmlSource, null);
			
        },
        
		resetMenu : function() { //Add the class "on" on the active tab
			sf.calcObj.$calculationType.getElements('a').removeClass('on');
		},
		
		attachEvents : function() {

	        $('goalKind').addEvent('change', function() { //display fields
		        sf.savingsCalc.setCalc(this.options[this.options.selectedIndex].value);
	        });
			
			sf.calcObj.$calculationType.getElements('a').addEvent('click', function(e) {
				e = new Event(e).stop();
				sf.savingsCalc.setCalc(e.target.getProperty('rel'));
			});

		},
		
        setProducts : function() { //Creates options in product dropdown
			var accounts = xmlDoc.getElementsByTagName('account');

		var defaultOpt = new Element('option', {'value': "select"});
		defaultOpt.innerHTML = "Select product...";
		defaultOpt.injectInside(sf.calcObj.$savingsProduct);

            for (i=0; i <= (accounts.length -1); i++) 
            {
                var account = accounts[i];
                var option = new Element('option', {'value': account.getAttribute('id')});
                option.innerHTML = account.getAttribute('name');
                option.injectInside(sf.calcObj.$savingsProduct);
            }
            sf.calcObj.$savingsProduct.addEvent('change', function() {
                sf.savingsCalc.clearResults();
            });
        },
        
        setMonths : function() { //Creates options in years dropdown
            var maxMonths = 12;
            for (i=0; i < maxMonths; i++) 
            {
                var option = new Element('option', {'value': i });
                option.innerHTML = i;
                option.injectInside(sf.calcObj.$months);
            }
        },
		
		setYears : function() { //Creates options in years dropdown
            var maxYears = 30;
            for (i=0; i <= maxYears; i++) 
            {
                var option = new Element('option', {'value': i });
                option.innerHTML = i;
                option.injectInside(sf.calcObj.$years);
            }
        },
        
        clearResults : function() {
            if(document.getElement('.calcResults')){
                Element.remove(document.getElement('.calcResults'))
            } 
        },

        clearErrors : function() {
            if(document.getElement('.calcErrors')){
                Element.remove(document.getElement('.calcErrors'))
            };
			sf.calcObj.$calculator.getElements('.error').removeClass('error');
        },
        
		clearInputs : function(calculation) {
			
			switch(calculation)
            {
                case 'totalamount':
					sf.calcInputs.setTarget(0);
					sf.calcInputs.setInitialDeposit(0);
					break
				case 'time':
                case 'monthlyamount':
					if (oldCalculation === 'totalamount') {
						sf.calcInputs.setTarget(0);
						sf.calcInputs.setInitialDeposit(0);
					}
					break
			}
			sf.calcInputs.setMonthlyPayment(0);
			sf.calcInputs.setYears(0);
			sf.calcInputs.setMonths(0);
						
			oldCalculation = calculation;
		},
		
        setCalc : function(calculation) { //display fields and attach calc function based on calculation output selected
            sf.calcObj.$calcButton.removeEvents(); //clear any previous calc function from button
			
			
   			this.resetMenu();
            this.clearResults();
            this.clearErrors();
			this.clearInputs(calculation);

			switch(calculation)
            {
				case 'totalamount': // TIMESCALE TAB
					sf.calcInputs.setTarget(''); // this is to pass the validator
					sf.calcObj.$calculationType.getElements('.totalamount a').addClass('on');
                    sf.calcObj.$period.setStyle('display', 'block').injectTop(sf.calcObj.$calcInputs);
					sf.calcObj.$periodLbl.setStyle('display', 'inline');
                    sf.calcObj.$deposit.setStyle('display', 'block').injectAfter(sf.calcObj.$period);
                    sf.calcObj.$monthly.setStyle('display', 'block').injectAfter(sf.calcObj.$deposit);
					sf.calcObj.$monthlyLbl.setStyle('display', 'inline');
					sf.calcObj.$target.setStyle('display', 'none');
                    sf.calcObj.$goal.setStyle('display', 'none');
                    sf.calcObj.$calculator.getElement('.btnRounded').setStyle('display', 'block');
                    sf.calcObj.$calcButton.addEvent('click', function(e) {
						this.blur();
						var e = new Event(e).stop();
		                sf.savingsCalc.calcMoneySaved();
	                });
                    break
				case 'time': // MYGOAL TAB - monthly dropdown
					sf.calcObj.$calculationType.getElements('.time a').addClass('on');
                    sf.calcObj.$target.setStyle('display', 'block').injectTop(sf.calcObj.$calcInputs);
                    sf.calcObj.$deposit.setStyle('display', 'block').injectAfter(sf.calcObj.$target);
					sf.calcObj.$goal.setStyle('display', 'block').injectAfter(sf.calcObj.$deposit).getElement('select').options[0].selected = true;
                    sf.calcObj.$monthly.setStyle('display', 'block').injectAfter(sf.calcObj.$goal);
					sf.calcObj.$monthlyLbl.setStyle('display', 'none');
					sf.calcObj.$period.setStyle('display', 'none');
					sf.calcObj.$periodLbl.setStyle('display', 'none');
                    sf.calcObj.$calculator.getElement('.btnRounded').setStyle('display', 'block');
                    sf.calcObj.$calcButton.addEvent('click', function(e) {
						this.blur();
						var e = new Event(e).stop();
		                sf.savingsCalc.calcTime();
	                });
                    break
				case 'monthlyamount': // MYGOAL TAB - timescale dropdown
					sf.calcObj.$calculationType.getElements('.time a').addClass('on');
                    sf.calcObj.$target.setStyle('display', 'block').injectTop(sf.calcObj.$calcInputs);
                    sf.calcObj.$deposit.setStyle('display', 'block').injectAfter(sf.calcObj.$target);
                    sf.calcObj.$goal.setStyle('display', 'block').injectAfter(sf.calcObj.$deposit).getElement('select').options[1].selected = true;
                    sf.calcObj.$period.setStyle('display', 'block').injectAfter(sf.calcObj.$goal);
					sf.calcObj.$periodLbl.setStyle('display', 'none');
                    sf.calcObj.$monthly.setStyle('display', 'none');
					sf.calcObj.$monthlyLbl.setStyle('display', 'none');
                    sf.calcObj.$calculator.getElement('.btnRounded').setStyle('display', 'block');
                    sf.calcObj.$calcButton.addEvent('click', function(e) {
						this.blur();
						var e = new Event(e).stop();
		                sf.savingsCalc.calcMonthlyAmount();
	                });
                    break
                case '':
                    sf.calcObj.$period.setStyle('display', 'none');
                    sf.calcObj.$target.setStyle('display', 'none');
                    sf.calcObj.$deposit.setStyle('display', 'none');
                    sf.calcObj.$monthly.setStyle('display', 'none');
                    sf.calcObj.$calculator.getElement('.btnRounded').setStyle('display', 'none');
                    break
            }
	
        },
        
        validateCalcInputs : function() { //general calculator validation
	        this.clearErrors();
	        var errorFound = false;
	        var errorMessages = new Array();
	        var minTargetAmount = 1;
	        var maxTargetAmount = 2000000;
	        var minMonthlyAmount = 1;
	        var maxMonthlyAmount = 100000;
        	
            //validate savings period
	        if(sf.calcObj.$period.style.display != 'none' && parseInt(sf.calcInputs.years()) <= 0) {
	            if(parseInt(sf.calcInputs.months()) <= 0) {
	                errorFound = true;
	                sf.calcObj.$months.addClass('error');
	                document.getElement('label[for=' + sf.calcObj.$months.id + ']').addClass('error');
	                errorMessages.push(sf.calcErrorMessages.invalidSavingPeriod);
	            } else {
                    sf.calcObj.$months.removeClass('error');
                    document.getElement('label[for=' + sf.calcObj.$months.id + ']').removeClass('error');
                }
	        }
        	
	        //validate input fields
	        sf.calcObj.$calcInputs.getElements('input[type=text]').each( function(el) {
                if(el.getParent().getParent().style.display != 'none') { //check that field is active
                    var fieldValue = parseInt(el.value);
                    var fieldLabel = document.getElement('label[for=' + el.id + ']');
                    
                    if(String(fieldValue) != String(el.value)) { //test that field contains int
                        errorFound = true;
                        el.addClass('error');
                        fieldLabel.addClass('error');
                        if(errorMessages.indexOf(sf.calcErrorMessages.invalidTextField) == -1) {
                            errorMessages.push(sf.calcErrorMessages.invalidTextField);
                        }
                        
                    } else if(el.id == 'targetAmount' && (fieldValue < minTargetAmount || fieldValue > maxTargetAmount)) { //test target amount
                        errorFound = true;
                        el.addClass('error');
                        fieldLabel.addClass('error');
                        if(errorMessages.indexOf(sf.calcErrorMessages.invalidTarget) == -1) {
                            errorMessages.push(sf.calcErrorMessages.invalidTarget);
                        }
                    } else if(el.id == 'initialDeposit' && !errorFound && fieldValue >= parseFloat(sf.calcInputs.target())) { //test that initial deposit is < target amount
                        errorFound = true;
                        el.addClass('error');
                        fieldLabel.addClass('error');
                        if(errorMessages.indexOf(sf.calcErrorMessages.invalidDeposit) == -1) {
                            errorMessages.push(sf.calcErrorMessages.invalidDeposit);
                        }
                    } else if(el.id == 'monthlyPayment' && (fieldValue < minMonthlyAmount || fieldValue > maxMonthlyAmount)) { //test monthly payment
                        errorFound = true;
                        el.addClass('error');
                        fieldLabel.addClass('error');
                        if(errorMessages.indexOf(sf.calcErrorMessages.invalidPayment) == -1) {
                            errorMessages.push(sf.calcErrorMessages.invalidPayment);
                        }
                    } else {
                        el.removeClass('error');
                        fieldLabel.removeClass('error');
                    }
                }
	        });

		//validate savings product
		if(sf.calcObj.$savingsProduct.style.display != 'none' && sf.calcObj.$savingsProduct.value == "select"){
			errorFound = true;
			sf.calcObj.$savingsProduct.addClass('error');
			document.getElement('label[for=' + sf.calcObj.$savingsProduct.id + ']').addClass('error');
			errorMessages.push(sf.calcErrorMessages.invalidProduct);
		}
        		
            if(errorFound) {
                this.clearResults();
                this.displayErrorMessages(errorMessages);
                return false;
            } else {
                return true;
            }
        },
        
        displayErrorMessages : function(errorMessages) {
            var errorBox = new Element('div', {'class': 'calcErrors'});
            errorMessages.each( function(error) {
                var errorPara = new Element('p'); 
                errorPara.innerHTML = error;
                errorPara.injectInside(errorBox);
            });
            errorBox.injectBefore(sf.calcObj.$calcInputs);
        },
        
		findRate : function (accountId, monthLimit, limitLimit) {
			var accountNode, monthNode, limitNode;
			var accounts = xmlDoc.getElementsByTagName('account');
			for( var i = 0, max = accounts.length; i < max; i++ ) {
				if ( accounts[i].getAttribute('id') == accountId ) {
					accountNode = i;
					break;
				};
			};
			var months = accounts[accountNode].getElementsByTagName('period');
			for( var i = 0, max = months.length; i < max; i++ ) {
				if ( (monthLimit <= Number(months[i].getAttribute('months'))) || isNaN(Number(months[i].getAttribute('months'))) ) {
					monthNode = i;
					break;
				};
			};
			var limits = months[monthNode].getElementsByTagName('limit');
			for( var i = 0, max = limits.length; i < max; i++ ) {
				if ( limitLimit < Number(limits[i].getAttribute('money')) || isNaN(Number(limits[i].getAttribute('money'))) ) {
					return Number(limits[i].getAttribute('rate'));
				};
			};
		},

        /* CALCULATIONS BEGIN HERE */
		
        calcTime : function() { //calculate time I need to reach my target
            // declare variables
            var accountName = sf.calcInputs.accountName();
			var accountId = sf.calcInputs.accountId();
            var target = sf.calcInputs.target();
	        var start = sf.calcInputs.initialDeposit();
	        var monthlyPayment = sf.calcInputs.monthlyPayment();
	        var result ='';
			
            // validate
	        if(!this.validateCalcInputs()) {
	            return false;
	        }

	        // do calc
	        var lnRate;
	        var lnAmt = parseFloat(start);
	        var lnGoal = parseFloat(target);
	        var lnMonthlyAmt = parseFloat(monthlyPayment);
	        var lnMonths = 0;
			var lnAccrued = 0;
			
			while ( ( lnAmt + lnAccrued ) < lnGoal ) {
				lnMonths++;
				lnAmt += lnMonthlyAmt;
				lnRate = parseFloat(this.findRate(accountId,lnMonths,lnAmt))/1200;
				lnAccrued += lnAmt*lnRate;
				if (lnMonths%12 == 0) { // add interests to final balance every 12
					lnAmt += lnAccrued;
					lnAccrued = 0;
				}
	        }  
	        if (lnMonths>11) {
                var howLongRes = Math.floor(lnMonths/12);
                howLongRes += (howLongRes == 1) ? ' year' : ' years';
                var lnRemainder = sf.mathRemainder(lnMonths,12);
                if (lnRemainder > 0) {
                    lnRemainder += (lnRemainder == 1) ? ' month' : ' months';
                    howLongRes = howLongRes + ', ' + lnRemainder;			   
                }
                result = '<strong>' + howLongRes + '</strong>';
	        } else {
		        lnMonths += (lnMonths == 1) ? ' month' : ' months';
		        result = '<strong>' + lnMonths + '</strong>';
	        }
        							
	        //results
	        calcResults = '';
	        calcResults += 'It will take you ';
	        calcResults += result;
	        calcResults += ' to save ';

	        calcResults += '<strong>&pound;' + sf.formatNumber(Math.ceil(target),0,1) + '</strong> ';
			calcResults += 'with ' + accountName + '.';
			this.displayResults(calcResults);

        },
        
        calcMoneySaved : function() { //calculate money i can save in a set time
            // declare variables
            var accountName = sf.calcInputs.accountName();
			var accountId = sf.calcInputs.accountId();
            var years = sf.calcInputs.years();
	        var months = sf.calcInputs.months();
	        var start = sf.calcInputs.initialDeposit();
	        var monthlyPayment = sf.calcInputs.monthlyPayment();
	        var result ='';
        	
            // validate	
            if(!this.validateCalcInputs()) {
	            return false;
	        }
            	
	        // do calc	
			var lnRate;
	        var lnMonthlyAmt = parseFloat(monthlyPayment);
	        var lnAmt = parseFloat(start);
	        var lnYears = parseFloat(years);
            var lnMonths = parseFloat(months);	
            var lnPeriods = (lnYears*12)+lnMonths;
			var lnAccrued = 0;
    	    
	        for (ln=0; ln<lnPeriods; ln++) {
				lnAmt += lnMonthlyAmt;
				lnRate = parseFloat(this.findRate(accountId,ln+1,lnAmt))/1200;
				lnAccrued += lnAmt*lnRate;
				if ((ln+1)%12 == 0 || (ln+1) == lnPeriods) { // add interests every 12 months or at the end of partial periods
					lnAmt += lnAccrued;
					lnAccrued = 0;
				}
            }
    	    
            howMuchRes = sf.formatNumber(lnAmt,2,1);	
	        result = '<strong>&pound;'+howMuchRes+'</strong>';
        	
	        //results
	        calcResults = '';
	        calcResults += 'You\'ll have saved ';
	        calcResults += result + ' in ';
	        if(lnYears > 0) {
	            calcResults += '<strong>' + years;
	            calcResults += (lnYears == 1) ? ' year,</strong> ' : ' years,</strong> ';
	        }
	        calcResults += '<strong>' + months;
	        calcResults += (lnMonths ==1) ? ' month</strong> ' : ' months</strong> ';
			calcResults += 'with ' + accountName +'.';
	        this.displayResults(calcResults);				
        },
        
        calcMonthlyAmount : function() { //money to deposit each month
            // declare variables
            var accountName = sf.calcInputs.accountName();
			var accountId = sf.calcInputs.accountId();
            var years = sf.calcInputs.years();
	        var months = sf.calcInputs.months();
            var target = sf.calcInputs.target();
	        var start = sf.calcInputs.initialDeposit();
	        var result ='';
        	
            // validate	
            if(!this.validateCalcInputs()) {
	            return false;
	        }
        	
	        // set up 
	        var lnGoal = parseFloat(target);
	        var lnStartAmt = parseFloat(start);	
        	
	        // do calc	
			var lnRate;
			var lnYears = parseFloat(years);	
			var lnMonths = parseFloat(months);	
			var lnPeriods = (lnYears*12)+lnMonths;
			var lnMonthlyAmt = (lnGoal-lnStartAmt)/lnPeriods;
			
            lnLastTested = lnMonthlyAmt;
            lnAmt = lnGoal+1

            while (lnAmt>lnGoal) {

				var lnAccrued = 0;
				lnAmt = lnStartAmt;
				for (ln=0; ln<lnPeriods; ln++) {
					lnAmt += lnMonthlyAmt;
					lnRate = parseFloat(this.findRate(accountId,ln+1,lnAmt))/1200;
					lnAccrued += lnAmt*lnRate;
					if ((ln+1)%12 == 0 || (ln+1) == lnPeriods) { // add interests every 12 months or at the end of partial periods
						lnAmt += lnAccrued;
						lnAccrued = 0;
					}
				}
				lnLastTested = lnMonthlyAmt;
				lnMonthlyAmt = lnMonthlyAmt*.9995;
            }
            howMuchRes = sf.formatNumber(lnLastTested*(1/.9995),2,1);	
	        result = '<strong>&pound;'+howMuchRes+'</strong>';	
        	
	        //results
	        calcResults = '';
			calcResults += 'You\'ll need to pay in ';
	        calcResults += result;
	        calcResults += ' every month to save ';
	        calcResults += '<strong>&pound;' + sf.formatNumber(Math.ceil(target),0,1) + '</strong> ';
			calcResults += 'with ' + accountName + '.';
	        this.displayResults(calcResults);				
        },       
        
        displayResults : function(calcResults) {
            if(document.getElement('.calcResults p')){
                document.getElement('.calcResults p').innerHTML = calcResults;
            } else {
                var resultBox = new Element('div', {'class': 'calcResults'});
                var resultPara = new Element('p'); 
                resultPara.innerHTML = calcResults;
                resultPara.injectInside(resultBox);
                resultBox.injectInside('calcForm');
            }
        }
        
    };
    return _this;
}();


sf.formatRound = function(number,precision) {
	if (precision==null) precision = 2;
	if (precision==0) {
		return Math.round(number);
	}
	lnFactor = Math.floor(Math.pow(10,precision));
	if (precision<0) {
		return  Math.round(number/lnFactor)*lnFactor;
	}

	return Math.round(number*lnFactor)/lnFactor;
}
   
sf.formatNumber = function(number,precision,commas) {
	if (precision==null) precision = 2;
	number = sf.formatRound(number,precision);
	var llSign = number<0;
	number = Math.abs(number);
	var ln = precision+1;
	var lc = "";
	number = Math.floor(number*Math.pow(10,precision));
	while (((ln--)>0) || (number>0)) {
		lc = (number%10)+lc;
		number = Math.floor(number/10);
		if (ln==1) lc = "."+lc;
	}
	if (llSign) lc = "-"+lc;
	var lc2 = "";
	if (commas) {
		var lc3 = lc;
		ln = lc.length;
		if (lc.indexOf(".")!=-1) {
			lc2 = lc.substring(lc.indexOf("."),ln);
			lc3 = lc.substring(0,lc.indexOf("."));
		}
		ln = lc3.length;
		var lnCount = 0;
		while (ln>0) {
			lnCount++;
			if (lnCount==4) {
				lc2 = ","+lc2;
				lnCount = 1;
			} 
			lc2 = lc3.substring(ln-1,ln)+lc2;
			ln--;
		}
	} else {
		lc2 = lc;
	}
	return lc2;
}

sf.mathRemainder = function(tnValue,tnDivisor) {
	if (tnValue==null) return 0;
	if (tnDivisor==null) return 0;
	var lnTimes = Math.floor(tnValue/tnDivisor);
	return tnValue-(lnTimes*tnDivisor);
}
