var MSECOND_PER_DAY = 86400000;
//var MSECOND_PER_HALFDAY = 86400000 / 2;


function debug(str, flag)
{
	if (flag == undefined || flag == true) {
		//alert(str);
	}
}

function mydebug(str, flag)
{
	if (flag == undefined || flag == true) {
		//alert(str);
	}
}

function roundup(decimal, places)
{
	return (Math.floor(decimal * Math.pow(10,places) + 0.5)) / Math.pow(10,places);
}

// convert today date to settlement date
function calSettlementDate(sd)
{
	// Get Settlement Date
	
	var tempyear, tempmonth, tempday;
	/*
	var datePat = /^(\d{1,2})(\/|-)(\d{1,2})\2(\d{4})$/; // requires 4 digit year
	var tempmatchArray = sd.match(datePat); // is the format ok?
	tempmonth = tempmatchArray[3]; // parse date into variables
	tempday = tempmatchArray[1];
	tempyear = tempmatchArray[4];
	*/
	
	var settlementDate = new Date();
	var tempdate = new Date();
	tempdate.setTime(0);
	tempdate.setFullYear(settlementDate.getFullYear(),settlementDate.getMonth(),settlementDate.getDate());
	settlementDate.setTime(tempdate.getTime());
		
	//settlementDate.setFullYear(tempyear,tempmonth-1,tempday);
	//settlementDate.setFullYear(settlementDate.getFullYear(),settlementDate.getMonth()-1,settlementDate.getDay());
	//settlementDate.setTime(Date.parse(settlementDate.getMonth()-1+"/"+settlementDate.getDate()+"/"+settlementDate.getFullYear()));
	//alert(settlementDate.toDateString());
	return settlementDate;
}

// convert maturity date into date format
function calMaturityDate(d)
{	
	var tempyear, tempmonth, tempday;
	
	// Get Maturity Date
	var datePat = /^(\d{1,2})(\/|-)(\d{1,2})\2(\d{4})$/; // requires 4 digit year
	var tempmatchArray = d.match(datePat); // is the format ok?
	tempmonth = tempmatchArray[3]; // parse date into variables
	tempday = tempmatchArray[1];
	tempyear = tempmatchArray[4];
	
	var maturityDate = new Date();
	maturityDate.setTime(0);
	maturityDate.setFullYear(tempyear,tempmonth-1,tempday);
	
	return maturityDate;
}

// number of semi-annual coupons payable between settlement date and maturity date
/*
function calN(issueDate, maturityDate, frequency, basis)
{
	var n = maturityDate.getTime() - issueDate.getTime() + MSECOND_PER_HALFDAY;//no. of ms between 2 dates.
	n = n/MSECOND_PER_DAY; //n/1000/60/60/24; //convert to days
	//n = Math.floor(n/(365/frequency)) + 1; //calculate no. of coupons payable.
	n = Math.floor(n/(basis/frequency)); //calculate no. of coupons payable.
	
	//mydebug("coupons payable days - " + n);
	return n;
}
*/
function calN(prevcoupondate, maturityDate, frequency, basis)
{
	var n = maturityDate.getTime() - prevcoupondate.getTime();//no. of ms between 2 dates.
	//var n = trunc(maturityDate) - trunc(prevcoupondate);//no. of ms between 2 dates.
	n = n/MSECOND_PER_DAY; //n/1000/60/60/24; //convert to days
	//n = Math.floor(n/(365/frequency)) + 1; //calculate no. of coupons payable.
	n = Math.floor(n/(basis/frequency)); //calculate no. of coupons payable.
	
	//mydebug("coupons payable days - " + n);
	return n;
}

// calculate next coupon date based on maturity date
//function calNextCouponDate(prevcd, md, n, frequency)
function calNextCouponDate(sd, md, frequency)
{
	/*
	// first, find the next coupon date.
	var nextcoupondate = new Date();
	
	nextcoupondate.setTime(prevcd.getTime());
	nextcoupondate.setMonth(prevcd.getMonth() + Math.floor(12/frequency));
	if(nextcoupondate.getTime() > md.getTime()){
		nextcoupondate.setTime(md.getTime());
	}
	
	//mydebug("next - " + nextcoupondate);
	return nextcoupondate;
	*/
	
		// first, find the previous valid coupon date prior to settlement date
	var nextcoupondate = new Date();
	
	//mydebug("prev - " + nextcoupondate);
	var tempdate = new Date();
	tempdate.setTime(md.getTime());	
	var tempdate_ms = tempdate.getTime();
	var sd_ms = sd.getTime();
	while(tempdate_ms > sd_ms){
		nextcoupondate.setTime(tempdate_ms);
		tempdate.setMonth(tempdate.getMonth() - Math.floor(12/frequency));
		tempdate_ms = tempdate.getTime();			
	}
		
	return nextcoupondate;
}

