// date graph: turns a <canvas> element into a graph with date/time along the x-axis and bilirubin level (0-22) along y-axis
(function($){
	var tz = (new Date).getTimezoneOffset(); // this should never change (unless you're doing this at the daylight saving switch)
	
	$.ui.widget.subclass ('ui.biligraph', {
		_init: function(){
			this.c = $('<canvas>').appendTo(this.element)[0];
			this.redraw();
		},
		redraw: function(){
			$(this.c).attr ({
				width: this.element.width()-this.options.xmargin,
				height: this.element.height()-this.options.ymargin
			}).css({
				marginLeft: this.options.xmargin
			});
			if (window.G_vmlCanvasManager) {
				G_vmlCanvasManager.initElement(this.c); // play nice with IE
			}
			this.ctx = this.c.getContext('2d'); // if it won't work, it will throw an error here
			this.ctx.save();
			this.ctx.translate (0, this.c.height);
			this.ctx.scale (1, -1);
			this.setBirth (this.options.bd);
			this._grabColors();
			this._drawGraph();
			this._drawCurves(this.options.plotset);
			this.ctx.restore();
		},
		setBirth: function(d){
			this.birth = d.getTime()/(1000*60*60)-tz/60; // birth time in hours since the epoch, local time
			this.zero = Math.floor(this.birth/4) * 4 - 4; // the x==0 point of the graph, rounded to 4 hours before birth
		},
		_h2p: function(h){ // convert  hours from this.zero to pixels along the x-axis
			return h*this.c.width/this.options.hours;
		},
		_a2p: function(a){ // convert age in hours to pixels along the x-axis
			return this._h2p(a + this.birth - this.zero);
		},
		_b2p: function(b){ // convert a bilirubin level to pixels along the y-axis
			return b*this.c.height/this.options.maxlevel;
		},
		_h2t: function(h){ // convert hours from this.zero to unix ms
			return (h+this.zero)*60*60*1000+tz*60*1000;
		},
		_timestr: function(h){ // h hours from this.zero, as a time
			h = Math.floor((h+this.zero) % 24);
			if (h == 0) return "12<br/>AM";
			else if (h == 12) return "12<br/>PM";
			else if (h < 12) return h+"<br/>AM";
			else return (h-12)+"<br/>PM";
		},
		_datestr: function(h){ // h hours from this.zero, as a date
			if (h%24 < 1) h+= 1; // avoid rounding errors
			var d = new Date(this._h2t(h));
			return (d.getMonth()+1)+'/'+d.getDate();
		},
		_drawGraph: function(){
			// erase the old information
			this.element.find('span').remove();
			this.ctx.fillStyle = this.background;
			this.ctx.fillRect (0, 0, this.c.width, this.c.height);
			this._drawXLines();
			this._drawYLines();		
			this._drawPoints(this.options.points);
		},
		_drawXLines: function(){
			var c = this.ctx;
			for (var i = 0; i <= this.options.maxlevel; i += 2){
				var y = this._b2p(i);
				c.strokeStyle = this.border;
				c.beginPath();
				c.moveTo (0, y);
				c.lineTo (this.c.width, y);
				c.stroke();
				if (i > 0 && i < this.options.maxlevel) { // don't show labels that intersect the border
					var label = $('<span>').text(i).css({
						position: 'absolute',
						display: 'block',
						right: this.c.width,
						paddingRight: '0.5em'
					}).appendTo(this.element);
					label.css({bottom: y+this.options.ymargin-label.height()/2});
				}
			}
		},
		_drawYLines: function(){
			var c = this.ctx;
			for (var i = 0; i <= this.options.hours; i += 4){
				var x = this._h2p(i);
				c.strokeStyle = this.border;
				c.beginPath();
				c.moveTo (x, 0);
				c.lineTo (x, this.c.height);
				c.stroke();
				if (i < this.options.hours) { // don't show labels that intersect the border
					var label =$('<span>').html(this._timestr(i)).css({
						textAlign: 'center',
						position: 'absolute',
						display: 'block',
						top: this.c.height,
						fontSize: '0.8em'
					}).appendTo(this.element);
					label.css({left: x+this.options.xmargin-label.width()/2});
				}
				if ((i == 0 && (this.zero%24 > 20)) || (i+this.zero) % 24 == 0){ // need day label (the zero%24 > 20 keeps the labels from running into each other)
					$('<span>').text(this._datestr(i)).css({
						position: 'absolute',
						display: 'block',
						top: this.c.height+label.height(),
						left: x+this.options.xmargin
					}).appendTo(this.element);
				}
				if (i == 0) label.hide(); // the first label we need to calculate heights but it's ugly
			}
			for (i = 0; i < this.options.hours; i += 24){
				c.strokeStyle = this.color;
				c.beginPath();
				c.moveTo (this._a2p(i), 0);
				c.lineTo (this._a2p(i), this.c.height);
				c.stroke();			
				$('<span>').text(i==0 ? "Birth" : i+ " hours").css({
					paddingLeft: '0.5em',
					position: 'absolute',
					display: 'block',
					top: 0,
					left: this._a2p(i)+this.options.xmargin,
					color: this.color
				}).appendTo(this.element);
			}
		},
		_drawCurves: function (plotset){
			// see the bili.js file for the structure of a plotset
			$('#graphtitle').text(plotset.title);
			var self = this;
			$.each(plotset.lines, function(){
				self._drawCurve (bili.hours, this);
			}); 
			$.each (plotset.labels, function(){
				self._drawLabel (this[0], this[1], this[2]);
			});
		},
		_drawCurve: function(hours, values){
			var c = this.ctx;
			c.strokeStyle = this.color;
			c.lineWidth = 3;
			c.lineCap = c.lineJoin = "round";
			c.beginPath();
			c.moveTo (this._a2p(hours[0]), this._b2p(values[0]));
			for (var i = 1; i < hours.length; ++i){
				c.lineTo (this._a2p(hours[i]), this._b2p(values[i]));
			}
			c.stroke();
		},
		_drawLabel: function(label, hour, value){
			var label = $('<span>').text(label).css({
				fontFamily: 'sans-serif',
				position: 'absolute',
				display: 'block',
				bottom: this._b2p (value)+this.options.ymargin,
				left: this._a2p(hour)+this.options.xmargin,
				color: this.color
			}).appendTo(this.element);
		},
		_drawPoints: function (points){
			var c = this.ctx, self = this, size = 7;
			$.each(points, function(){
				c.save();
				c.translate(self._a2p(this[0]), self._b2p(this[1]));
				c.beginPath();
				c.moveTo(-size, -size);
				c.lineTo(size, size);
				c.moveTo(size, -size);
				c.lineTo(-size, size);
				c.lineWidth = 6;
				c.strokeStyle = self.color;
				c.lineCap = "round";
				c.stroke();
				//--size;
				c.beginPath();
				c.moveTo(-size, -size);
				c.lineTo(size, size);
				c.moveTo(size, -size);
				c.lineTo(-size, size);
				c.lineWidth = 2;
				c.strokeStyle = self.background;
				c.stroke();
				c.restore();
			});
		},
		_grabColors: function(){
			this.background = this.border = this.color = 'black';
			// copy colors/fonts from the UI elements
			var div = $('<div class="ui-state-default">').appendTo(this.element);
			this.background = div.css('background-color');
			this.border = div.css('border-left-color');
			this.color = div.css('color');
			div.remove();
		},
		options: {
			bd: new Date(), // birthdate
			plotset: plotsets.risk,
			points: [],
			xmargin: 50, // pixels for x labels
			ymargin: 75, // pixels for y labels
			hours: 130, // total hours to show on graph
			maxlevel: 26 // maximum bilirubin level to show on graph
		}
	});
	
})(jQuery);