//knGraph, for drawing line graphs
// also includes line-drawing routines
function knSeries() {
	this.drawType="line"; //"line", "dots", "dotsAndLine", "bars"
	this.dotSize = 2; this.barSize=5;
	this.array = new Array();
	this.col="#000000";
}

knSeries.prototype.toString = function() {
	return(this.array.join(","));
}

//---------graph---------public methods-----------------------------------------------
function knGraph(div, minX, minY, maxX, maxY, col) {
	this._divnm = div;
	this._div = document.getElementById(div);
	
	this._width =  Math.floor(parseInt(getStyle((this._div), "width")) * 0.8);
	this._height =  Math.floor(parseInt(getStyle((this._div), "height")) * 0.8);
	this._top = Math.floor(parseInt(getStyle((this._div), "height")) * 0.1);
	this._left = Math.floor(parseInt(getStyle((this._div), "width")) * 0.1);

	this._bottom = this._top + this._height;
	this._right = this._left + this._width;
	this._minX = minX; this._maxX = maxX; this._midX = Math.floor((this._minX+this._maxX)/2);
	this._minY = minY; this._maxY = maxY; this._midY = Math.floor((this._minY+this._maxY)/2);
	this._scaleX = (this._right-this._left)/(this._maxX-this._minX);
	this._scaleY = (this._top-this._bottom)/(this._maxY-this._minY);
	this.col = col;
	
	
	//"public" properties
	this.nTicksX=3; this.nTicksY=3;
	this.title="";
	this.titleX="";
	this.titleY="";
	}

knGraph.prototype.setTitles = function(t, tx, ty) {
	this.title = t;
	this.titleX = tx;
	this.titleY = ty;
}

knGraph.prototype.clear = function() {
	while(this._div.hasChildNodes()) {
		this._div.removeChild(this._div.firstChild);
		}
	}

knGraph.prototype.makePoly = function(a,b,c,   start_, stop_, step) {
	if (!start_) start_ = this._minX;
	if (!stop_) stop_ = this._maxX;
	if (!step) step = (this._maxX-this._minX)/100;
	start = (start_<this._minX) ? this._minX : start_;
	stopp = (stop_>this._maxX) ? this._maxX : stop_;
	result = new knSeries();
	for (var i = start; i<=stopp; i += step) 	{
		if (a*i*i+b*i+c<=this._maxY && a*i*i+b*i+c>=this._minY) 
			result.array.push([i, a*i*i+b*i+c]);
		
		}
	return(result);	
}


function btw(x1,num,x2) {return(x1<=num && num<x2);}

knGraph.prototype.makeNDist = function(mn,sd,   start_, stop_, step) {
	if (!start_) var start = Math.max(mn-3*sd, this._minX);
	if (!stop_) var stop = Math.min(mn+3*sd, this._maxX);
	if (!step) var step = (this._maxX-this._minX)/1000;
	var start = (start<this._minX) ? this._minX : start;
	var stop = (stop>this._maxX) ? this._maxX : stop;
	
	result = new knSeries();
	var i = start;
	while (i<stop) {
		var nextnum = Math.exp(-(i-mn)*(i-mn)/(2*sd*sd))/sd/Math.sqrt(2*3.14);
		if (nextnum<this._maxY && nextnum>0.001) 
			result.array.push([i, nextnum]);
		if (btw(mn-sd/2, i, mn+sd/2)) i += sd/200;
			else i += sd/10;
	}
	return(result);	
}

knGraph.prototype.makeSeries = function(start, stopp, step, func) {
	result = new knSeries();
	for (var i = start; i<=stopp; i += step) 	
		{result.array.push([i, func(i)]);}
	return(result);	
}

knGraph.prototype.makeLineFromX = function(x,y) {
	result = new knSeries();
	result.array.push([x, 0]);
	result.array.push([x, y]);
	return(result);	
}

knGraph.prototype.makeLineFromY = function(x,y) {
	result = new knSeries();
	result.array.push([0, y]);
	result.array.push([x, y]);
	return(result);	
}

knGraph.prototype.makeRandom2D = function(num,unknown) {
	result = new knSeries();
	for(var i = 0; i < num; ++ i, 
		result.array.push([this._minX + Math.random()*(this._maxX - this._minX), 
							this._minY + Math.random()*(this._maxY - this._minY)])
		);
	return(result);	
}

