//
// Rendering of DSL spectrum graphs  showing
// US/DS SNR and US/DS bits/tone
//
// This version does depend on an ubus version that support DSL line stattiscis  but 
// does not depend on chart.js or any other package

class DataSet {
	constructor (input, extractFunction) {
		this.groupSize = input.groupsize;
		this.numData = input.groups;
		// needs to be validated with various input
		this.maxX = this.numData * this.groupSize;
		this.data = input.data.map(extractFunction,
			{groupSize: this.groupSize}
		);
	}
}

function myBitsFunction(value, index, array) {
	return({x: index, y: value, error: false});
}

function mySnrFunction(value, index, array) {
	let result;

	if (value == null) {
		result = {
			x: index * this.groupSize,
			y: -40 ,
			error: true
		}
	} else {
		result = {
			x: index * this.groupSize,
			y: value,
			error: false
		}
	}

	return(result);
}

function myQLNFunction(value, index, array) {
	let result;

	if (value == null) {
		result = {
			x: index * this.groupSize,
			y:  - 150,
			error: true
		}
	} else {
		result = {
			x: index * this.groupSize,
			y:  value,
			error: false
		}
	}

	return(result);
}

function myHLOGFunction(value, index, array) {
	let result;

	if (value == null) {
		result = {
			x: index * this.groupSize,
			y: -100,
			error: true
		}
	} else {
		result = {
			x: index * this.groupSize,
			y: value,
			error: false
		}
	}

	return(result);
}

const usSnrData  = new DataSet(window.json['snr']['upstream'], mySnrFunction);
const dsSnrData  = new DataSet(window.json['snr']['downstream'], mySnrFunction);
const usBitsData = new DataSet(window.json['bits']['upstream'], myBitsFunction);
const dsBitsData = new DataSet(window.json['bits']['downstream'], myBitsFunction);
const usQLNData  = new DataSet(window.json['qln']['upstream'], myQLNFunction);
const dsQLNData  = new DataSet(window.json['qln']['downstream'], myQLNFunction);
const usHLOGData = new DataSet(window.json['hlog']['upstream'], myHLOGFunction);
const dsHLOGData = new DataSet(window.json['hlog']['downstream'], myHLOGFunction);
const pilotTonesData = window.json['pilot_tones'] || [];

const marginX = 50;
const marginY = 80;
let darkMode = document.getElementsByTagName("body")[0].parentNode.dataset.darkmode;

let bitsChart = {
	"config": {
		"canvas": document.getElementById("bitsChart"),
		"ctx" : document.getElementById("bitsChart").getContext("2d"),
		"minX" : 0,
		"maxX" : Math.max(dsBitsData.maxX, usBitsData.maxX),
		"stepX": Math.max(dsBitsData.maxX, usBitsData.maxX) / 16,
		"graphWidth" : document.getElementById("bitsChart").width - 2 * marginX,
		"lineWidth" : 1,
		"titleX" : _("Sub-carrier"),
		"minY" : 0,
		"maxY" : 16,
		"stepY": 2,
		"graphHeight" : document.getElementById("bitsChart").height - 2 * marginY,
		"titleY" : _("bits")
	},
	"dataSet" : [
		{
			"data" :usBitsData.data,
			"color":"YellowGreen",
			"title": ("Upstream bits allocation")
		},
		{
			"data" : dsBitsData.data,
			"color": "navy",
			"title": _("Downstream bits allocation")
		},
		{
			"lines": true,
			"data": pilotTonesData,
			"color": "red",
			"title": _("Pilot tones")
		}
	]
};

let dBChart = {
	"config": {
		"canvas": document.getElementById("dbChart"),
		"ctx" : document.getElementById("dbChart").getContext("2d"),
		"minX" : 0,
		"maxX" : Math.max(dsSnrData.maxX, usSnrData.maxX),
		"stepX": Math.max(dsSnrData.maxX, usSnrData.maxX) / 16,
		"graphWidth" : document.getElementById("dbChart").width - 2 * marginX,
		"lineWidth": 4,
		"titleX" : _("Sub-carrier"),
		"minY" : -40,
		"maxY" : 100,
		"stepY": 10,
		"graphHeight" : document.getElementById("dbChart").height - 2 * marginY,
		"titleY" : _("dB")
	},
	"dataSet" : [
		{
			"data" :usSnrData.data,
			"color":"Turquoise",
			"title": _("Upstream SNR")
		},
		{
			"data" : dsSnrData.data,
			"color": "Coral",
			"title" : _("Downstream SNR")
		}
	]
};

