/*
 *	DataSource.js
 *
 *	Michael Holder,  Noveber 2009
 *
 *	$Id: DataSource.js,v 1.38 2010/05/26 05:42:17 michaelholder Exp $
 *
 *  Copyright (C) 2007-2009 TRD Associates, LLC
 *
 *    - All rights reserved.
 */

//========================================================================

var controlFactors = {
	A2: [null, 2.660, 1.880, 1.023, 0.729, 0.577, 0.483, 0.419, 0.373, 0.337, 0.308],
	D3: [null, null, null, null, null, null, null, 0.076, 0.136, 0.184, 0.223],
	D4: [null, 3.267, 3.267, 2.575, 2.282, 2.115, 2.004, 1.924, 1.864, 1.816, 1.777]
};

//========================================================================

function downloadURL(URL) {
	if (window.confirm("A file 'dataset.csv' will be downloaded")){
		document.location=URL;
	}
}

//========================================================================

function descendingY(a,b)
{
	return b.Y() - a.Y();
}

//========================================================================

function ascendingY(a,b)
{
	return a.Y() - b.Y();
}

//========================================================================

function median(pts) {
	var median;
	
	var count = pts.length;
	
	pts.sort(ascendingY);
	
	var odd = count % 2;
	
	if (odd) {
		median = pts[Math.floor(count/2)].Y();
	} else {
		var i = Math.floor(count/2);
		var j = i - 1;
		median = (pts[i].Y() + pts[j].Y()) / 2;
	}
	
	return median;
}

//========================================================================

function yStats(pts) {

	var count = 0;
	var sum = 0.0;
	var yMax = -1e99;
	var yMin = 1e99;
	for ( var p in pts ) {
		count++;
		sum += pts[p].Y();
		if ( pts[p].Y() > yMax ) {
			yMax = pts[p].Y();
		}
		if ( pts[p].Y() < yMin ) {
			yMin = pts[p].Y();
		}
		
	}

	var xBar = sum / count;
	var range = yMax - yMin;
	
	var yySum = 0.0;
	var yy;
	for ( var p in pts ) {
		yy = ( pts[p].Y() - xBar );
		yySum += yy * yy;
	}
	
	var s = Math.sqrt( yySum / ( count - 1 ) );
	
	var result = {
		count:	count,
		max:	yMax,
		min:	yMin,
		sum:	sum,
		range:	range,
		sdev:	s,
		mean:	xBar
	};

	return result;
}

//========================================================================

function select(observations, column) {
	
	var value = 0;
	
	if (column == "XBAR") {
		
		for (var i in observations) {
			if (observations[i]) {
				var strgn = observations[i].value+"";
				if ( strgn.length > 0 ) {
					value += observations[i].value - 0;
				}
			}
		}
		value = value / observations.length;
		
	} else if(column == "SUM") {
		
		for (var i in observations) {
			if (observations[i]) {
				var strgn = observations[i].value+"";
				if ( strgn.length > 0 ) {
					value += observations[i].value - 0;
				}
			}
		}
		
	} else if(column == "RANGE") {
		
		var max = -1e99;
		var min = 1e99;
		
		for (var i in observations) {
			if (observations[i]) {
				var v = observations[i].value - 0;
				if (v > max) {
					max = v;
				}
				if (v < min) {
					min = v;
				}
			}
		}
		value = max - min;
		
	} else if((column>-1) && (column<21)) {
		value = null;
		if (observations[column]) {
			var strgn = observations[column].value+"";
			if (strgn.length > 0) {
				value = observations[column].value - 0;
			}
		}
	}
	
	return value;
}

//========================================================================

var lspcInt = /\d+/;
var lspcText = /\b\w+\b/;

function xLabel(label, ix) {
	
	var x = Date.parse(label);
	var xType = "date";
	if (isNaN(x)) {
		x = parseFloat(label);
		xType = "numeric";
		if (isNaN(x)) {
			x = lspcInt.exec(label);
			xType = lspcText.exec(label)[0];
			if (x == null) {
				x = ix;
				xType = "text";
			} else {
				x = x[0];
			}
		}
	}

	return [x, xType];
}

//========================================================================

