

function vis_line_Create(ob)
{	
	var margin = {top: 20, right: 20, bottom: 30, left: 30};
		
	
	var svg = d3.select("#plotarea_" + (ob.number-1))
	  .append("svg")		
		.attr("viewBox", "0 0 " + ob.width + " " + ob.height)
		.attr("preserveAspectRatio","xMidYMid meet")
		.attr("width","100%")
		.attr("height","100%")
		
	ob.basesvg = svg;
	svg = svg.append("g")
		.attr("transform",
			  "translate(" + margin.left + "," + margin.top + ")");
			  
	ob.svg = svg;
			  
	var width = ob.width - margin.left - margin.right;
    var height = ob.height - margin.top - margin.bottom;
	
	var data = ob.data;

	// gridlines in x axis function
	function make_x_gridlines() {		
		return d3.axisBottom(x)
			.ticks(5)
	}

	// gridlines in y axis function
	function make_y_gridlines() {		
		return d3.axisLeft(y)
			.ticks(5)
	}
	
	//var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
	//var msTimeParser = d3.timeParse("%Y-%m-%d %H:%M:%S.%f");
	
	var xextents = d3.extent(data[0].hist,function(d) { 
		return parseDateTime(d[0]); 
	});
	
	var dragstart = null;
	var dragend = null;
	var dragged = false;
	
	svg.append('rect')
	     .attr("width",width)
		 .attr("height",height)
		 .attr("fill","black")
		 .attr("stroke","none")
		 .attr("data-panel", ob.number-1)
		 .attr("data-id", -1)
		 .attr("data-base",ob.data[0].asset)
		 .attr("opacity",0)
		 .on("mouseover",function(e) { ShowTooltip(e); })
		 .on("mousemove",function(e) { 
			UpdateTooltip(e); 
			if (dragstart != null)
			{
				const dsvg = ob.basesvg.node();
				const pt = dsvg.createSVGPoint();

				pt.x = e.clientX;
				pt.y = e.clientY;
				const svgP = pt.matrixTransform( dsvg.getScreenCTM().inverse() );
				svgP.x -= 30;		
				
				dragged = true; 
				if (dragend < dragstart)
				{
					dragend = x.invert(svgP.x);	
					selection.attr('x',x(dragend));						
				}
				else
				{
					dragend = x.invert(svgP.x);				
				}										
				selection.attr("width",Math.abs(x(dragend) - x(dragstart)));
				ob.svg.selectAll(".trendline")
					.attr("pointer-events","none");
			}
		 })
		 .on("mouseout",function(e) { 						
			if (e.relatedTarget.nodeName != "line")
			{
				if (dragstart != null) dragstart = null;
				if (dragged == true)
				{
					console.log("Drag Stopped - " + e.relatedTarget);
					ob.svg.selectAll(".trendline")
						.attr("pointer-events","auto");
				}
				dragged = false;
				selection.attr("width",0);			
				HideTooltip(e); 
			}
		 })
		 .on("mousedown",function(e) { 
			if (e.which == 1)
			{
				const dsvg = ob.basesvg.node();
				const pt = dsvg.createSVGPoint();

				pt.x = e.clientX;
				pt.y = e.clientY;
				const svgP = pt.matrixTransform( dsvg.getScreenCTM().inverse() );
				svgP.x -= 30;				
				
				dragstart = x.invert(svgP.x); 
				console.log("Starting Drag @ " + dragstart); 
				selection.attr('x',x(dragstart));							
			}
		 })
		 .on("mouseup",function(e) { 
			dragend = x.invert(e.clientX - (margin.left+20));
			console.log("Finishing Drag @ " + dragend);
			
			sd = dragstart.getTime();
			ed = dragend.getTime();
			
			if (Math.abs(ed - sd) < 30000)
			{
				dragged = false;
				return;
			}
			
			if (sd > ed)
			{
				var vx = sd;
				sd = ed;
				ed = vx;				
			}
			
			ob.svg.selectAll(".trendline")
					.attr("pointer-events","auto");
			
			ZoomInOnRange(sd,ed);
			
			dragstart = null;
			dragend = null;
			selection.attr("width",0);
		 })
		 .attr("data-id", -1)	
		 .attr("data-assetid","-1")
		 .attr("data-range", ob.data[0].rangemin + " to " + ob.data[0].rangemax)
		 .attr("draggable","true")
		 .on("dragstart",function(e) { console.log("Dragging In Control"); })
		 .on("drag",function(e) { console.log("Dragging In Control"); })
		 .on("dragstop",function(e) { console.log("Finished Drag"); })
		 .on("click",function(e) { if (dragged == true) { dragged = false; return ;} NavMenu(e); })
		 .on("contextmenu",function(e) { e.preventDefault(); e.stopPropagation(); ContextMenu(e);});
	 
	var gradients = '<clipPath id="inframe' + ob.number + '">';
    gradients += '  <rect x="0" y="1" width="' + width + '" height="' + (height-2) + '" />';
    gradients += '</clipPath>';
	var defs = svg.append("defs").html(gradients);		 
		 		 
	var selection = svg.append('rect')
						.attr('width',0)
						.attr('height',height)
						.attr('x',0)
						.attr('y',0)
						.attr('fill','white')
						.attr('opacity',0.2)
						.attr('id','selectbox')
						.attr('pointer-events','none');

	var x = d3.scaleTime()
		.domain([parseDateTime(data[0].rangemin),parseDateTime(data[0].rangemax)])
		.range([0,width]);
		
		
	ob.x = x;
	  
	var bottomclass = "axis";	
	
	svg.append("g")
	  .attr("transform", "translate(0," + height + ")")
	  .call(d3.axisBottom(x))
	  .selectAll("text")
		.attr("transform", "translate(-10,0)rotate(0)")
		.style("text-anchor", "middle")
		.attr("class",bottomclass);
			
	var ydomain = [d3.min(data,function(d) {return d.minimum; }), d3.max(data,function(d) { return d.maximum; })];
	var yspan = ydomain[1] - ydomain[0];
	ydomain[0] -= yspan * 0.02;
	ydomain[1] += yspan * 0.02;
	//if (ydomain[0] != 0) ydomain[0] *= 1.05;
	//if (ydomain[1] != 0) ydomain[1] *= 1.05;
	if (ob.scaling)
	{
		if (ob.scaling == "auto")
		{
			var activemin = null;
			var activemax = null;
			var thismin = 0;
			var thismax = 0;
			for(var v=0;v<data.length;v++)
			{
				thismin = d3.min(data[v].hist, function (r) {
					return parseFloat(r[1]);
				});
				
				thismax = d3.max(data[v].hist, function (r) {
					return parseFloat(r[1]);
				});
				
				if ((activemin == null) || (activemin > thismin))
				{
					activemin = thismin;
				}
				
				if ((activemax == null) || (activemax < thismax))
				{
					activemax = thismax;
				}
			}
			ydomain = [activemin,activemax];//[d3.min(data,function(d) {return d3.min(d.hist,function(r) { return r[1]; });}), d3.max(data,function(d) {return d3.max(d.hist,function(r) { return r[1]; });})];
			var ddif = ydomain[1] - ydomain[0];
			if (ddif == 0)
			{
				ddif = 2;
			}
			ydomain[0] = parseFloat(ydomain[0]) - (ddif / 5);
			ydomain[1] = parseFloat(ydomain[1]) + (ddif / 5);
		};
	}
			
	// Add Y axis
	var y = d3.scaleLinear()	  
	  .domain(ydomain)
	  .range([ height, 0])
	  .clamp(true);
	  
	ob.y = y;
	  
	svg.append("g")
	  .attr("class","yaxis")
	  .call(d3.axisLeft(y))
	  
	  
	// add the X gridlines
	svg.append("g")			
	  .attr("class", "grid xaxis")
	  .attr("transform", "translate(0," + height + ")")
	  .call(make_x_gridlines()
		  .tickSize(-height)
		  .tickFormat("")
	  )

	// add the Y gridlines
	svg.append("g")			
	  .attr("class", "grid yaxis")
	  .call(make_y_gridlines()
		  .tickSize(-width)
		  .tickFormat("")
	  )	
	  
	ob.hover = svg.append("line")
		.attr("x1","0")
		.attr("y1","0")
		.attr("x2","0")
		.attr("y2",height)
		.attr("stroke","white")
		.attr("display","none")
		.attr("stroke-dasharray","4 1")
		.attr("opacity",0.5)
		.attr("pointer-events","none");
	
			  
	ob.update = function(dt) {
		
		//console.log("Updating");
		
		var margin = {top: 20, right: 20, bottom: 90, left: 30};
				
		if (ob.data.length == 1)
		{
			margin.bottom = 60;
		}
		
		var width = ob.width - margin.left - margin.right;
		var height = ob.height - margin.top - margin.bottom;
		
		if (ob.scaling)
		{
			if (ob.scaling == "auto")
			{				
				var ydomain = [d3.min(ob.data,function(d) {return d3.min(d.hist,function(r) { return parseFloat(r[1]); });}), d3.max(ob.data,function(d) {return d3.max(d.hist,function(r) { return parseFloat(r[1]); });})];
				var ddif = ydomain[1] - ydomain[0];
				if (ddif == 0)
				{
					ddif = 2;
				}
				ydomain[0] = parseFloat(ydomain[0]) - (ddif / 5);
				ydomain[1] = parseFloat(ydomain[1]) + (ddif / 5);	

				if (ydomain[1] < ydomain[0])
				{
					var vx = ydomain[0];
					ydomain[0] = ydomain[1];
					ydomain[1] = vx;
				}
						
				// Add Y axis
				var y = d3.scaleLinear()	  
				  .domain(ydomain)
				  .range([ height, 0])
				  .clamp(true);
				  
				ob.y = y;
				
				ob.svg.selectAll(".yaxis").remove();
				
				svg.append("g")
					.attr("class","yaxis")
					.call(d3.axisLeft(y));
					
				// add the Y gridlines
				svg.append("g")			
				  .attr("class", "grid yasix")
				  .call(make_y_gridlines()
					  .tickSize(-width)
					  .tickFormat("")
				  )	
			}
			
		}
		
		ob.svg.selectAll(".trendline").remove();
		
		for(var q=0;q<ob.data.length;q++)
		{
			var tzoffset = ob.data[q].tzoffset * 60000;
			var data = ob.data[q].hist.map(function(d) {
				var dt = new Date(parseDateTime(d[0]).getTime() - tzoffset);
				return {
				  date: dt,
				  value: d[1]
				};
			});
			ob.data[q].optimised = data;
						
			line = d3.line()
					.defined(function(d) {
						return (d.value != null) && (d.value != "^");
					})
					.x(function(d) {
						return ob.x(d.date).toFixed(0);
					})
					.y(function(d) {
						return ob.y(d.value).toFixed(0);
					})				

			var ln = [0];
			
			bars = ob.svg.selectAll(".line" + q)
				.data(ln)
				.join(
					  enter => 
						enter.append("path") 		  
							.attr("class","line" + q + " trendline")
							.attr("fill","none")
							.attr("stroke",GetSequenceColour(q))
							.attr("stroke-width","2")
							.attr("d", line(data))
							.attr("data-panel", ob.number-1)
							.attr("data-id", q)
							.attr("clip-path","url(#inframe" + ob.number + ")")
							.attr("data-assetid", ob.data[q].asset)
							.attr("data-propertyid", ob.data[q].property)
							.attr("data-range", ob.data[q].rangemin + " to " + ob.data[q].rangemax)
							.attr("shape-rendering","geometricPrecision")
							.on("mouseover",function(e) { ShowTooltip(e); })
							.on("mousemove",function(e) { UpdateTooltip(e); })
							.on("mouseout",function(e) { HideTooltip(e); })	
							.on("contextmenu",function(e) { e.preventDefault(); e.stopPropagation(); ContextMenu(e);})
							.on("click",function(e) { NavMenu(e);}),
					  update =>
						update
							.transition()
							.duration(800)
							.attr("d", line(data))
				)			
		};
		
		if (window.markers.length > 0)
		{
			var marks = ob.svg.selectAll(".marker" + q)
				.data(window.markers)
				.join(
					  enter => 
						enter.append("line") 		  
							.attr("class","marker")
							.attr("fill","none")
							.attr("stroke","silver")
							.attr("stroke-width","2")
							.attr("x1",function(d) {
								return x(d);
							})
							.attr("x2",function(d) {
								return x(d);
							})
							.attr("y1",0)
							.attr("y2",ob.height)
							.attr("stroke-dasharray","4")
							.attr("shape-rendering","geometricPrecision"),
					  update =>
						update
							.transition()
							.duration(800)
							.attr("d", line(data))
				)	
		}
	}
	ob.update(ob.data);	
	
	ob.closeTooltip = function(pntno,ctx) {
		ob.hover.attr("display","none");
	};
	
	ob.updateTooltip = function(pntno,ctx) {			
		var bi = d3.bisector(d => d.date);		
		
		const dsvg = ob.basesvg.node();
		const pt = dsvg.createSVGPoint();

		pt.x = ctx.clientX;
		pt.y = ctx.clientY;
		
		const svgP = pt.matrixTransform( dsvg.getScreenCTM().inverse() );
		svgP.x -= 30;
		var lookup = ob.x.invert(svgP.x);
				
		ob.hover.attr("display","block")
			.attr("x1",svgP.x)
			.attr("x2",svgP.x);
			
		var clrid;
					
		if ((pntno == "-1") || (pntno == -1))
		{
			var val = "";
			var st = "";
			for(var q=0;q<ob.data.length;q++)
			{
				var tzoffset = ob.data[q].tzoffset * 60000;				
				data = ob.data[q].optimised;
				
				i = bi.right(data,lookup, 1);
				if (i < data.length)
				{
					d0 = data[i - 1];
					d1 = data[i];
					d = lookup - d0.date > d1.date - lookup ? d1 : d0;
				}
				else
				{
					if (data.length > 1)
						d = data[data.length-1];
					else
						val = 0;
				}
				clrid = q%11;
				if (d.value)
					val = d.value;
				if (val == undefined) 
					val = "Unknown";
				else
				{
					try
					{
						val = parseFloat(d.value).toFixed(2) + ob.data[q].units;
					}
					catch(e)
					{
						val = d.value + ob.data[q].units;
					};
				}
				
				st += "<div class=\"legendbox\" style=\"background-color: " + GetSequenceColour(clrid) + "\"></div><label>" + ob.data[q].name + "</label>: " + val + "<br/>";
			}
			var dateparts = lookup.toString().split('(')
			st += "<br/>At " + dateparts[0] + "<br/>" + dateparts[1].substr(0,dateparts[1].length-1);
			
			//console.log("Complete!");
			return st;
		}
		else
		{
			var tzoffset = ob.data[pntno].tzoffset * 60000;				

			data = ob.data[pntno].optimised;			
			
			i = bi.right(data,lookup, 1);
			if (i < data.length)
			{
				d0 = data[i - 1];
				d1 = data[i];
				d = lookup - d0.date > d1.date - lookup ? d1 : d0;
			}
			else
			{
				d = data[data.length-1].value;
			}
			var dateparts = lookup.toString().split('(')			
			var st = "<br/>At " + dateparts[0] + "<br/>" + dateparts[1].substr(0,dateparts[1].length-1);
			try
			{
				return ob.data[pntno].name + "<br/><strong>" + (Math.round(d.value*100)/100).toFixed(2) + ob.data[pntno].units + "</strong>" + st;
			}
			catch(e)
			{
				return ob.data[pntno].name + "<br/><strong>" + (Math.round(d.value*100)/100) + ob.data[pntno].units + "</strong>" + st;
			}
		}
	}
	
}