let qLNChart = {
	"config": {
		"canvas": document.getElementById("qlnChart"),
		"ctx" : document.getElementById("qlnChart").getContext("2d"),
		"minX" : 0,
		"maxX" : Math.max(dsQLNData.maxX, usQLNData.maxX),
		"stepX": Math.max(dsQLNData.maxX, usQLNData.maxX) / 16,
		"graphWidth" : document.getElementById("qlnChart").width - 2 * marginX,
		"lineWidth": 4,
		"titleX" : _("Sub-carrier"),
		"minY" : -150,
		"maxY" : -20,
		"stepY": 10,
		"graphHeight" : document.getElementById("qlnChart").height - 2 * marginY,
		"titleY" : _("dBm/Hz")
	},
	"dataSet" : [
		{
			"data" :usQLNData.data,
			"color":"brown",
			"title": _("Upstream QLN")
		},
		{
			"data" : dsQLNData.data,
			"color": "teal",
			"title" : _("Downstream QLN")
		}
	]
};

let hLogChart = {
	"config": {
		"canvas": document.getElementById("hlogChart"),
		"ctx" : document.getElementById("hlogChart").getContext("2d"),
		"minX" : 0,
		"maxX" : Math.max(dsHLOGData.maxX, usHLOGData.maxX),
		"stepX": Math.max(dsHLOGData.maxX, usHLOGData.maxX) / 16,
		"graphWidth" : document.getElementById("hlogChart").width - 2 * marginX,
		"lineWidth": 4,
		"titleX" : _("Sub-carrier"),
		"minY" : -100,
		"maxY" : 14,
		"stepY": 10,
		"graphHeight" : document.getElementById("hlogChart").height - 2 * marginY,
		"titleY" : _("dB")
	},
	"dataSet" : [
		{
			"data" :usHLOGData.data,
			"color":"#E8E800",
			"title": _("Upstream HLOG")
		},
		{
			"data" : dsHLOGData.data,
			"color": "darkmagenta",
			"title" : _("Downstream HLOG")
		}
	]
};

function drawChart (info) {
	drawAxisX(info.config, info.config.minX, info.config.maxX, info.config.stepX, info.config.titleX);
	drawAxisY(info.config, info.config.minY, info.config.maxY, info.config.stepY, info.config.titleY);

	drawLegend(info.config, info.dataSet);

	for (let item of info.dataSet) {
		if (item.lines === true) {
			drawLines(info.config, item.data, item.color);
		} else {
			drawData(info.config, item.data, item.color);
		}
	}
}

function drawBlocks(config, dataPoints, color, borders) {
	borders.map(drawBlock, {config, dataPoints, color, borders});
}

function drawLines(config, dataPoints, color) {
	let ctx = config.ctx;
	let len = dataPoints.length;
	let minX = config.minX;
	let maxX = config.maxX;
	let minY = config.minY;
	let maxY = config.maxY;

	ctx.strokeStyle = color;
	ctx.beginPath();

	for (let item of dataPoints) {
		let relX = (item - minX) / (maxX - minX);
		ctx.moveTo(relX * config.graphWidth + marginX, marginY);
		ctx.lineTo(relX * config.graphWidth + marginX, marginY + config.graphHeight);
	}

	ctx.stroke();
}

function drawData(config, dataPoints, color) {
	let ctx = config.ctx;
	let len = dataPoints.length;
	let minX =config.minX;
	let maxX = config.maxX;
	let minY = config.minY;
	let maxY = config.maxY;
	let startX = (dataPoints[0].x  - config.minX) / (config.maxX - config.minX)
	let startY = (config.minY - config.minY) / (config.maxY - config.minY)

	ctx.fillStyle = color;
	ctx.beginPath();
	ctx.moveTo(startX * config.graphWidth + marginX, marginY + config.graphHeight - startY * config.graphHeight);

	for (let i  = 1 ;  i < len  ; i++) {
		let relX = (dataPoints[i].x - minX) / (maxX - minX);
		let relY = (dataPoints[i].y - minY) / (maxY - minY);
		ctx.lineTo(relX * config.graphWidth + marginX, marginY + config.graphHeight - relY * config.graphHeight);
	}

	let endX = (dataPoints[len-1].x - minX) / (maxX - minX)
	let endY = (config.minY - minY) / (maxY - minY)

	ctx.lineTo(endX * config.graphWidth + marginX, marginY + config.graphHeight - endY * config.graphHeight);
	ctx.lineTo(startX * config.graphWidth + marginX, marginY + config.graphHeight - startY * config.graphHeight);
	ctx.closePath();
	ctx.fill();
}

