#region create
	global.node_plot_linear_keys = [ "plot", "bar chart", "graph", "waveform" ];
	
	function Node_create_Plot_Linear(_x, _y, _group = noone, _param = {}) {
		var node  = new Node_Plot_Linear(_x, _y, _group).skipDefault();
		var query = struct_try_get(_param, "query", "");
		
		switch(query) {
			case "waveform" : 
			case "graph" : 
				node.inputs[11].setValue(1); 
				break;
		}
		
		return node;
	}
#endregion

function Node_Plot_Linear(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
	name = "Draw Bar / Graph";
	
	newInput(0, nodeValue_Dimension(self));
	
	newInput(1, nodeValue_Float("Data", self, []))
		.setArrayDepth(1)
		.setVisible(true, true);
		
	newInput(2, nodeValue_Slider_Range("Range", self, [ 0, 1 ]));
		
	newInput(3, nodeValue_Float("Sample frequency", self, 1));
	
	newInput(4, nodeValue_Vec2("Origin", self, [ 0, DEF_SURF_H / 2 ]));
		
	newInput(5, nodeValue_Float("Scale", self, DEF_SURF_W / 2));
	
	newInput(6, nodeValue_Color("Base Color", self, c_white));
		
	newInput(7, nodeValue_Float("Graph Thickness", self, 1));
	
	newInput(8, nodeValue_Bool("Background", self, false));
		
	newInput(9, nodeValue_Color("Background color", self, c_black));
	
	newInput(10, nodeValue_Rotation("Direction", self, 0));
	
	newInput(11, nodeValue_Enum_Scroll("Type", self,  0, [ new scrollItem("Bar chart", s_node_plot_linear_type, 0), 
												           new scrollItem("Graph",	   s_node_plot_linear_type, 1), ]));
	
	newInput(12, nodeValue_Float("Value Offset", self, 0));
	
	newInput(13, nodeValue_Gradient("Color Over Sample", self, new gradientObject(cola(c_white))))
		.setMappable(27);
	
	newInput(14, nodeValue_Enum_Scroll("Trim mode", self,  0, [ "Range", "Window" ]));
	
	newInput(15, nodeValue_Int("Window Size", self, 8));
	
	newInput(16, nodeValue_Float("Window Offset", self, 0));
	
	newInput(17, nodeValue_Float("Spacing", self, 1));
	
	newInput(18, nodeValue_Float("Bar Width", self, 4));
	
	newInput(19, nodeValue_Bool("Rounded Bar", self, false));
	
	newInput(20, nodeValue_PathNode("Path", self, noone))
		.setVisible(true, true);
	
	newInput(21, nodeValue_Bool("Flip Value", self, false));
	
	newInput(22, nodeValue_Bool("Loop", self, false));
	
	newInput(23, nodeValue_Float("Smooth", self, 0))
		.setDisplay(VALUE_DISPLAY.slider);
		
	newInput(24, nodeValue_Gradient("Color Over Value", self, new gradientObject(cola(c_white))))
		.setMappable(29);
		
	newInput(25, nodeValue_Range("Value range", self, [ 0, 1 ] ));
	
	newInput(26, nodeValue_Bool("Absolute", self, false));
	
	//////////////////////////////////////////////////////////////////////////////////////////////////
	
	newInput(27, nodeValueMap("Sample Gradient map", self));
	
	newInput(28, nodeValueGradientRange("Sample Gradient map range", self, inputs[13]));
	
	newInput(29, nodeValueMap("Value Gradient map", self));
	
	newInput(30, nodeValueGradientRange("Value Gradient map range", self, inputs[24]));
	
	//////////////////////////////////////////////////////////////////////////////////////////////////
	
	newOutput(0, nodeValue_Output("Surface out", self, VALUE_TYPE.surface, noone));
	
	input_display_list = [ 0, 
		["Data", 	 true], 1, 12, 21, 14, 2, 3, 15, 16, 
		["Plot",	false], 11, 4, 10, 20, 5, 22, 23, 
		["Render",	false], 6, 13, 27, 24, 29, 25, 26, 7, 17, 18, 19, 
		["Background",	true, 8], 9, 
	];
	
	attribute_surface_depth();
	
	static step = function() {
		var _ubg = getSingleValue(8);
		var _typ = getSingleValue(11);
		var _trim_mode = getSingleValue(14);
		
		var _use_path = getSingleValue(20) != noone;
		
		inputs[ 2].setVisible(_trim_mode == 0);
		inputs[15].setVisible(_trim_mode == 1);
		inputs[16].setVisible(_trim_mode == 1);
		
		inputs[ 9].setVisible(_ubg);
		inputs[ 7].setVisible(_typ == 1);
		inputs[18].setVisible(_typ == 0);
		inputs[19].setVisible(_typ == 0);
		inputs[22].setVisible(_typ == 1);
		inputs[23].setVisible(_typ == 1);
		
		inputs[ 4].setVisible(!_use_path);
		inputs[10].setVisible(!_use_path);
		
		inputs[13].mappableStep();
		inputs[24].mappableStep();
	}
	
	static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) {
		PROCESSOR_OVERLAY_CHECK
		
		var _use_path = current_data[20] != noone;
		
		if(!_use_path) {
			var a = inputs[4].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny);
			active &= !a;
		}
		
		var a = inputs[28].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny, current_data[0]); active &= !a;
		var a = inputs[30].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny, current_data[0]); active &= !a;
	}
	
	static processData = function(_outSurf, _data, _output_index, _array_index) {
		var _dim = _data[ 0];
		var _dat = _data[ 1];
		var _ran = _data[ 2];
		var _sam = _data[ 3]; _sam = max(1, _sam);
		var _ori = _data[ 4];
		var _amp = _data[ 5];
		var _lth = _data[ 7];
		var _ubg = _data[ 8];
		var _bgc = _data[ 9];
		var _ang = _data[10];
		var _typ = _data[11];
		var _off = _data[12];
		
		var _trim_mode = _data[14];
		var _win_size  = _data[15];
		var _win_offs  = _data[16]; _win_offs = max(0, _win_offs);
		
		var _pnt_spac = _data[17];
		
		var _bar_wid = _data[18];
		var _bar_rnd = _data[19];
		
		var _path = _data[20];
		var _flip = _data[21];
		var _loop = _data[22];
		var _smt  = _data[23];
		
		var _lcl     = _data[ 6];
		var _cls     = _data[13];
		var _cls_map = _data[27];
		var _cls_rng = _data[28];
		var _clv     = _data[24];
		var _clv_map = _data[29];
		var _clv_rng = _data[30];
		var _clv_r   = _data[25];
		var _clv_a   = _data[26];
		
		_outSurf = surface_verify(_outSurf, _dim[0], _dim[1], attrDepth());
		
		surface_set_target(_outSurf);
			if(_ubg) draw_clear(_bgc);
			else	 DRAW_CLEAR
			
			var _len = array_length(_dat);
			var _st  = clamp(_ran[0], 0, 1) * _len;
			var _ed  = clamp(_ran[1], 0, 1) * _len;
			var ox, oy, nx, ny, fx, fy;
			
			if(_typ == 1)
				draw_set_circle_precision(4);
			
			var _dat_amo = array_length(_dat);
			var _smp_data = [];
			var _ind = 0;
			
			if(_trim_mode == 0) {
				for( var i = _st; i < _ed; i += _sam )
					_smp_data[_ind++] = _dat[i];
			} else if(_trim_mode == 1) {
				for( var i = 0; i < _win_size; i++ ) {
					_ind = _win_offs + i * _sam;
					
					if(_ind >= _dat_amo) break;
					if(frac(_ind) != 0 && floor(_ind) + 1 < _dat_amo)
						_smp_data[i] = lerp(_dat[floor(_ind)], _dat[floor(_ind) + 1], frac(_ind));
					else
						_smp_data[i] = _dat[_ind];
					
					if(_flip)
						_smp_data[i] = -_smp_data[i];
				}
			}
			
			var amo = array_length(_smp_data);
			var _px, _py, _ang_nor, _val, _col_sam, _col_val;
			var _pnt, _ppnt = undefined;
			var _bar_spc = _typ == 1? _pnt_spac + 1 : _pnt_spac + _bar_wid;
			var _oc;
			
			for( var i = 0; i < amo; i++ ) {
				if(_path == noone) {
					_px = _ori[0] + lengthdir_x(i * _bar_spc, _ang);
					_py = _ori[1] + lengthdir_y(i * _bar_spc, _ang);
				} else {
					_pnt    = _path.getPointRatio(i / amo);
					_ppnt ??= _path.getPointRatio(i / amo - 0.001);
					
					_px = _pnt.x;
					_py = _pnt.y;
					_ang = point_direction(_ppnt.x, _ppnt.y, _pnt.x, _pnt.y)
					
					_ppnt = _pnt;
				}
				
				_ang_nor = _ang + 90;
				_val	 = _smp_data[i] + _off;
				_col_sam = evaluate_gradient_map(i / amo, _cls, _cls_map, _cls_rng, inputs[13]);
				
				var _val_p = _clv_a? abs(_val) : _val;
				var _val_prog = (_val_p - _clv_r[0]) / (_clv_r[1] - _clv_r[0]);
				_col_val = evaluate_gradient_map(_val_prog, _clv, _clv_map, _clv_rng, inputs[24]);
				
				var _c1 = colorMultiply(_lcl, _col_sam);
				var _c2 = _col_val;
				var _col_final = colorMultiply(_c1, _c2);
				
				draw_set_color(_col_final);
				
				nx = _px + lengthdir_x(_amp * _val, _ang_nor);
				ny = _py + lengthdir_y(_amp * _val, _ang_nor);
				
				switch(_typ) {
					case 0 :
						if(_bar_rnd) draw_line_round(_px, _py, nx, ny, _bar_wid);
						else		 draw_line_width(_px, _py, nx, ny, _bar_wid);
						break;
					case 1 :
						if(i > _st) {
							if(_smt > 0) {
								var dist = dot_product(nx - ox, ny - oy, lengthdir_x(1, _ang), lengthdir_y(1, _ang));
								var _b0x = ox + lengthdir_x(dist * _smt, _ang);
								var _b0y = oy + lengthdir_y(dist * _smt, _ang);
								var _b1x = nx + lengthdir_x(dist * _smt, _ang + 180);
								var _b1y = ny + lengthdir_y(dist * _smt, _ang + 180);
								
								var _ox = ox, _oy = oy, _nx, _ny;
								
								for( var j = 1; j <= 8; j++ ) {
									var _t  = 1 - j / 8;
									_nx = ox * power(_t, 3) + 3 * _b0x * power(_t, 2) * (1 - _t) + 3 * _b1x * (_t) * power(1 - _t, 2) + nx * power(1 - _t, 3);
									_ny = oy * power(_t, 3) + 3 * _b0y * power(_t, 2) * (1 - _t) + 3 * _b1y * (_t) * power(1 - _t, 2) + ny * power(1 - _t, 3);
									
									if(_lth > 1) draw_line_round(_ox, _oy, _nx, _ny, _lth);
									else		 draw_line(_ox, _oy, _nx, _ny);
									
									_ox = _nx;
									_oy = _ny;
								}
							} else {
								if(_lth > 1) draw_line_round_color(ox, oy, nx, ny, _lth, _oc, _col_final);
								else		 draw_line_color(ox, oy, nx, ny, _oc, _col_final);
							}
						}
						break;
				}
				
				ox = nx;
				oy = ny;
				_oc = _col_final;
				
				if(i == 0) {
					fx = nx;
					fy = ny;
				}
			}
			
			if(_loop && amo > 1 && _typ == 1)
				draw_line_round(fx, fy, nx, ny, _lth);
			
			draw_set_circle_precision(64);
		surface_reset_target();
		return _outSurf;
	}
}