function dataPoints(dataset, column ) {

	var pts = new Array();
	
	var numObservations = dataset.columns.length;
	if ((column == 'RANGE') && (numObservations == 1)) {
		for (var i = 0; i < dataset.samples.length - 1; i++) {
			var x = xLabel(dataset.samples[i+1].label, i);
			dataset.xType = x[1];
			var y = Math.abs(dataset.samples[i+1].observations[0].value - dataset.samples[i].observations[0].value);
			if (y != null) {
				var pt = new Point(x[0], y);
				pt.xText = dataset.samples[i+1].label+"";
				pts.push(pt);
			}
		}
	} else {
		for (var i in dataset.samples) { 
			var x = xLabel(dataset.samples[i].label, i);
			dataset.xType = x[1];
			var y = select(dataset.samples[i].observations, column);
			if (y != null) {
				var pt = new Point(x[0], y);
				pt.xText = dataset.samples[i].label+"";
				pts.push(pt);
			}
		}
	}	
	return pts;
}

//========================================================================

function blank( dataset, view, w, h ) {
	view.LiveSPC = null;
}

//========================================================================

function hbarChart(dataset, view, w, h ) {
	
	var Chart = new GraphPaper(view, "Frequency", "#C8E1FF", w, h );
	
	var samplePts = dataPoints(dataset, 0);
	
	var histogramPts = new Array();
	var normalPts = new Array();

	var stats = yStats(samplePts);
	
	//		System.out.println("mean = " + xBar + " s = " + s );
	
	var yMax = stats.max + 1.5 * stats.sdev;
	var yMin = stats.min - 1.5 * stats.sdev;
	
	var tics = Chart.TickMarks( yMax, yMin, 20 );
	
	var tick = tics[1].q - tics[0].q;
	var binMin = tics[0].q - 0;
	var last = tics.length - 1;
	var binMax = tics[last].q - 0;
	
	if ( yMin < binMin ) {
		binMin -= tick;
	}
	if ( yMax > binMax ) {
		binMax += tick;
	}
	
	var numBins = Math.floor(( binMax - binMin ) / tick);
	var bin = new Array();
	for (i = 0; i < numBins; i++ ) {
		bin[i] = 0;
	}
	var maxFrequency = -1;
	
	for ( var p in samplePts ) {
		var  index = Math.floor(( samplePts[p].Y() - binMin ) / tick);
		bin[index]++;
		if ( bin[index] > maxFrequency ) {
			maxFrequency = bin[index];
		}
	}
	
	var y = binMin + tick / 2.0;
	for ( var i = 0; i < numBins; i++ ) {
		//			System.out.println("count = " + bin[i] + " y = " + y );
		histogramPts.push( new Point( bin[i], y) );
		y += tick;
	}
	
	var inc = ( yMax - yMin ) / 99.0;
	for ( var i = 0; i < 100; i++ ) {
		y = i * inc + yMin;
		var z = ( y - stats.mean ) / stats.sdev;
		var p = Math.exp(-z * z) * maxFrequency;
		//			System.out.println("p = " + p + " y = " + y );
		normalPts.push( new Point( p, y) );
	}
	
	Chart.init();
	Chart.addData( "HBAR", dataset.name, "#FF0000", histogramPts );
	Chart.addData( "LINE", "Normal Distribution", "#0000FF", normalPts );
	Chart.setYlimits( yMax, yMin );
	Chart.specLimits(dataset.specMax, dataset.specMin );
	Chart.paint();

	view.LiveSPC = Chart;
}

//========================================================================