// E - DCS = days from settlement date to next coupon date
function calDsc(settlementdate, nextcoupondate)
{
	var dsc = nextcoupondate.getTime() - settlementdate.getTime();
	//var dsc = trunc(nextcoupondate) - trunc(settlementdate);
	dsc = Math.floor(dsc/MSECOND_PER_DAY); //Math.floor(dsc/1000/60/60/24); //convert to days
	
	return dsc;
}

// days between settlement date and maturity date
function calDsr(settlementdate, maturitydate)
{
	var dsr = maturitydate.getTime() - settlementdate.getTime();//no. of ms between 2 dates.
	//var dsr = trunc(maturitydate) - trunc(settlementdate);//no. of ms between 2 dates.
	dsr = Math.floor(dsr/MSECOND_PER_DAY); //dsr/1000/60/60/24; //convert to days
	return dsr;
}

// calculate previous coupon date based on maturity date
function calPrevCouponDate(sd, md, frequency)
{

	// first, find the previous valid coupon date prior to settlement date
	var prevcoupondate = new Date();
	
	//mydebug("prev - " + prevcoupondate);
	var tempdate = new Date();
	tempdate.setTime(md.getTime());	
	var tempdate_ms = tempdate.getTime();
	var sd_ms = sd.getTime();
	while(tempdate_ms > sd_ms){
		tempdate.setMonth(tempdate.getMonth() - Math.floor(12/frequency));
		tempdate_ms = tempdate.getTime();			
	}
	prevcoupondate.setTime(tempdate_ms);
	
	return prevcoupondate;
}

// number of days in coupon period where settlement occurs
function calE(nextcoupondate, prevcoupondate, basis, frequency)
{
	var e = nextcoupondate.getTime() - prevcoupondate.getTime();
	//var e = trunc(nextcoupondate) - trunc(prevcoupondate);
	//e = Math.floor(e/MSECOND_PER_DAY); //Math.floor(e/1000/60/60/24); //convert to days
	e = basis/frequency;
	
	return e;
}

// days between beginning of current coupon period and settlement date
function calA(prevcoupondate, settlementdate)
{
	var dcs = settlementdate.getTime() - prevcoupondate.getTime();
	//var dcs = trunc(settlementdate) - trunc(prevcoupondate);
	dcs = Math.floor(dcs/MSECOND_PER_DAY); //Math.floor(dcs/1000/60/60/24); //convert to days

	return dcs;
}

function calYld(yld)
{
	return ((Math.sqrt(1+yld/100)-1)*2);
}

function calRate(rate)
{
	return (rate/100);
}

function calPrice(redemption, yld, frequency, dayN, dsc, dayE, rate, dayA, dayE)
{
	var part1 = redemption/(Math.pow(((yld/frequency)+1),(dayN-1+(dsc/dayE))));
	//mydebug("part1 - " + part1);
	//document.getElementsByName("part1")[0].value = part1;
	
	var part2 = 0 ;
	for(k=1; k<=dayN; k=k+1)
	{
		part2 = part2 + ((100*rate/frequency)/Math.pow(((yld/frequency)+1),(k-1+(dsc/dayE))));
	}
	//mydebug("part2 - " + part2);
	//document.getElementsByName("part2")[0].value = part2;
	
	var part3 = 100*(rate/frequency)*(dayA/dayE);
	//mydebug("part3 - " + part3);
	//document.getElementsByName("part3")[0].value = part3;
	//document.getElementsByName("part4")[0].value = "---";
	
	return part1 + part2 - part3;
}

function unitTest1(form, md, sd, rate, yld){
	form.MaturityDateP.value = md;
	form.settlementDateP.value = sd;
	form.SemiAnnualCouponRateP.value = rate;
	form.YieldToMaturityP.value = yld;
	
	calculateP(form);
}

function unitTest2(form, md, sd, rate, price){
	form.MaturityDateY.value = md;
	form.settlementDateY.value = sd;
	form.SemiAnnualCouponRateY.value = rate;
	form.BondPriceY.value = price;
	
	calculateY(form);
}

