/**
 * @author Stephan Six <stephan.six@gmail.com>
 */
"use strict";
(function ($) {
	/**
	 * @see http://www.quirksmode.org/js/cookies.html
	 */
	function createCookie(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	}

	function readCookie(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}

	function eraseCookie(name) {
		createCookie(name,"",-1);
	}
	
	/**
	 * Ruerup Calc Class
	 * 
	 * @param object settings
	 */
	function RuerupCalc(wrapper, settings)
	{
		var self = this,
			allowSubmit = false,
			elements = {
				typeOfEmployment: $(settings.typeOfEmploymentSelector, wrapper),
				familyState: $(settings.familyStateSelector, wrapper),
				preTaxAnnualIncome: $(settings.preTaxAnnualIncomeSelector, wrapper),
				desiredMonthlyContribution: $(settings.desiredMonthlyContributionSelector, wrapper),
				maxMonthlyContributionInfo: $(settings.maxMonthlyContributionInfoSelector, wrapper),
				output: $(settings.outputSelector, wrapper),
				calcSaving: $(settings.calcSavingSelector, wrapper),
				calcIncome: $(settings.calcIncomeTaxSelector, wrapper)
			};
		
		/**
		 * Helper to format a float
		 * 
		 * @param float
		 * @return string
		 */
		this.formatNumber = function (float) {
			var result = new String(Math.round(float * 100) / 100).replace('.', ',');
			var cleanUp = result.match(/(\d+),(\d+)/);
			if (!cleanUp) {
				result += ',00';
			}
			else if (cleanUp[2].length < 2) {
				result += '0';
			}
			
			return result;
		}
		
		/**
		 * Returns if cookie data is available
		 * 
		 * @return bool
		 */
		function hasCookieData() {
			return retrieve(false) !== null;
		}
		
		/**
		 * Stores a result as cookie
		 * 
		 * @param string result
		 */
		function store(result) {
			createCookie(settings.cookieName, result);
		}
		
		/**
		 * Retrieves a result from cookie
		 * 
		 * @return string
		 */
		function retrieve() {
			return readCookie(settings.cookieName);
		}
		
		/**
		 * Returns a float for value
		 * 
		 * @return float|NaN
		 */
		function getFloatValue(value) {
			return parseFloat(value.replace(',', '.'));
		}
		
		/**
		 * Check if string could be a number
		 * 
		 * @param string string
		 * @return bool
		 */
		function checkFloatValue(string) {
			return new String(string).match(/^(\d+)((\.|,)\d+)?$/);
		}
		
		/**
		 * Returns whether the user is employed or not
		 * 
		 * @return bool
		 */
		function getTypeOfEmployment() {
			return $(elements.typeOfEmployment).val();
		}
		
		/**
		 * Returns whether the user is employed or not
		 * 
		 * @return bool
		 */
		function isEmployee() {
			return getTypeOfEmployment() === 'employee';
		}
		
		/**
		 * Returns the value of the family state select menu
		 * 
		 * @return string
		 */
		function getFamilyState() {
			return $(elements.familyState).val();
		}
		
		/**
		 * Returns whether the family state is married or not
		 * 
		 * @return bool
		 */
		function isMarried() {
			return getFamilyState() === 'married';
		}
		
		/**
		 * Returns a float value for the entered income
		 * 
		 * @return float|null
		 */
		function getIncome() {
			return getFloatValue($(elements.preTaxAnnualIncome).val());
		}
		
		/**
		 * Returns a float value for the entered desired montly contribution
		 * 
		 * @return float|null
		 */
		function getDesiredMonthlyContribution() {
			return getFloatValue($(elements.desiredMonthlyContribution).val());
		}
		
		/**
		 * Returns the max. contribution
		 * 
		 * @return int
		 */
		function getMaxContribution() {
			var result = isMarried() ? 40000 : 20000;
			
			if (isEmployee()) {
				result -= getIncome() * 0.199;
			}
			
			if (result < 0) {
				result = 0;
			}
			
			return result;
		}
		
		/**
		 * Calculate the income tax value for a given income and family state
		 * 
		 * @param float income
		 * @param bool married
		 * @result int
		 */
		function calcIncomeTax(income, married) {
			var result; /* Fix for newer FFs */
			if (married) {
				return calcIncomeTax(income / 2, false) * 2;
			}
			else {
				if (income <= 8004) {
					result = 0;
				}
				else if (income <= 13469) {
					var f = (income - 8004) / 10000;
					result = (912.17 * f + 1400) * f;
				}
				else if (income <= 52881) {
					var f = (income - 13469) / 10000;
					result = (228.74 * f + 2397) * f + 1038;
				}
				else if (income <= 250730) {
					result = 0.42 * income - 8172;
				}
				else {
					result = 0.45 * income - 15694;
				}
			}
			
			return Math.floor(result);
		}
		
		/**
		 * Returns the deduction factor for this year
		 * 
		 * @return float
		 */
		function getDeductionFactor() {
			var year = (new Date).getFullYear();
			
			if (year <= 2010) {
				return 0.7;
			}
			else if (year >= 2025) {
				return 1;
			}
			
			return (year - 2010) * 0.02 + 0.7;
		}
		
		/**
		 * Returns if the desired monthly contribution is valid
		 * 
		 * @reutrn bool
		 */
		function isDesiredMonthlyContributionValid(desiredMonthlyContribution) {
			return desiredMonthlyContribution * 12 <= getMaxContribution();
		}
		
		/**
		 * calc saving
		 * 
		 * @param object settings
		 * @return int|null
		 */
		function calcSaving() {
			var desiredMonthlyContribution = getDesiredMonthlyContribution();
			
			return	  calcIncomeTax(getIncome(), isMarried())
					- calcIncomeTax(getIncome() - (desiredMonthlyContribution * 12 * getDeductionFactor()), isMarried());
		}
		
		/**
		 * Validate the form
		 * 
		 * @param bool relaxed if true, no error classes will be added (for initial display)
		 * @return bool
		 */
		function validate() {
			var result = true;
			
			// disable submit
			if (elements.calcSaving.length > 0) {
				$(elements.calcSaving).attr({disabled: 'disabled'}).addClass('disabled');
			}
			
			// check income
			if (elements.preTaxAnnualIncome.length > 0) {
				if (!checkFloatValue($(elements.preTaxAnnualIncome).val())) {
					if ($(elements.preTaxAnnualIncome).val().length > 0) {
						$(elements.preTaxAnnualIncome).addClass('error');
					}
					
					result = false;
				}
				else {
					$(elements.preTaxAnnualIncome).removeClass('error');
				}
			}

			// check monthly contribution syntax
			if (elements.desiredMonthlyContribution.length > 0) {
				if (!checkFloatValue($(elements.desiredMonthlyContribution).val())) {
					if ($(elements.desiredMonthlyContribution).val().length > 0) {
						$(elements.desiredMonthlyContribution).addClass('error');
					}
					
					result = false;
				}
				else {
					$(elements.desiredMonthlyContribution).removeClass('error');
				}
			}
			
			// check monthly contribution value
			if (elements.desiredMonthlyContribution.length > 0) {
				if (result && !isDesiredMonthlyContributionValid(getFloatValue($(elements.desiredMonthlyContribution).val()))) {
					$(elements.maxMonthlyContributionInfo).addClass('error');
					result = false;
				}
				else {
					$(elements.maxMonthlyContributionInfo).removeClass('error');
				}
			}
			
			// enable submit
			if (elements.calcSaving.length > 0 && result) {
				$(elements.calcSaving).removeAttr('disabled').removeClass('disabled');
			}
			
			// store result
			allowSubmit = result;
			
			return result;
		}
		
		/**
		 * Updates the info text for the max monthly contribution
		 * 
		 * @return void
		 */
		function updateMaxMonthlyContributionInfo() {
			var maxContribution = getMaxContribution();
			if (!isNaN(maxContribution)) {
				$(elements.maxMonthlyContributionInfo).html(
					'(max. ' + self.formatNumber(getMaxContribution() / 12) + ' &euro;)'
				);
			}
		}

		/**
		 * Init
		 * 
		 * @param object settings
		 */
		function init() {
			// bind buttons
			$(settings.calcSavingSelector, wrapper).bind('click', function () {
				if (allowSubmit) {
					var result = calcSaving();
					if (settings.saveToCookie) {
						store(result);
					}
					
					settings.onSubmit.apply(self, [result, settings, wrapper]);
				}
			});
			
			// bind onchange
			$('input, select', wrapper).bind('keyup change', function () {
				updateMaxMonthlyContributionInfo();
				
				if (validate()) {
					settings.onChange.apply(self, [calcSaving(), settings, wrapper]);
				}
			});
			
			// on cookie data
			if (hasCookieData(settings)) {
				if (settings.onCookieData.apply(self, [retrieve()])) {
					eraseCookie(settings.cookieName);
				}
			}
			else {
				settings.onNoCookieData.apply(self);
			}
			
			// first validation and info update
			validate();
			updateMaxMonthlyContributionInfo();
		}
		
		init();
	}
	
	/**
	 * Extend jQuery
	 */
	$.fn.extend({
		hqwareRuerupCalc: function (settings) {
			var mySettings = $.extend({}, $.hqwareRuerupCalc.defaults, settings);
			
			this.each(function () {
				$(this).data('hqwareRuerupCalc', new RuerupCalc(this, mySettings));
			});
			
			return this;
		}
	});
	
	/**
	 * Inital plugin settings
	 */
	$.extend({
		hqwareRuerupCalc: {
			defaults: {
				typeOfEmploymentSelector: '#type-of-employment',
				familyStateSelector: '#family-status',
				preTaxAnnualIncomeSelector: '#pre-tax-annual-income',
				desiredMonthlyContributionSelector: '#desired-monthly-contribution',
				maxMonthlyContributionInfoSelector: '#max-monthly-contribution-info',
				outputSelector: '#output',
				calcSavingSelector: '#calcSaving',
				calcIncomeTaxSelector: '#calcIncomeTax',
				cookieName: 'hqware-ruerup-result',
				saveToCookie: true,
				onSubmit: function (result, settings, wrapper) {
					if (result !== null) {
						$(settings.outputSelector, wrapper).html(result);
					}
				},
				onChange: function (result, settings, wrapper) {
				},
				onCookieData: function (result) {
					return false;
				},
				onNoCookieData: function () {
				}
			}
		}
	});
}(jQuery));

