var daysInYear_cache=(function(){
	var cache={};
	return function(year){
		var _y=year.toString();
		if(cache[_y])
			return cache[_y];
		else{
			cache[_y]=daysInYear(year);
			return cache[_y];
		}
	};
	
}());
var DepCalc={
	// @memberOf depCalc2 - инициализирует необходимые параметры
	// @param {Date} dayFirst - первый день с которго начинается отчет процентов
	// @param {Date} dayLast - день на котором заканчивается отчет процентов
	init:function(dayFirst,dayLast,proc){
		this._dayFirst=this._dayLast=new Date();
		this._proc=0;
		
		dayFirst && DepCalc.setDayFirst(dayFirst);
		dayLast && DepCalc.setDayLast(dayLast);
		proc && DepCalc.setProc(proc);
	},
	// ?? Методы можно упрастить
	// @param {Date} day
	setDayFirst:function(day){
	 day = new Date(+day + 24*60*60*1000);
	 this._dayFirst=setIfIs(day,"[object Date]");
	},
	// @param {Date} day
	setDayLast:function(day){
		this._dayLast=setIfIs(day,"[object Date]");
	},
	// @param {Number} number
	setProc:function(number){
		this._proc=setIfIs(number,"[object Number]")/100;
	},
	// Алгоритм расчета вклада с учетом капитализации, но без пополнения!
	calc1:function(sum, isMonthCap){
		var 	timePointsArray=[this._dayFirst],// Массив где храним опорные даты
				startPoint=new Date(this._dayFirst);
		
		while(startPoint<this._dayLast){
			var 	month=startPoint.getMonth(),
					year=startPoint.getFullYear(),// Меняем дискрет в зависимости есть ли ежемесячная капитализация:
					nextPoint=isMonthCap?new Date(year,month+1,1):new Date(year+1,0,1);
			timePointsArray.push(nextPoint);
			startPoint=nextPoint;
		}
		timePointsArray[timePointsArray.length-1]=this._dayLast; // Небольшая коррекция

		var		i=1,
				totalSum=sum,
				kSum=0;
		
		while(timePointsArray[i]){
			var 	year=timePointsArray[i-1].getFullYear(),
					daysInYear_int=daysInYear(year),// Можно мемоизировать
					daysBetween=totalDays(timePointsArray[i-1],timePointsArray[i]);
					
			kSum=daysBetween*this._proc/daysInYear_int;// Коэффициент увеличения суммы вклада на итерации
			if (isMonthCap)		
				totalSum*=(1+kSum);
			else
				totalSum+=sum*kSum;
			i++;
		}
		return totalSum;
	},
	// Алгоритм расчета депозита с пополнениями
	calc2:function(sum,isMonthCap,addOn){
		var 	timePointsArray=[
					{
						sum:sum,
						date:this._dayFirst
					}],// Массив где храним опорные даты
				startPoint=new Date(this._dayFirst);
		while(startPoint<this._dayLast){
			var 	month=startPoint.getMonth(),
					year=startPoint.getFullYear(),// Меняем дискрет в зависимости есть ли ежемесячная капитализация:
					nextPoint=isMonthCap?new Date(year,month+1,1):new Date(year+1,0,1);
			timePointsArray.push({
				sum:0,
				date:nextPoint
			});
			startPoint=nextPoint;
		}
		timePointsArray[timePointsArray.length-1]={
						sum:0,
						date:this._dayLast
					}; // Небольшая коррекция
		var 	timePointsArray2,
				addOnTotalSum=0; // Сумма всех пополнений
		if(addOn){
			timePointsArray2=timePointsArray.concat(addOn).sort(function(a,b){
				return a["date"]-b["date"];
			});
			for(var i23=0;i23<addOn.length;addOnTotalSum+=addOn[i23++].sum);i23=null;// считсаем сумму всех пополнений
		}else{
			timePointsArray2=timePointsArray;
		}
		var		i=1,
				totalSum=0,
				_sum=0, // Текущая сумма на которую высчитывается проценты
				kSum=0, // Коэффициент увеличения суммы вклада на итерации
				plotData=[[timePointsArray2[0].date,timePointsArray2[0].sum]];
		
		while(timePointsArray2[i]){
			var 	year=timePointsArray2[i-1].date.getFullYear(),
					daysInYear_int=daysInYear_cache(year),// Можно мемоизировать
					daysBetween=totalDays(timePointsArray2[i-1].date, timePointsArray2[i].date);
					
			kSum=daysBetween*this._proc/daysInYear_int;
			_sum=(isMonthCap?totalSum:_sum) + timePointsArray2[i-1].sum;
			if (isMonthCap){		
				// Здесь totalSum  это фактическая сумма вклада
				totalSum=_sum*(1+kSum);
				plotData.push([timePointsArray2[i].date,totalSum]);
			}else{
				// сумма дохода которая выплачивается вконце срока вклада
				totalSum+=_sum*kSum;
				plotData.push([timePointsArray2[i].date,_sum+totalSum]);
			}
			i++;
		}
		return {
			sumWithProfit:isMonthCap?totalSum:(_sum+totalSum),
			plotData:plotData,
			addOnTotalSum:addOnTotalSum
		};
	}
}

// Количество дней между двумя датами
function totalDays(day1,day2){
	return Math.round((day2 - day1)/86400000); // (1000*60*60*24);
}
function daysInYear(year_number){
	return totalDays((new Date(year_number,1,1)),(new Date(year_number+1,1,1)));
};

function setIfIs(value,typeStr){
	var inst=instance(value);
	if(inst===typeStr) 
		return value;
	else{
		throw("Function '"+setIfIs.name+"', must have '"+typeStr+"' type, but found '"+inst+"'");
	}
}
function instance(obj){
	return Object.prototype.toString.call(obj);
}
