function convertRange(value,oldMin,oldMax,newMin,newMax) {
  return (Math.round(((((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin) * 10000)/10000)
}

function hexToRgbFloat(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    0: convertRange(parseInt(result[1],16), 0, 255, 0, 1),
    1: convertRange(parseInt(result[2],16), 0, 255, 0, 1),
    2: convertRange(parseInt(result[3],16), 0, 255, 0, 1)
  } : null;
}

var LastTouchTime = 0;
var LastTouchEvent = null;

function TouchHandler(ob)
{
	if (ob.pointerType == "mouse") 
		NavMenu(ob);
	else
	{
		if (LastTouchEvent != null)
		{
			if (LastTouchEvent.target == ob.target)
			{
				var td = (new Date()) - LastTouchTime;
				if (td < 2000)
					NavMenu(ob);
			}	
		}
		LastTouchTime = new Date();
		LastTouchEvent = ob;
	}
}

var ColourSequence = ["gold", "blue", "green", "yellow", "purple", "grey", "darkgreen", "pink", "brown", "slateblue", "violet", "orange"];	

function GetSequenceColour(clrid)
{
	return ColourSequence[clrid % ColourSequence.length];
}	

var hasupdates = false;

var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
var timeParserHD = d3.timeParse("%Y-%m-%d %H:%M:%S.%f");

function parseDateTime(dt)
{
	var tm = null;
	try
	{
		tm = timeParser(dt)	
		if (tm == null)
		{
			tm = timeParserHD(dt);
		}		
	}
	catch(e)
	{
		tm = timeParserHD(dt);
	}	
	return tm;
}

function CleanTitles(dta)
{
	//De-mangle Names
	if (dta.length == 1)
	{
		var pieces = dta[0].name.split(' - ');
		if (pieces.length > 1)
		{
			dta[0].name = pieces[1];			
		}
		return dta;
	}
	
	//Find common start
	var common = dta[0].name;
	for(var x=1;x<dta.length;x++)
	{
		for(var q=0;q<common.length;q++)
		{
			if (dta[x].name[q] != common[q])
			{
				common = dta[x].name.substr(0,q);
				break;
			}
		}
		if (common == "") break;
	}
	
	//Remove common start
	if (common != "")
	{
		var ln = common.length;
		for (var x=0;x<dta.length;x++)
		{
			dta[x].name = dta[x].name.substr(ln-1);
		}
	}
	
	//Calculate Common End
	var common = dta[0].name;
	for(var x=1;x<dta.length;x++)
	{
		for(var q=0;q<common.length;q++)
		{
			if (dta[x].name[dta[x].name.length - q] != common[common.length-q])
			{
				common = dta[x].name.substr(dta[x].name.length - q);
				break;
			}
		}
		if (common == "") break;
	}
	
	//Remove common start
	if (common != "")
	{
		//console.log("Removing Common End: " + common);
		var ln = common.length;
		for (var x=0;x<dta.length;x++)
		{
			dta[x].name = dta[x].name.substr(0,dta[x].name.length - (ln-1));
		}
	}
	
	return dta;
}

function DashCalcGradientColourRGB(perc, gradientarray)
{
	var prevcolour = [255,255,255];
	var prevpoint = 0;
	
	var nextcolour = [0,0,0];
	var nextpoint = 0;
	
	for(var x=0;x<gradientarray.length;x++)
	{
		if (gradientarray[x][0] > perc)
		{
			nextcolour = gradientarray[x][1];
			nextpoint = gradientarray[x][0];
			break;
		}
		
		prevcolour = gradientarray[x][1];
		prevpoint = gradientarray[x][0];
	}
	
	var diff = nextpoint - prevpoint;
	var xpert = (perc - prevpoint) / diff;
	var ypert = 1-xpert;
	
	var fin = [(nextcolour[0] * xpert) + (prevcolour[0] * ypert),(nextcolour[1] * xpert) + (prevcolour[1] * ypert),(nextcolour[2] * xpert) + (prevcolour[2] * ypert)];
	return 'rgb(' + ((Math.round(fin[0]*100)/100)*255) + ',' + ((Math.round(fin[1]*100)/100)*255) + ',' + ((Math.round(fin[2]*100)/100)*255) + ')';
}

function DashCalcGradientBandsRGB(perc, gradientarray)
{
	var prevcolour = [255,255,255];
	var prevpoint = 0;
	
	var nextcolour = [0,0,0];
	var nextpoint = 0;
	
	for(var x=0;x<gradientarray.length;x++)
	{
		if (gradientarray[x][0] > perc)
		{
			nextcolour = gradientarray[x][1];
			nextpoint = gradientarray[x][0];
			break;
		}
		
		prevcolour = gradientarray[x][1];
		prevpoint = gradientarray[x][0];
	}
	
	var diff = nextpoint - prevpoint;
	var xpert = (perc - prevpoint) / diff;
	var ypert = 1-xpert;
	
	var fin = [(nextcolour[0] * xpert) + (prevcolour[0] * ypert),(nextcolour[1] * xpert) + (prevcolour[1] * ypert),(nextcolour[2] * xpert) + (prevcolour[2] * ypert)];
	return 'rgb(' + ((Math.round(fin[0]*100)/100)*255) + ',' + ((Math.round(fin[1]*100)/100)*255) + ',' + ((Math.round(fin[2]*100)/100)*255) + ')';
}

var dashdata = {
	queries: [],
	livepoints: [],
	histpoints: [],
	timer: 0,
	hasupdates: false,
	hmi: null,
	ardiserver: "",	
	missedupdatetimer: null,
	AddLive: function(code,panel,index) {
		var bits = code.split(':');
		
		var d = {
			addr: code,
			ob: panel,
			index: index,
			ast: parseInt(bits[0]),
			prop: parseInt(bits[1])
		};		
		this.livepoints.push(d);
		panel.lastupdate = null;
		hasupdates = true;
	},
	AddHist: function (code,panel,index) {
		var bits = code.split(':');
		
		var d = {
			addr: code,
			ob: panel,
			index: index,
			ast: parseInt(bits[0]),
			prop: parseInt(bits[1])
		};		
		this.histpoints.push(d);
		hasupdates = true;
	},
	Query: function(start,end,res) {
		var qry = "";
		var props = [];
		var asts = [];
		var properties = "";
		var assets = "";
		var bits = null;
		var x = 0;
		if (res == undefined) res = "";
		
		for(x=0;x<this.livepoints.length;x++)
		{
			bits = this.livepoints[x].addr.split(':');
			if (!asts.includes(bits[0]))
				asts.push(bits[0]);
			if (!props.includes(bits[1]))
				props.push(bits[1]);
		}
		for(x=0;x<this.histpoints.length;x++)
		{
			bits = this.histpoints[x].addr.split(':');
			if (!asts.includes(bits[0]))
				asts.push(bits[0]);
			if (!props.includes(bits[1]))
				props.push(bits[1]);
		}
		
		qry = "(";
		var first = true;
		for(x=0;x<asts.length;x++)
		{
			if (first == true)
				first = false;
			else
				qry += ",";
			
			qry += asts[x];
		}
		qry += ") ASSETBYID (";
		first = true;
		for(x=0;x<props.length;x++)
		{
			if (first == true)
				first = false;
			else
				qry += ",";
			
			qry += props[x];
		}
		grain = 1000;
		if (res != "")
		{
			grain = parseFloat(res);
		}
		qry += ') PROPERTYBYID VALUES {"start": "' + start + '","end": "' + end + '","grain": -' + grain + '} GETHISTORY';
		console.log(qry);
		
		var url = location.protocol + "//" + this.ardiserver.replace("//","/") + "/aql/api/query?query=" + qry;
		console.log(url);
		$.post(url,function(data) {
			for(x=0;x<data.results.length;x++)
			{
				if (data.results[x].type != "pointlist") continue;
				for(var y=0;y<data.results[x].value.length;y++)
				{
					var obx = data.results[x].value[y];
					var src = parseInt(obx.sourceid);
					var prop = parseInt(obx.propid);
					dashdata.UpdatedHistoryChannel(src,prop,obx);
				}
			}
		});
	},
	UpdatedHistoryChannel: function (ast,prop,content) {
		for(var x=0;x<this.histpoints.length;x++)
		{
			if (this.histpoints[x].ast == ast)
			{
				if (this.histpoints[x].prop == prop)
				{
					if (content.history)
						this.histpoints[x].ob.data[this.histpoints[x].index].hist = content.history;
					else
					{
						var hist = null;
						try {
							hist = [["2022-02-01 09:00:00",content.rawvalue.split('|')[0]],["2022-02-02 09:00:00",content.rawvalue.split('|')[0]]];
						} catch (e) {
							hist = [["2022-02-01 09:00:00",content.rawvalue],["2022-02-02 09:00:00",content.rawvalue]];
						}
						this.histpoints[x].ob.data[this.histpoints[x].index].hist = hist;
					}
					hasupdates = true;
				}
			}
		}
	},
	TimeUpdate: function() {
		/*for(var x=0;x<this.livepoints.length;x++)
		{
			if (this.livepoints[x].addr == "RANDOM")
			{
				this.livepoints[x].ob.data[this.livepoints[x].index].value = Math.random() * 100;
				hasupdates = true;
			}
		}*/
		if (hasupdates == true)
		{
			hasupdates = false;
			UpdateData();
		}		
	},
	NewData: function (q,dt) {
		//console.log(q + ": " + dt);//alert('New Data: ' + dt);
		this.livepoints[q].ob.data[this.livepoints[q].index].value = dt;
		
		if (this.livepoints[q].ob.data.length > 15)
		{
			var ob = this.livepoints[q].ob;
			
			var ts = (new Date()).getTime();
			if ((ob.lastupdate == null) || (ob.lastupdate + 500 < ts))
			{
				//console.log("Updating Panel " + ob.number);
				this.livepoints[q].ob.lastupdate = ts;
				this.livepoints[q].ob.update(this.livepoints[q].ob.data);	
			}
			else
			{
				var ctx = this;			
				if (this.missedupdatetimer == null)
				{
					this.missedupdatetimer = window.setTimeout(500,function() {
						ctx.missedupdatetimer = null;
						ctx.UpdateData();				
					});
				}
			}
		}
		else
		{
			this.livepoints[q].ob.update(this.livepoints[q].ob.data);	
		}
		
		if (this.livepoints[q].ob == tooltipob)
		{
			if (this.livepoints[q].index == tooltipid)
			{
				//console.log("Updating Tooltip");
				UpdateTooltipInPlace();
			}
		}
	},
	Reconnect: function() {
		this.hmi.Connect(this.ardiserver);
	},
	Start: function(srv) {
		this.hmi = HMIPanel();
		this.ardiserver = srv;
		for(var x=0;x<this.livepoints.length;x++)
		{
			if (this.livepoints[x].addr != "RANDOM")
			{
				//Add to ARDI Subscription
				var q = x+0;
				this.hmi.Subscribe(this.livepoints[x].addr,(function(index) { return function(cbx) {
					dashdata.NewData(index,cbx);
				}}(q)));
			}
		}
		
		this.hmi.Connect(srv);		
		this.timer = window.setInterval("dashdata.TimeUpdate();",1000);
		window.addEventListener("visibilitychange", function() {
			if (document.visibilityState === "visible")
			{
				console.log("Connecting - Visibility Changed");
				dashdata.Reconnect();
			}
			else
			{
				console.log("Disconnecting - Visibility Changed");
				dashdata.hmi.Close();				
			}
		});
	},
	Stop: function () {
		if (this.timer != 0)
		{
			window.clearInterval(this.timer);
		}
	}
}

function ShowTooltip(element)
{
	$('#tooltip').css('left',element.clientX);
	$('#tooltip').css('top',element.clientY);
	$('#tooltip').css('display','block');
	//$('#tooltip').stop(true,true).fadeIn(200);
	var panel = element.currentTarget.getAttribute('data-panel');
	var item = element.currentTarget.getAttribute('data-id');
	
	tooltipob = panelsettings[panel];
	tooltipid = item;
	tooltipcontext = element;
	try
	{
		//console.log("Creating Tooltip");
		$('#tooltip').html(tooltipob.updateTooltip(tooltipid,tooltipcontext));
	}
	catch(e)
	{
	}
}

function UpdateTooltipInPlace()
{
	try
	{
		//console.log("Updating Tooltip In Place");
		$('#tooltip').html(tooltipob.updateTooltip(tooltipid,tooltipcontext));
	}
	catch(e)
	{
	}
}

function UpdateTooltip(element)
{
	$('#tooltip').css('left',element.clientX + 10);
	$('#tooltip').css('top',element.clientY + 10);
	tooltipcontext = element;
	try
	{
		$('#tooltip').html(tooltipob.updateTooltip(tooltipid,tooltipcontext));
	}
	catch(e)
	{
	}
}

function HideTooltip(element)
{
	//$('#tooltip').hide(0);
	$('#tooltip').css('display','none');
	try
	{
		$('#tooltip').html(tooltipob.closeTooltip(tooltipid,tooltipcontext));
	}
	catch(e)
	{
	}
}

function NavMenu(element)
{
	$('#moremenu').css('left',element.clientX + window.scrollX);
	$('#moremenu').css('top',element.clientY + window.scrollY);
	$('#moremenu').html('<menu class="menu show-menu"><li class="menu-item"><button type="button" class="menu-btn"> <i class="fa fa-folder-open"></i> <span class="menu-text">[ LOADING ]</span> </button></li></menu>');
	$('#moremenu').stop(true,true).fadeIn(250);
	var ass = element.currentTarget.getAttribute('data-assetid');
	var prop = element.currentTarget.getAttribute('data-propertyid');
	var ts = element.currentTarget.getAttribute('data-range');
	var base = "";
	try
	{
		base = element.currentTarget.getAttribute('data-base');
	}
	catch(e)
	{
	}
	
	if (ass == -1)
	{
		var ur = window.location.search.substr(1);
		$.post('getnavmenu',{panel: element.currentTarget.getAttribute('data-panel'),url: ur, range: ts, base: base},function(data) {
			$('#moremenu').html(data);
		});

	}
	else
	{
		$.post('getnavmenu',{panel: element.currentTarget.getAttribute('data-panel'),asset: ass,property: prop, range: ts},function(data) {
			$('#moremenu').html(data);
		});
	}
	
}

function ContextMenu(element)
{
	$('#moremenu').css('left',element.clientX + window.scrollX);
	$('#moremenu').css('top',element.clientY + window.scrollY);
	$('#moremenu').html('<menu class="menu show-menu"><li class="menu-item"><button type="button" class="menu-btn"> <i class="fa fa-folder-open"></i> <span class="menu-text">[ LOADING ]</span> </button></li></menu>');
	$('#moremenu').stop(true,true).fadeIn(250);
	var ass = element.currentTarget.getAttribute('data-assetid');
	var prop = element.currentTarget.getAttribute('data-propertyid');
	var ts = element.currentTarget.getAttribute('data-range');
	if (ass == -1)
	{
		var ur = window.location.search.substr(1);
		$.post('getcontextmenu',{panel: element.currentTarget.getAttribute('data-panel'),url: ur, range: ts},function(data) {
			$('#moremenu').html(data);
		});

	}
	else
	{
		$.post('getcontextmenu',{panel: element.currentTarget.getAttribute('data-panel'),asset: ass,property: prop, range: ts},function(data) {
			$('#moremenu').html(data);
		});
	}
	
}

function RenamePanel(id)
{
	id = id + 1;
	var value = $('#panel_' + id + ' .title').html();
	//alert(value);
	$('#panel_' + id + ' .title').html('<input type="text" value="' + value + '" id="panelrename" />');
	$('#panelrename').focus();
	$('#panelrename').on('blur',function (e){
		var value = $('#panelrename').val();
		$('#panel_' + id + ' .title').html(value);
		panelsettings[id-1].title = value;
		panelsettings[id-1].name = value;
		StoreGridChanges();
	});	
}

function CombinePanel(fromid,toid)
{
	$('#actionformact').val("joinpanel");
	$('#actionformasset').val(fromid);	
	$('#actionformpanel').val(toid);
	$('#actionform').submit();
}

function CombineChannel(asset,property,fromid,toid)
{
	$('#actionformact').val("joinchannel");
	$('#actionformasset').val(asset);	
	$('#actionformproperty').val(property);
	$('#actionformpanel').val(fromid);
	$('#actionformmisc').val(toid);
	$('#actionform').submit();
}

function SplitPanel(fromid)
{
	$('#actionformact').val("splitpanel");	
	$('#actionformpanel').val(fromid);
	$('#actionform').submit();
}

function SplitChannel(asset,property,fromid)
{
	$('#actionformact').val("splitchannel");
	$('#actionformasset').val(asset);	
	$('#actionformproperty').val(property);
	$('#actionformpanel').val(fromid);	
	$('#actionform').submit();
}

function NavigatingOut()
{
	$('.menuholder').fadeOut(500);
	$('.panel').fadeOut(500);
}

function GoTo(content)
{
	NavigatingOut();
	window.location="dash?" + content;	
}

function GoToReport(content)
{	
	for(var x=0;x<panelsettings.length;x++)
	{
		var panelcontent = "";
		for(var y=0;y<panelsettings[x].data.length;y++)
		{
			if (panelcontent != "") panelcontent += ",";
			panelcontent += panelsettings[x].data[y].asset + ":" + panelsettings[x].data[y].property;
		}
		
		content = content.replace("[PANEL" + x + "]",panelcontent);
	}
	NavigatingOut();
	window.location="report?range=" + rangestart + " to " + rangeend + "&" + content;	
}

function ReplaceVariable(varname,value)
{
	var finalval = "?";
	var bits = window.location.href;
	var pieces = bits.split('?');
	if (pieces.length > 1)
	{
		query = pieces[1];
		var bits = pieces[1].split('&');
		for(var vx=0;vx<bits.length;vx++)
		{
			var parts = bits[vx].split('=');
			if (parts[0] == varname)
			{
				parts[1] = value;
			}
			if (finalval.length > 1)
			{
				finalval += "&";
			}
			finalval += parts[0] + '=' + parts[1];
		}
	}
	return pieces[0] + finalval;
}

function OpenNav(id)
{
	$('.navigate').hide(0);
	$('#navpopup_' + id).toggle(500);	
}


function ChangeScaling(panel,scalemode)
{
	for(var x=0;x<panelsettings[panel].data.length;x++)
	{
		panelsettings[panel].scaling=scalemode;
		panelsettings[panel].initialised = false;
		RenderContent(panelsettings[panel]);		
	}
	$('#moremenu').fadeOut(250);
}

function ChangeStyle(panel,stylename)
{
	panelsettings[panel].style=stylename;
	panelsettings[panel].initialised = false;
	RenderContent(panelsettings[panel]);
	if ($('#timerange').val() == "")
	{
		if (stylename == "line")
		{
			$('#timerange').val(1800);
			ApplyRange();
		}
	}
	$('#moremenu').fadeOut(250);
	StoreGridChanges();
}

function ZoomIn()
{
	var sd = moment(rangestart,'YYYY-MM-DD HH:mm:ss');
	var ed = moment(rangeend,'YYYY-MM-DD HH:mm:ss');
	
	var diff = (ed - sd)/4;
	sd += diff;
	ed -= diff;
	
	
	rangestart = moment(sd).format('YYYY-MM-DD HH:mm:ss');
	rangeend = moment(ed).format('YYYY-MM-DD HH:mm:ss');
	
	//$('#customtime').data('daterangepicker').setStartDate(moment(sd).format('hh:mm A DD/MM/YYYY'));
	//$('#customtime').data('daterangepicker').setEndDate(moment(ed).format('hh:mm A DD/MM/YYYY'));
	ApplyRange(rangestart + " to " + rangeend);
}

function ZoomOut()
{
	var sd = moment(rangestart,'YYYY-MM-DD HH:mm:ss');
	var ed = moment(rangeend,'YYYY-MM-DD HH:mm:ss');
	
	var diff = (ed - sd)/2;
	sd -= diff;
	ed += diff;
	
	
	rangestart = moment(sd).format('YYYY-MM-DD HH:mm:ss');
	rangeend = moment(ed).format('YYYY-MM-DD HH:mm:ss');
	
	/*$('#customtime').data('daterangepicker').setStartDate(moment(sd).format('hh:mm A DD/MM/YYYY'));
	$('#customtime').data('daterangepicker').setEndDate(moment(ed).format('hh:mm A DD/MM/YYYY'));
	$('#timerange').val('*');
	$('#timecontrols').fadeIn(500);
	$('#customtime').fadeIn(500);*/
	ApplyRange(rangestart + " to " + rangeend);
}

function PrevTime()
{
	var sd = moment(rangestart,'YYYY-MM-DD HH:mm:ss');
	var ed = moment(rangeend,'YYYY-MM-DD HH:mm:ss');
	
	var diff = (ed - sd);
	sd -= diff;
	ed -= diff;
	
	rangestart = moment(sd).format('YYYY-MM-DD HH:mm:ss');
	rangeend = moment(ed).format('YYYY-MM-DD HH:mm:ss');
	
	/*$('#customtime').data('daterangepicker').setStartDate(moment(sd).format('hh:mm A DD/MM/YYYY'));
	$('#customtime').data('daterangepicker').setEndDate(moment(ed).format('hh:mm A DD/MM/YYYY'));
	$('#timerange').val('*');
	$('#timecontrols').fadeIn(500);
	$('#customtime').fadeIn(500);*/
	ApplyRange(rangestart + " to " + rangeend);
}

function NextTime()
{
	var sd = moment(rangestart,'YYYY-MM-DD HH:mm:ss');
	var ed = moment(rangeend,'YYYY-MM-DD HH:mm:ss');
	
	var diff = (ed - sd);
	sd += diff;
	ed += diff;
	
	rangestart = moment(sd).format('YYYY-MM-DD HH:mm:ss');
	rangeend = moment(ed).format('YYYY-MM-DD HH:mm:ss');
	
	/*$('#timerange').data('timeselector').setStartDate(moment(sd).format('hh:mm A DD/MM/YYYY'));
	$('#timerange').data('timeselector').setEndDate(moment(ed).format('hh:mm A DD/MM/YYYY'));
	$('#timerange').val('*');
	$('#timecontrols').fadeIn(500);
	$('#customtime').fadeIn(500);*/
	ApplyRange(rangestart + " to " + rangeend);
}

function ZoomInOnRange(sd,ed)
{
	rangestart = moment(sd).format("YYYY-MM-DD HH:mm:ss");
	rangeend = moment(ed).format("YYYY-MM-DD HH:mm:ss");
	$('#timerange').val('*');
	$('#timecontrols').fadeIn(500);
	
	document.getElementById('timerange').selectedIndex = 1;
	$('#customtime').fadeIn(500);
	$('#timecontrols').fadeIn(500);	
	$('#customtime').fadeIn(500);
	ApplyRange(rangestart + " to " + rangeend);
}

function ResetStructure(struct)
{
	NavigatingOut();
	$('#actionformact').val("structure");
	$('#actionformasset').val(structure);
	//alert('Resetting Structure');	
	$('#actionform').submit();
}