function gasGauge(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, "-na-", "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var xbarPts = dataPoints(dataset, "XBAR");
	var rangePts = dataPoints(dataset, "RANGE");
	
	var stats = yStats(xbarPts);
	var rStats = yStats(rangePts);
	
	var A2 = controlFactors.A2[numObservations];
	var UCL = stats.mean + rStats.mean * A2;
	var LCL = stats.mean - rStats.mean * A2;
	
	Chart.init();
	Chart.addData( "GASGAUGE", dataset.name, "#FF0000", xbarPts );
	
	Chart.spcLimits(stats.mean, "X", 1, UCL, LCL);

	Chart.specLimits(dataset.specMax, dataset.specMin );
	
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function stopLight(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, "-na-", "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var xbarPts = dataPoints(dataset, "XBAR");
	var rangePts = dataPoints(dataset, "RANGE");
	
	var stats = yStats(xbarPts);
	var rStats = yStats(rangePts);
	
	var A2 = controlFactors.A2[numObservations];
	var UCL = stats.mean + rStats.mean * A2;
	var LCL = stats.mean - rStats.mean * A2;

	Chart.init();
	Chart.addData( "STOPLIGHT", dataset.name, "#FF0000", xbarPts );
	
	Chart.spcLimits(stats.mean, "X", 1, UCL, LCL);
	
	Chart.specLimits(dataset.specMax, dataset.specMin );
	
	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function runChart(dataset, view, w, h) {

	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );

	Chart.init();

	for (var column in dataset.columns) {
		
		var samplePts = dataPoints(dataset, column);
		
		Chart.addData( "RUN", dataset.columns[column], "#FF0000", samplePts );
		
	}
	
	Chart.specLimits(dataset.specMax, dataset.specMin);

	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function vbarChart(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );
	
	Chart.init();
	Chart.checkY(0.0);
	
	var numPts;
	for (var column in dataset.columns) {
		
		var samplePts = dataPoints(dataset, column);
		numPts = samplePts.length;
		
		Chart.addData( "VBAR", dataset.columns[column], "#FF0000", samplePts );
		
	}
	
	Chart.specLimits(dataset.specMax, dataset.specMin);

	Chart.xType(dataset.xType);
	Chart.paint(numPts);
	
	view.LiveSPC = Chart;
}

//========================================================================

function pareto(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.columns[0], "#C8E1FF", w, h );
	
	Chart.init();
	Chart.checkY(0.0);
	
	var paretoCnts = [];

	for (var s in dataset.samples) {
		var smpl = dataset.samples[s];
		for(var o in smpl.observations) {
			var key = smpl.observations[o].value;
			if (paretoCnts[key]) {
				paretoCnts[key]++;
			} else {
				paretoCnts[key] = 1;
			}				
		}
	}
	
	var paretoPts = new Array();
	for (var key in paretoCnts) {
		var pt = new Point(0, paretoCnts[key]);
		pt.xText = key+"";
		paretoPts.push( pt );
	}
	
	paretoPts.sort(descendingY);
	
	var numPts = paretoPts.length;
	var maxPoints = Math.floor(w / 25);
	var others = maxPoints - 1;
	if (numPts > maxPoints) {
		for (var i = maxPoints; i < paretoPts.length; i++) {
			paretoPts[others].y += paretoPts[i].y;
		}
		paretoPts[others].xText = "[others]";
		for (var i = 0; i < numPts-maxPoints; i++) {
			paretoPts.pop();
		}
		numPts = maxPoints;
	}
	

	Chart.addData( "PARETO", "Frequency of "+dataset.name, "#FF0000", paretoPts );
	
	Chart.xType('text');
	Chart.paint(numPts);

	view.LiveSPC = Chart;
}

//========================================================================

