// ## -*- coding: utf-8 -*-

/*jslint browser:true, sub:true, white:false */

var controls_str = [ "mo", "n", "i", "PV", "PMT", "FV" ];

function handledecimal(s)
{
	var t = s;
	// replace all commas by points
	while (t.indexOf(",") != -1) {
		t = t.replace(",", ".");
	}
	// remove all points but the last one, if any
	while (t.indexOf(".") != t.lastIndexOf(".")) {
		t = t.substring(0, t.indexOf(".")) + t.substring(t.indexOf(".")+1);
	}
	return t;
}


function ftostring(value, decs)
{
	var res = "";
	if (value.toFixed) {
		res = value.toFixed(decs);
	} else {
		res = "";
		var decs2 = decs;
		var valueprov = Math.round(Math.abs(value)*Math.pow(10,decs));
		res = "";
		while (valueprov > 0 || decs2 >= 0) {
			res = parseInt(valueprov % 10, 10) + res;
			valueprov = Math.floor(valueprov/10);
			decs2--;
			if (decs2 === 0) {
				res = "." + res;
			}
		}
		res = (value >= 0 ? "" : "-")+res;
	}
	return res;
}

function comppmtlim(i, n)
{
	if (Math.abs(i) < 0.00000001) {
		return n;
	} else {
		return ( 1 - Math.pow(1+(i/100),-n) ) / (i/100);
	}
}

function calcNPV(values, stoeex, begin, is_n)
{
	var mo = values[0];
	var n = values[1];
	var i = values[2];
	var pv = values[3];
	var pmt = values[4];
	var fv = values[5];

	if (mo > 1) {
		i = i / mo;
		n = n * mo;
	}

	if (n == Math.floor(n) || is_n) {
		return pv + 
			(1 + (i/100)*(begin ? 1:0)) * pmt * comppmtlim(i,n) + 
			fv*Math.pow(1+(i/100),-n);
	} else if (! stoeex) {
		return pv*(1+((i/100)*(n-Math.floor(n)))) + 
			(1 + (i/100)*(begin ? 1:0)) * pmt * comppmtlim(i,Math.floor(n)) +
			fv*Math.pow(1+(i/100),-Math.floor(n));
	} else  {
		return pv*Math.pow(1+(i/100),(n - Math.floor(n))) + 
			(1 + (i/100)*(begin ? 1:0)) * pmt * comppmtlim(i,Math.floor(n)) +
			fv*Math.pow(1+(i/100),-Math.floor(n));
	}
}


function financecalc(dependent, values, decimals, stoeex, is_begin)
{
	var firstNPV;
	var secondNPV;
	var interpolation_guess;
	var firstguess;
	var secondguess;
	var iteration;
	var tolerance = 0.0000000001;

	if (values[1] < 0) {
		return "Err n < 0";
	}
 	if (values[2] <= -100) {
		return "Err i < -100";
	}

	// increase tolerance as independent variables get too big
	tolerance *= (1 + Math.abs(values[3]) + Math.abs(values[4]) + Math.abs(values[5]));

	iteration = 1;
	while (iteration <= 70) {
		if (iteration == 1) {
			firstguess = 0;
			secondguess = 1;
		} else {
			firstguess = secondguess;
			secondguess = interpolation_guess;
		}

		values[dependent] = firstguess;

		if (values[2] <= -100) {
			break;
		}

		firstNPV = calcNPV(values,stoeex,is_begin,dependent == 1);

		values[dependent] = secondguess;

		if (values[2] <= -100) {
			break;
		}

		secondNPV = calcNPV(values,stoeex,is_begin,dependent == 1);

		if ( Math.abs(secondNPV) < tolerance) {
			if (dependent == 1 && values[0] == 1) {
				if (secondguess - Math.floor(secondguess) > 0.003) {
					secondguess = Math.floor(secondguess)+1;
				} else {
					secondguess = Math.floor(secondguess);
				}
			}
			return ftostring(secondguess, decimals);
		}

		var interpolation_B = (secondNPV-firstNPV)/(secondguess-firstguess); // B
		interpolation_guess = firstNPV - firstguess*interpolation_B; // A
		interpolation_guess /= -interpolation_B; // -A/B is the interpolation root
		iteration++;
	}
	return "Unsolvable";
}