function drawLegend(config, dataSet){
	let ctx = config.ctx;
	let graphWidth = config.graphWidth;
	let graphHeight = config.graphHeight;

	ctx.font = "12px Arial";

	let legendWidth = -10;
	for (let item of dataSet) {
		legendWidth += 50 + ctx.measureText(item.title).width;
	}

	var x = 0.5 * (graphWidth - legendWidth) + marginX;
	var y = config.canvas.height - marginY*1/4;

	for (let item of dataSet) {
		ctx.fillStyle = item.color;
		ctx.fillRect(x, y - 8, 30, 10);
		ctx.strokeStyle = "#C0C0C0";
		ctx.strokeRect(x, y - 8, 30, 10);

		if (darkMode == "true") {
			ctx.fillStyle = "#A0A0A0";
		} else {
			ctx.fillStyle = "#303030";
		}

		x += 40;

		ctx.textAlign = "left"
		ctx.fillText(item.title, x, y);

		x += ctx.measureText(item.title).width;
		x += 10;
	}
}

function drawAxisX(config, minValue, maxValue, step, title) {
	let ctx = config.ctx;
	let graphWidth = config.graphWidth;
	let graphHeight = config.graphHeight;

	ctx.font = "12px Arial";
	ctx.textAlign = "center";

	if (darkMode == "true") {
		ctx.strokeStyle = "#505050";
		ctx.fillStyle = "#A0A0A0";
	} else {
		ctx.strokeStyle = "#E0E0E0";
		ctx.fillStyle = "#303030";
	}

	for (let x = minValue ; x <= maxValue ; x=x+step) {
		let relX = (x - config.minX) / (config.maxX - config.minX);

		ctx.fillText(x , relX * graphWidth + marginX,  config.canvas.height - marginY*3/4);

		ctx.beginPath();
		ctx.moveTo(relX * graphWidth + marginX, marginY);
		ctx.lineTo(relX * graphWidth + marginX, config.canvas.height - marginY);
		ctx.stroke();
	}

	ctx.font = "12px Arial";
	ctx.textAlign = "center";
	ctx.fillText(title, config.canvas.width/2, config.canvas.height - marginY*2/4);
}

function drawAxisY(config, minValue, maxValue, step, title) {
	let ctx = config.ctx
	let graphWidth = config.graphWidth;
	let graphHeight = config.graphHeight;

	ctx.font = "12px Arial";
	ctx.textAlign = "center";

	if (darkMode == "true") {
		ctx.strokeStyle = "#505050";
		ctx.fillStyle = "#A0A0A0";
	} else {
		ctx.strokeStyle = "#E0E0E0";
		ctx.fillStyle = "#303030";
	}

	for (let y = minValue ; y <= maxValue ; y=y+step) {
		let relY = (y - config.minY) / (config.maxY - config.minY);

		ctx.fillText(y , marginX *2 / 3,  marginY + graphHeight - relY * graphHeight + 4);

		ctx.beginPath();
		ctx.moveTo(marginX, marginY + graphHeight - relY * graphHeight );
		ctx.lineTo(config.canvas.width - marginX, marginY + graphHeight - relY * graphHeight);
		ctx.stroke();
	}

	ctx.font = "12px Arial";
	ctx.textAlign = "center";
	ctx.translate(marginX/3, marginY + graphHeight / 2);
	ctx.rotate(-3.14 /2);
	ctx.fillText(title, 0, 0);
	ctx.rotate(3.14 /2)
	ctx.translate(-marginX/3,-(marginY + graphHeight / 2));
}

drawChart(dBChart);
drawChart(bitsChart);
drawChart(qLNChart);
drawChart(hLogChart);