//-------intermediate level methods--------------------------------------------------------------
knGraph.prototype.cX = function(dataX) {	return(Math.floor(this._left + (dataX-this._minX) * this._scaleX));}
knGraph.prototype.cY = function(dataY) {	return(Math.floor(this._bottom + (dataY-this._minY) * this._scaleY));}

knGraph.prototype.drawAxes = function() {
	//alert([this.cX(this._minX), this.cY(this._minY), this.cX(this._minX), this.cY(this._maxY), this.col]);
	//alert([this._minX,this._minY,this._minX,this._maxY]);
	knDrawLine(this._divnm, this.cX(this._minX), this.cY(this._minY), this.cX(this._minX), this.cY(this._maxY), this.col);
	knDrawLine(this._divnm, this.cX(this._minX), this.cY(this._minY), this.cX(this._maxX), this.cY(this._minY), this.col);
	for (var i=0; i<this.nTicksX; ++i) {
		this.drawTickX(i * (this._minX + this._maxX)/(this.nTicksX-1));
		this.drawLabelX(i * (this._minX + this._maxX)/(this.nTicksX-1));
		}
	for (var i=0; i<this.nTicksX; ++i) {
		this.drawTickY(i * (this._minY + this._maxY)/(this.nTicksY-1));
		this.drawLabelY(i * (this._minY + this._maxY)/(this.nTicksY-1));
		}
	this.drawLabel(this._midX,-this.titleX.length*3, this._minY,-20, this.titleX, "");	
	this.drawLabel(this._minX,-20, this._maxY,0,   this.titleY, "");	
	this.drawLabel(this._minX,20, this._maxY,-20, this.title, "graphTitle");	
}

knGraph.prototype.drawFrame = function() {
	knDrawLine(this._divnm, this.cX(this._minX), this.cY(this._minY), this.cX(this._minX), this.cY(this._maxY), this.col);
	knDrawLine(this._divnm, this.cX(this._minX), this.cY(this._minY), this.cX(this._maxX), this.cY(this._minY), this.col);
	knDrawLine(this._divnm, this.cX(this._maxX), this.cY(this._maxY), this.cX(this._minX), this.cY(this._maxY), this.col);
	knDrawLine(this._divnm, this.cX(this._maxX), this.cY(this._maxY), this.cX(this._maxX), this.cY(this._minY), this.col);
	for (var i=0; i<this.nTicksX; ++i) {
		this.drawTickX(i * (this._minX + this._maxX)/(this.nTicksX-1));
		this.drawLabelX(i * (this._minX + this._maxX)/(this.nTicksX-1));
		}
	for (var i=0; i<this.nTicksX; ++i) {
		this.drawTickY(i * (this._minY + this._maxY)/(this.nTicksY-1));
		this.drawLabelY(i * (this._minY + this._maxY)/(this.nTicksY-1));
		}
	this.drawLabel(this._midX,-this.titleX.length*3, this._minY,-20, this.titleX, "");	
	this.drawLabel(this._minX,-20, this._maxY,0,   this.titleY, "");	
	this.drawLabel(this._minX,20, this._maxY,-20, this.title, "graphTitle");	
}

knGraph.prototype.drawImage = function(img, x, y, w, h, ax, ay, alt) {
	var imgNode = document.createElement("img");
	imgNode.src = img; imgNode.alt = alt;
	if (w) imgNode.width = w; else w=getElementWidth(imgNode);
	if (h) imgNode.height = h; else h=getElementHeight(imgNode);
	var elNode = document.createElement("div");
	elNode.style.position="absolute";
	//debug("x", (this.cX(x)-w/2)+"px"); 
	switch(ax) {
		case("left"): elNode.style.left = (this.cX(x))+"px"; break;
		case("mid"): elNode.style.left = (this.cX(x)-w/2)+"px"; break;
		case("right"): elNode.style.left = (this.cX(x)-w)+"px"; break;
		default: elNode.style.left = (this.cX(x)-w/2)+"px"; break;
	}
	switch(ay) {
		case("top"): elNode.style.top = (this.cY(y))+"px"; break;
		case("mid"): elNode.style.top = (this.cY(y)+h/2)+"px"; break;
		case("bot"): elNode.style.top = (this.cY(y)+h)+"px"; break;
		default: elNode.style.top = (this.cY(y)+h/2)+"px"; break;
	}

	elNode.appendChild(imgNode);
	this._div.appendChild(elNode);
}