function calculateP(form)
{
	var redemption = 100;
	var frequency = 2;
	var basis = 365;
	
	var maturityDate = calMaturityDate(form.MaturityDateP.value);
	var settlementDate = calSettlementDate(form.settlementDateP.value);	
	var issueDate = new Date();
	issueDate.setTime(settlementDate.getTime());
	//var dayN = calN(issueDate, maturityDate, frequency, basis);
	var prevcoupondate = calPrevCouponDate(settlementDate, maturityDate, frequency);
	var dayN = calN(prevcoupondate, maturityDate, frequency, basis);
	var nextcoupondate = calNextCouponDate(settlementDate, maturityDate, frequency);
	//var nextcoupondate = calNextCouponDate(prevcoupondate, maturityDate, dayN, frequency);	
	var yld = calYld(form.YieldToMaturityP.value);	
	var dsc = calDsc(settlementDate, nextcoupondate);	
	var dayE = calE(nextcoupondate, prevcoupondate, basis, frequency);		
	var dayA = calA(prevcoupondate, settlementDate);	
	var rate = calRate(form.SemiAnnualCouponRateP.value);	
	
	// form.redemption.value = redemption;
	// form.frequency.value = frequency;
	// form.basis.value = basis;		
	// form.maturitydate.value = maturityDate;
	// form.settlementdate.value = settlementDate;
	// form.issuedate.value = "---";
	// form.dayN.value = dayN;	
	// form.prevcoupondate.value = prevcoupondate;
	// form.nextcoupondate.value = nextcoupondate;
	// form.yld.value = yld;
	// form.dayDSC.value = dsc;
	// form.dayA.value = dayA;
	// form.dayE.value = dayE;	
	// form.rate.value = rate;
	// form.dayDSR.value = "---";
	// form.price.value = "---";
	
	var price = calPrice(redemption, yld, frequency, dayN, dsc, dayE, rate, dayA, dayE);
	
	var round = 1;
	//form.BondPriceDisplay.value = rounding(price,2);
	form.BondPriceDisplay.value = roundup(price,round).toFixed(round);
	
	return price;
}

function calculateY(form)
{
	var redemption = 100;
	var frequency = 2;
	var basis = 365;
	var maturityDate = calMaturityDate(form.MaturityDateY.value);
	
	var settlementDate = calSettlementDate(form.settlementDateY.value);	
	//var dayN = calN(issueDate, maturityDate, frequency, basis);	
	var prevcoupondate = calPrevCouponDate(settlementDate, maturityDate, frequency);
	var dayN = calN(prevcoupondate, maturityDate, frequency, basis);	
	var nextcoupondate = calNextCouponDate(settlementDate, maturityDate, frequency);
	//var nextcoupondate = calNextCouponDate(prevcoupondate, maturityDate, dayN, frequency);		
	var dsr = calDsr(settlementDate, maturityDate);	
	var dsc = calDsc(settlementDate, nextcoupondate);	
	var dayE = calE(nextcoupondate, prevcoupondate, basis, frequency);		
	var dayA = calA(prevcoupondate, settlementDate);	
	var rate = calRate(form.SemiAnnualCouponRateY.value);
	var par = form.BondPriceY.value;

	// form.redemption.value = redemption;
	// form.frequency.value = frequency;
	// form.basis.value = basis;		
	// form.maturitydate.value = maturityDate;
	// form.settlementdate.value = settlementDate;
	// form.issuedate.value = "---";
	// form.dayN.value = dayN;	
	// form.prevcoupondate.value = prevcoupondate;
	// form.nextcoupondate.value = nextcoupondate;
	// form.yld.value = "---";
	// form.dayDSC.value = dsc;
	// form.dayA.value = dayA;
	// form.dayE.value = dayE;	
	// form.rate.value = rate;	
	// form.dayDSR.value = dsr;
	// form.price.value = par;
	
	var yield;
	var p;
	var ytm, starytm, prevytm, tempprev;
	var offset;
	// if bond price is more than par value, ytm is less than coupon rate
	startytm = rate;	
	prevytm = startytm;
	for(var i = 2; i < 8; i++){
		offset = 1/Math.pow(10,i);
		// starting yield == coupon rate, guess and check by shifting yield by 0.01
		if (par >= 100) {
			ytm=startytm;
			p = calPrice(redemption, ytm, frequency, dayN, dsc, dayE, rate, dayA, dayE);
			while(p < par)
			{	
				prevytm = ytm;
				ytm=ytm-offset;
				p = calPrice(redemption, ytm, frequency, dayN, dsc, dayE, rate, dayA, dayE);
			}
			startytm = prevytm;
		} else {
			ytm=startytm;
			p = calPrice(redemption, ytm, frequency, dayN, dsc, dayE, rate, dayA, dayE);
			while(p > par)
			{	
				prevytm = ytm;
				ytm=ytm+offset;
				p = calPrice(redemption, ytm, frequency, dayN, dsc, dayE, rate, dayA, dayE);
			}
			startytm=prevytm;
		}
	}
	
	yield = 100*(Math.pow((ytm/2+1),2)-1);

	if(yield>100 || yield <-100){
		alert("此債券計算機只處理計算範圍在-100%至100%的收益率。請輸入另一組數據。");
	}else{
		var round = 0;
		if(yield<10 && yield >-10){round = 1;}else{round = 0;}
		form.YieldToMaturityDisplay.value = roundup(yield,round).toFixed(round);
	}
}