function xbarChart(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var xbarPts = dataPoints(dataset, "XBAR");
	var rangePts = dataPoints(dataset, "RANGE");
	
	var stats = yStats(xbarPts);
	var rStats = yStats(rangePts);
	
	var A2 = controlFactors.A2[numObservations];
	var UCL = stats.mean + rStats.mean * A2;
	var LCL = stats.mean - rStats.mean * A2;
	
	Chart.init();
	Chart.addData( "STAT", dataset.name, "#FF0000", xbarPts );
	
	Chart.spcLimits(stats.mean, "X", numObservations, UCL, LCL);
	
	Chart.specLimits(dataset.specMax, dataset.specMin);
	
	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function rChart(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var rangePts = dataPoints(dataset, "RANGE");
	
	var stats = yStats(rangePts);
	
	var D3 = controlFactors.D3[numObservations];
	var D4 = controlFactors.D4[numObservations];
	var UCL = stats.mean * D4;
	var LCL = stats.mean * D3;
		
	Chart.init();
	Chart.addData( "STAT", dataset.name, "#FF0000", rangePts );
	
	Chart.spcLimits(stats.mean, "R", 1, UCL, LCL);
		
	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function cChart(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var pts = dataPoints(dataset, "SUM");
	
	var stats = yStats(pts);
	
	var limit = 3 * Math.sqrt(stats.mean);
	var UCL = stats.mean + limit;
	var LCL = stats.mean - limit;
	if (LCL < 0.0) {
		LCL = 0.0;
	}
	
	Chart.init();
	Chart.addData( "STAT", dataset.name, "#FF0000", pts );
	
	Chart.spcLimits(stats.mean, "C", 1, UCL, LCL);
	
	Chart.specLimits(dataset.specMax, dataset.specMin);

	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function pChart(dataset, view, w, h) {
	
	var Chart = new GraphPaper(view, dataset.xLabel, "#C8E1FF", w, h );
	
	var numObservations = dataset.columns.length;
	
	var pPts = dataPoints(dataset, 0);
	var nPts = dataPoints(dataset, 1);
	
	for (var i in pPts) {
		pPts[i].y = pPts[i].Y() / nPts[i].Y();
	}
	
	var pStats = yStats(pPts);
	var nStats = yStats(nPts);
	
	var limit = 3 * Math.sqrt(pStats.mean * (1.0 - pStats.mean) / nStats.mean);
	var UCL = pStats.mean + limit;
	var LCL = pStats.mean - limit;
	if (LCL < 0.0) {
		LCL = 0.0;
	}
	
	Chart.init();
	Chart.addData( "STAT", dataset.name, "#FF0000", pPts );
	
	Chart.spcLimits(pStats.mean, "P", 1, UCL, LCL);
	
	Chart.specLimits(dataset.specMax, dataset.specMin);
	
	Chart.xType(dataset.xType);
	Chart.paint();
	
	view.LiveSPC = Chart;
}

//========================================================================

function basicStats(dataset, view, w, h) {
	
	var stat = [ 'count', 'mean', 'median', 'max', 'min', 'range', 'sdev', 'sum' ];
	
	var numObservations = dataset.columns.length;
	
	var stats = [];
	
	for (var i = 0; i < numObservations; i++ ) {
		var pts = dataPoints(dataset, i);
		stats[i] = yStats(pts);
		stats[i].median = median(pts);
	}
	
	var sameSamples = true;
	var nObs = stats[0].count;
	for (var i = 0; i < numObservations; i++ ) {
		if(nObs != stats[i].count) {
			sameSamples = false;
			break;
		}
	}
		
	var fntSize = Math.floor(h / 27);
	var html = "<table cellspacing=0 class=\"statsTable\" style=\"font-size: "+fntSize+"pt\">";
	html += "<caption>"+dataset.name+"</caption>";
	
	
	
	html += "<tr><th></th>";
	for (var i in dataset.columns) {
		html += "<th class=\"column\">"+dataset.columns[i]+"</th>";
	}
	html += "</tr>";
	
	for (var value in stat) {
	
		html += "<tr><th class=\"label\">"+stat[value]+"</th>";
		
		for (var i = 0; i < numObservations; i++ ) {
			var v = stats[i][stat[value]];
			if ((value < 1) && (sameSamples)) {
				html += "<td colspan="+numObservations+">"+v+"</td>";
				break;
			} else {
				v = v.toPrecision(3);
				html += "<td>"+v+"</td>";
			}
			
		}
		
		html += "</tr>";
	}
	
	view.innerHTML = html+"</table>";

}

//========================================================================

function discussion(dataset, view, w, h) {
	var wd = w - 65;
	var ht = h - 50;
	view.innerHTML = "<div class=\"discussDiv\"><iframe class=\"discussFrame\" width=" + wd + "px height=" + ht +
		"px src=\"/discussion.pl?CLASS=REPORT&OBJECT=" + document.LiveSPC.reportID + "\"/></div>";
	
}

//========================================================================

function UpdateRep( SID, chart ) {
	
	var URL = "export.pl";
	
	var xmlHttp;
	
	if (window.XMLHttpRequest) {
		xmlHttp = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		try {
			xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch(e) {
			try {
				xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch(e) {
				throw(e);
			}
		}
	}
	xmlHttp.open("POST", URL, true);
	xmlHttp.onreadystatechange = function() {
		if (xmlHttp.readyState == 4) {
			
			var dataset = jsonParse(xmlHttp.responseText);
			if (dataset.updated) {
				var view = document.getElementById(chart.elementID);
				
				if (view.LiveSPC) {
					var chartObject = view.LiveSPC;
					chartObject.paper.remove();
				}
				
				chart.updated = dataset.updated;
				
				chart.representation(dataset, view, chart.width, chart.height);
			}
		}
	}
	xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	xmlHttp.send("SID="+SID+"&datasetID="+chart.datasetID+"&updated="+chart.updated);	
}

//========================================================================

function UpdateLiveSPC() {
	
	var d = new Date();
	
	var chart = document.LiveSPC.charts;
	
	for (var i in chart) {
		UpdateRep(document.LiveSPC.SID, chart[i]);
	}
	
}