knGraph.prototype.drawLabel = function(x, xinc, y, yinc, titleX, classnm) {
	var txtNode = document.createTextNode(titleX);
	var elNode = document.createElement("div");
	elNode.className=classnm;
	elNode.style.position="absolute";
	elNode.style.left = (this.cX(x)+xinc)+"px";
	elNode.style.top = (this.cY(y)-yinc)+"px";
	elNode.style.color = this.col;
	elNode.appendChild(txtNode);
	this._div.appendChild(elNode);
}
knGraph.prototype.drawLabelXlbl = function(x, lbl, col) {
	var txtNode = document.createTextNode(lbl);
	var elNode = document.createElement("div");
	elNode.style.position="absolute";
	elNode.style.left = (this.cX(x)-5)+"px";
	elNode.style.top = (this.cY(this._minY)+5)+"px";
	elNode.style.color = col;
	elNode.appendChild(txtNode);
	this._div.appendChild(elNode);
}
knGraph.prototype.drawLabelX = function(x) {
	var txtNode = document.createTextNode(x);
	var elNode = document.createElement("div");
	elNode.style.position="absolute";
	elNode.style.left = (this.cX(x)-5)+"px";
	elNode.style.top = (this.cY(this._minY)+5)+"px";
	elNode.style.color = this.col;
	elNode.appendChild(txtNode);
	this._div.appendChild(elNode);
}

knGraph.prototype.drawLabelYlbl = function(y, lbl, col) {
	var txtNode = document.createTextNode(lbl);
	var elNode = document.createElement("div");
	elNode.style.position="absolute";
	elNode.style.left = (this.cX(this._minX)-25)+"px";
	elNode.style.top = (this.cY(y)-10)+"px";
	elNode.style.color = col;
	elNode.appendChild(txtNode);
	this._div.appendChild(elNode);
}

knGraph.prototype.drawLabelY = function(y) {
	var txtNode = document.createTextNode(y);
	var elNode = document.createElement("div");
	elNode.style.position="absolute";
	elNode.style.left = (this.cX(this._minX)-25)+"px";
	elNode.style.top = (this.cY(y)-10)+"px";
	elNode.style.color = this.col;
	elNode.appendChild(txtNode);
	this._div.appendChild(elNode);
}

knGraph.prototype.drawTickX = function(val) {
	var ht = 5;
	knDrawLine(this._divnm, this.cX(val), this.cY(this._minY)+ht, 
			this.cX(val), this.cY(this._minY)-ht, this.col);
}

knGraph.prototype.drawTickY = function(val) {
	var ht = 5;
	knDrawLine(this._divnm, this.cX(this._minY)-ht, this.cY(val), 
			this.cX(this._minY)+ht, this.cY(val), this.col);
}

knGraph.prototype.drawSeries = function(series, col) {
	if (!col && !series.col) col="#000000";
	if (!col && series.col) col=series.col;

	if (series.drawType=="line" || series.drawType=="dotsAndLine") {
		for(var i = 1; i<series.array.length; ++i) {
			knDrawLine(this._divnm, this.cX(series.array[i-1][0]), this.cY(series.array[i-1][1]), 
				this.cX(series.array[i][0]), this.cY(series.array[i][1]), col);
			}
		}
	
	if (series.drawType=="dots" || series.drawType=="dotsAndLine") {
		for(var i = 0; i<series.array.length; ++i) {
			knDrawRectSolid(this._divnm, this.cX(series.array[i][0])-series.dotSize, this.cY(series.array[i][1])-series.dotSize, 
				this.cX(series.array[i][0])+series.dotSize, this.cY(series.array[i][1])+series.dotSize, col);
			}
		}

	if (series.drawType=="bars") {
		for(var i = 0; i<series.array.length; ++i) {
			alert([this.cX(series.array[i][0])-series.barSize, this.cY(series.array[i][1]), 
				this.cX(series.array[i][0])+series.barSize, this.cY(0)]);
			knDrawRectSolid(this._divnm, this.cX(series.array[i][0])-series.barSize, this.cY(series.array[i][1]), 
				this.cX(series.array[i][0])+series.barSize, this.cY(0), col);
			}
		}

}

// -----------low level methods----------------------------------------------------



function knDrawLine(div, x0,y0,x2,y2,col) {
	if (Math.abs(x2-x0) > Math.abs(y2-y0)) knDrawLineH(div, x0,y0,x2,y2,col); 
	else knDrawLineV(div, x0,y0,x2,y2,col);
	}
	
