var Calculators = Calculators || {};
Calculators.planning = Calculators.planning || {};

Calculators.planning.NetToGrossPaycheck = function NetToGrossPaycheck(net, ytdGross, shelteredContributionRate, preTaxDeductions, payPeriod, filingStatus, numberOfAllowances, postTaxDeductions, postTaxReimbursements, stateAndLocalWithholdingRate) {
	this.net = new Calculators.type.Money(net);
	this.ytdGross = new Calculators.type.Money(ytdGross);
	this.shelteredContributionRate = new Calculators.type.Rate(shelteredContributionRate);
	this.preTaxDeductions = new Calculators.type.Money(preTaxDeductions);
	this.payPeriod = new Number(payPeriod);
	this.filingStatus = filingStatus;	
	this.numberOfAllowances = new Number(numberOfAllowances);
	this.postTaxDeductions = new Calculators.type.Money(postTaxDeductions);
	this.postTaxReimbursements = new Calculators.type.Money(postTaxReimbursements);
	this.stateAndLocalWithholdingRate = new Calculators.type.Rate(stateAndLocalWithholdingRate);
	this.grossPaycheck = null;
	
	this.getNet = function() {
		return this.net;
	}
	
	this.getYtdGross = function() {
		return this.ytdGross;
	}
	
	this.getShelteredContributionRate = function() {
		return this.shelteredContributionRate;
	}
	
	this.getPreTaxDeductions = function() {
		return this.preTaxDeductions;
	}
	
	this.getPayPeriod = function() {
		return this.payPeriod;
	}	
	
	this.getFilingStatus = function() {
		return this.filingStatus;
	}	
	
	this.getNumberOfAllowances = function() {
		return this.numberOfAllowances;
	}
	
	this.getPostTaxDeductions = function() {
		return this.postTaxDeductions;
	}
	
	this.getPostTaxReimbursements = function() {
		return this.postTaxReimbursements;
	}
	
	this.getStateAndLocalWithholding = function() {
		if (this.grossPaycheck == null)
			this.findGross();		
		return this.grossPaycheck.getStateAndLocalWithholding();
	}
	
	this.getTotalPreTaxDeductions = function () {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getTotalPreTaxDeductions();
	}
	
	this.getFederalWithholding = function () {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getFederalWithholding();
	}
	
	this.getOASDI = function () {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getOASDI();
	}
	
	this.getMedicare = function() {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getMedicare();
	}
	
	this.getPostTaxNet = function () {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getPostTaxNet();
	}
	
	this.getGross = function() {
		if (this.grossPaycheck == null)
			this.findGross();
		return this.grossPaycheck.getGross();
	}	
		
	this.getTestGrossPaycheck = function(grossAmount) {
		paycheck = new Calculators.planning.GrossToNetPaycheck(grossAmount, this.ytdGross, this.shelteredContributionRate, this.preTaxDeductions, this.payPeriod, this.filingStatus, this.numberOfAllowances, this.postTaxDeductions, this.postTaxReimbursements, this.stateAndLocalWithholdingRate);
		return paycheck;
	}
	
	this.findGross = function() {
		var low = 0.0;
		var high = (this.net.getAmount() * 2);
		var calculatedNetPay = 0.0;
		var tmpGrossPay = 0.0;		
		var testGrossPaycheck = null;
		var validCalc = true;
		
		if(this.preTaxDeductions.getAmount() > 0.0) {
			high += this.preTaxDeductions.getAmount();
		}
		
		do {
			tmpGrossPay = (low+high)/2; // take a guess midway between known low and high points
			testGrossPaycheck = this.getTestGrossPaycheck(tmpGrossPay);
			calculatedNetPay = testGrossPaycheck.getNet().getAmount();
			/* if low guess == high guess, the window has closed on itself due to bad data
			 * entry. If allowed to continue, this will result in an infinite loop */			
			if(low == high) {
				validCalc = false;
				alert("low == high");
				break;
			}
			// gross pay must me at least equal to pre-tax deductions - if not, the guess is invalid 
			if(tmpGrossPay < testGrossPaycheck.getTotalPreTaxDeductions().getAmount()) {
				// the estimate is too low, make the new floor into the previous estimate
				low = tmpGrossPay;
				high = tmpGrossPay * 2;
				continue;				
			}
			// Prevent infinite loop - the scenario below causes an unsolvable problem 
			if ((tmpGrossPay - testGrossPaycheck.getTotalPreTaxDeductions().getAmount()) < (testGrossPaycheck.getFederalWithholding().getAmount() + testGrossPaycheck.getOASDI().getAmount() + testGrossPaycheck.getMedicare().getAmount())) {
				validCalc = false;
				alert("unsolvable problem");
				break;
			}			
			// adjust the guestimation window            
			if (calculatedNetPay < this.net.getAmount()) {
				// the estimate is too low, make the new floor into the previous estimate
				low = tmpGrossPay;
				high = tmpGrossPay * 2;
			}
			else if (calculatedNetPay > this.net.getAmount()) {
				high = tmpGrossPay;
			}			
		} while (calculatedNetPay.toFixed(2) != this.net.getAmount().toFixed(2));

		if(validCalc)
			this.grossPaycheck = testGrossPaycheck;
		else
			alert("Error: Could not compute.");
	}
}