function fincalc_fin_chk(op)
{
	var ret = true;
	var controls = [ document.calc.mo, document.calc.n, document.calc.i, document.calc.PV, document.calc.PMT, document.calc.FV ];
	var control = controls[0];

	for(var x = 0; x < controls.length; ++x) {
		if (op == controls_str[x]) {
			control = controls[x];
		}
		var v;
		if (x === 0) {
			v = parseInt(controls[x].value, 10);
		} else {
			v = parseFloat(handledecimal(controls[x].value));
		}
		if (isNaN(v) && op != controls_str[x]) {
			controls[x].value = "?";
			ret = false;
		}
		if (x === 0 && v < 1) {
			controls[x].value = "1";
		}
	}

	// clean up dependent variable
	control.value = "";

	return ret;
}

function fincalc_fin(op, decimals, stoeex, begin)
{
	var controls = [ document.calc.mo, document.calc.n, document.calc.i, document.calc.PV, document.calc.PMT, document.calc.FV ];
	var values = [0, 0, 0, 0, 0, 0];
	var nop = -1;
	
	for(var x = 0; x < controls.length; ++x) {
		values[x] = parseFloat(handledecimal(controls[x].value));
		if (isNaN(values[x])) {
			values[x] = 0;
		}
		if (op == controls_str[x]) {
			nop = x;
		}
	}

	controls[nop].value = financecalc(nop, values, decimals, stoeex, begin);
}

function fincalc_math_chk(op)
{
	var ret = true;
	var p1 = parseFloat(handledecimal(document.calc.PV.value));
	var p2 = parseFloat(handledecimal(document.calc.PMT.value));

	if (op != "inv") {
		if (isNaN(p1)) {
			document.calc.PV.value = "?";
			ret = false;
		}
	}

	if (isNaN(p2)) {
		document.calc.PMT.value = "?";
		ret = false;
	}
	return ret;
}

function fincalc_math(op, decimals)
{
	var p1 = parseFloat(handledecimal(document.calc.PV.value));
	var p2 = parseFloat(handledecimal(document.calc.PMT.value));
	var p3 = 0;
	var control = document.calc.FV;

	if (op == "+") {
		p3 = p1 + p2;
	} else if (op == "-") {
		p3 = p1 - p2;
	} else if (op == "*") {
		p3 = p1 * p2;
	} else if (op == "/") {
		if (p2 === 0) {
			p3 = NaN;
		} else {
			p3 = p1 / p2;
		}
		
	} else if (op == "pow") {
		p3 = Math.pow(p1, p2);
	} else if (op == "inv") {
		control = document.calc.PMT;
		if (p2 === 0) {
			p3 = NaN;
		} else {
			p3 = 1/p2;
		}
	}

	if (isNaN(p3)) {
		control.value = "Error";
	} else {
		control.value = ftostring(p3, decimals);
	}
}

function Fincalc(op)
{
	var decimals = parseInt(document.calc.decimals.value, 10);
	if (isNaN(decimals) || decimals > 9) {
		decimals = 2;
		document.calc.decimals.value = "2";
	}

	var stoeex = (parseInt(document.calc.stoeex.value, 10) !== 0);
	var begin = (parseInt(document.calc.begin.value, 10) !== 0);

	if (op == "i" || op == "n" || op == "PV" || op == "PMT" || op == "FV") {
		// financial
		if (fincalc_fin_chk(op)) {
			fincalc_fin(op, decimals, stoeex, begin);
		}
	} else {
		if (fincalc_math_chk()) {
			fincalc_math(op, decimals);
		}
	}
	return false;
}

function Begin_toogle()
{
	if (parseInt(document.calc.begin.value, 10) === 0) {
		document.calc.begin.value = 1;
		document.calc.Button_begin.className = "mybutton_1";
	} else {
		document.calc.begin.value = 0;
		document.calc.Button_begin.className = "mybutton_0";
	}
}

function Stoeex_toogle()
{
	if (parseInt(document.calc.stoeex.value, 10) === 0) {
		document.calc.stoeex.value = 1;
		document.calc.Button_stoeex.className = "mybutton_1";
	} else {
		document.calc.stoeex.value = 0;
		document.calc.Button_stoeex.className = "mybutton_0";
	}
}