function knDrawLineH(div, x0,y0,x2,y2,col) {
	var len = (y2==y0) ? (x2-x0) : (x2>x0 ? Math.floor((x2-x0)/Math.abs(y2-y0)):
											Math.ceil((x2-x0)/Math.abs(y2-y0)));
	var segs = (y2==y0) ? 1 : Math.abs(y2-y0);
	var signlen = x2>x0 ? 1 : -1;
	var signsegs = y2>y0 ? 1 : -1;
	var segArray = new Array();
	for (var i=0; i<segs; ++i) segArray.push(len);
	var expix = Math.abs(x2-x0) - Math.abs(len*segs);
	for (var i=0; i<expix; ++i) {
		segArray[Math.abs(Math.floor(i*len*segs / (expix+1) / len))] += signlen*1;
		}
	//alert(segArray);	
	var segdone = 0;
 	for (var i=0; i<segs; ++i) {
		knDrawLineSegH(div, x0+segdone,y0+signsegs*i,segArray[i],col);
		segdone+=segArray[i];
		}
	//alert(["line ",len, "*", signlen, segs, "*", signsegs, expix]);
}

function knDrawLineV(div, x0,y0,x2,y2,col) {

	var len = (x2==x0) ? (y2-y0) : (y2>y0 ? Math.floor((y2-y0)/Math.abs(x2-x0)):
											  Math.ceil((y2-y0)/Math.abs(x2-x0)));
	var segs = (x2==x0) ? 1 : Math.abs(x2-x0);
	var signlen = y2>y0 ? 1 : -1;
	var signsegs = x2>x0 ? 1 : -1;
	var segArray = new Array();
	for (var i=0; i<segs; ++i) segArray.push(len);
	var expix = Math.abs(y2-y0) - Math.abs(len*segs);
	//alert([len, segs, signlen, signsegs, expix]);
	for (var i=0; i<expix; ++i) {
		segArray[Math.abs(Math.floor(i*len*segs / (expix+1) / len))] += signlen*1;
		}
	var segdone = 0;
 	for (var i=0; i<segs; ++i) {
		knDrawLineSegV(div, x0+signsegs*i,y0+segdone,segArray[i],col);
		segdone+=segArray[i];
		}
}

function knDrawLineSegH(div, x0,y0,len,col) { len>0 ? knDrawRectSolid(div, x0,y0,x0+len,y0+1,col) :
												knDrawRectSolid(div, x0+len,y0,x0,y0+1,col); }
												//works bc len is negative
function knDrawLineSegV(div, x0,y0,len,col) { len>0 ? knDrawRectSolid(div, x0,y0,x0+1,y0+len,col):
												knDrawRectSolid(div, x0,y0+len,x0+1,y0,col); }
function knDrawRectSolid(div, x,y,x2,y2,col) {
	//alert(x + " " + y + " " + x2 + " " + y2); 
	var jg = document.getElementById(div);
	var rect = document.createElement("div");
	rect.style.left = x+"px";
	rect.style.top = y+"px";
	rect.style.width = (x2-x)+"px";
	rect.style.height =  (y2-y)+"px";
	//alert(rect.style.left + " " + rect.style.top + " " + rect.style.width + " " + rect.style.height);
	rect.style.position = "absolute";
	rect.style.backgroundColor = col;
	rect.style.overflow="hidden";
	jg.appendChild(rect);
	}
	
function testDrawLine(div) {
	knDrawLineSegH(div, 10,10,60, "#ff0000");
	knDrawLineSegH(div, 10,12,30, "#ff0000");
	knDrawLineSegH(div, 41,12,29, "#ff0000");
	knDrawRectSolid(div, 180,220,420,380,"#ddd");
	knDrawLine(div, 300,300, 420,220, "#f00");
	knDrawLine(div, 300,300, 420,300, "#0f0");
	knDrawLine(div, 300,300, 420,380, "#00f");
	knDrawLine(div, 300,300, 180,220, "#ff0");
	knDrawLine(div, 300,300, 180,300, "#0ff");
	knDrawLine(div, 300,300, 180,380, "#f0f");
	alert("hi");
	knDrawRectSolid(div, 220,180,380,420,"#ddd");
	knDrawLine(div, 300,300, 220,420, "#f00");
	knDrawLine(div, 300,300, 300,420, "#0f0");
	knDrawLine(div, 300,300, 380,420, "#00f");
	
	knDrawLine(div, 300,300, 220,180, "#ff0");
	knDrawLine(div, 300,300, 300,180, "#0ff");
	knDrawLine(div, 300,300, 380,180, "#f0f");
}
