var bvBrushLib =
{
	/*
	Simple default brush...
	*/
	defaultBrush: function(sizeWatcher)
	{
		var div = document.createElement("div"); // the gui
		div.name = "Smpl";
		var color;
		var context;
		var size = 8, spacing = 0.4, opacity = 0.5;
		var realsize = size;
		var lastDot, lastX, lastY;
		var lastPressure;
		var lastInput = {}; //looks the same as lastX and lastY
		sizeWatcher(size);
		div.getSize = function()
		{
			return size;
		};
		var started = false;
		
		function init()
		{
			var sizeSlider = new BV.pcSlider({label:"Size", w:250, h:30, start:1, end:100, startval:0.25, func:function(val)
			{
				lastInput = {};
				size = val;
				realsize = size;
				sizeWatcher(size);
			}, spline: [[0,1], [0.25, 8],[0.5,20],[1,100]]});
			var opacitySlider = new BV.pcSlider({label:"Opacity", w:250, h:30, start:0, end:100, startval:70, func:function(val)
			{
				lastInput = {};
				opacity = val/100;
			}});
			var spacingSlider = new BV.pcSlider({label:"Spacing", w:250, h:30, start:0.05, end:10, startval:spacing*15, func:function(val)
			{
				lastInput = {};
				spacing = val/15;
			}});
			opacitySlider.style.marginTop = "10px";
			spacingSlider.style.marginTop = "10px";
			div.appendChild(sizeSlider);
			div.appendChild(opacitySlider);
			div.appendChild(spacingSlider);
			
		};
		init();
		
		function drawDot(x, y)
		{
			if(realsize > 0)
			{
				context.fillStyle = "rgba("+color.r+", "+color.g+", "+color.b+", " + opacity + ")";
				context.beginPath();
				context.arc(x, y, realsize, 0, Math.PI*2, true);
				context.closePath();
				context.fill();
			}
		}
		
		//every brush needs the following methods
		//both setColor and setContext get called before the first startLine
		div.setColor = function(c)
		{
			lastInput = {};
			color = c;
		};
		div.setContext = function(c)
		{
			lastInput = {};
			context = c;
		}
		div.startLine = function(x, y, p, shift)
		{
			if(shift && lastInput.x) {
				var lx = lastInput.x, ly = lastInput.y;
				started = true;
				div.goLine(x,y,p, shift);
				div.endLine();
			} else {
				started = true;
				realsize = Math.max(0.1, p*size);
				drawDot(x, y);
				lastX = x;
				lastY = y;
				lastPressure = realsize;
				lastDot = realsize * spacing;
				lastInput.x = x;
				lastInput.y = y;
			}
		};
		div.goLine = function(x, y, p, shift)
		{
			if(!started)
				return;
			realsize = Math.max(0.1, p*size);
			var newsize = Math.max(0.1, p*size);
			if(!shift) {
				x = x*0.5 + lastX*0.5;
				y = y*0.5 + lastY*0.5;
			}
			var mouseDist = Math.sqrt(Math.pow(x - lastX, 2.0) + Math.pow(y - lastY, 2.0));
			var eX = (x - lastX) / mouseDist;
			var eY = (y - lastY)  / mouseDist;
			var loopDist;
			var bdist = realsize * spacing;
			for(loopDist = lastDot; loopDist <= mouseDist; loopDist += bdist)
			{
				var factor = (loopDist-lastDot) / (mouseDist-lastDot);
				realsize = newsize*factor + lastPressure*(1-factor);
				drawDot(lastX + eX * loopDist, lastY + eY * loopDist);
				
			}
			lastDot = loopDist - mouseDist;
			lastPressure = realsize;
			lastX = x;
			lastY = y;
			lastInput.x = x;
			lastInput.y = y;
		};
		div.endLine = function()
		{
			started = false;
		};
		return div;
	},
	
	smoothBrush: function(sizeWatcher)
	{
		var div = document.createElement("div"); // the gui
		div.name = "Smooth";
		var color;
		var context;
		var size = 29, spacing = 1/3, opacity = 0.5, blending = 0.65;
		sizeWatcher(size);
		div.getSize = function()
		{
			return size*0.7;
		};
		var realsize = size;
		var lastDot, lastX, lastY;
		var blendCol, blendMix = 0.45, mixr, mixg, mixb;
		var started = false;
		var lastInput = {};
		
		var averageCanvas = BV.createCanvas();
		averageCanvas.width = 10;
		averageCanvas.height = 10;
		function getAverage(x, y) {
			var canvas = div.requestCanvas();
			ctx = averageCanvas.getContext("2d");
			ctx.clearRect(0, 0, averageCanvas.width, averageCanvas.height);
			if(canvas != false) {
				averageCanvas = canvas.getRegion(x-5, y-5, averageCanvas);
			} else {
				ctx.drawImage(context.canvas, -x+5, -y+5);
			}
			
			var ar = 0, ag = 0, ab = 0, aa = 0, alpha;
			var imdat = ctx.getImageData(0, 0, 10, 10);
			for(var i = 0; i < imdat.data.length; i+=4)
			{
				alpha = imdat.data[i+3];
				ar += imdat.data[i+0] * alpha;
				ag += imdat.data[i+1] * alpha;
				ab += imdat.data[i+2] * alpha;
				aa += alpha;
			}
			if(aa != 0) {
				ar /= aa;
				ag /= aa;
				ab /= aa;
				aa = Math.min(1, aa);
			}
			
			return {
				r: ar,
				g: ag,
				b: ab,
				a: aa
			};
		}
		
		var pressure;

		function init()
		{
			var sizeSlider = new BV.pcSlider({label:"Size", w:250, h:30, start:1, end:200, startval:0.2, func:function(val)
			{
				lastInput = {};
				size = val;
				realsize = size;
				sizeWatcher(size*0.9);
			}, spline: [[0,1],[0.5,80],[1,200]]});
			var opacitySlider = new BV.pcSlider({label:"Opacity", w:250, h:30, start:0, end:100, startval:90, func:function(val)
			{
				lastInput = {};
				opacity = val/100;
			}});
			var spacingSlider = new BV.pcSlider({label:"Spacing", w:250, h:30, start:0.05, end:10, startval:5, func:function(val)
			{
				lastInput = {};
				spacing = val/15;
			}});
			var blendingSlider = new BV.pcSlider({label:"Blending", w:250, h:30, start:0, end:100, startval:blending*100, func:function(val)
			{
				lastInput = {};
				blending = val/100;
			}});
			opacitySlider.style.marginTop = "10px";
			spacingSlider.style.marginTop = "10px";
			blendingSlider.style.marginTop = "10px";
			div.appendChild(sizeSlider);
			div.appendChild(opacitySlider);
			div.appendChild(spacingSlider);
			div.appendChild(blendingSlider);
		};
		init();
		
		function drawDot(x, y)
		{
			context.save();
			var radgrad = context.createRadialGradient(realsize,realsize,0,realsize,realsize,realsize);
			radgrad.addColorStop(0, "rgba("+parseInt(mixr)+", "+parseInt(mixg)+", "+parseInt(mixb)+", " + opacity + ")");
			radgrad.addColorStop(1, "rgba("+parseInt(mixr)+", "+parseInt(mixg)+", "+parseInt(mixb)+", 0)");

			// draw shape
			context.fillStyle = radgrad;
			context.translate(x-realsize, y-realsize);
			context.fillRect(0,0,realsize*2,realsize*2);
			context.restore();
		}
		

		div.setColor = function(c)
		{
			lastInput = {};
			color = c;
		};
		div.setContext = function(c)
		{
			lastInput = {};
			context = c;
		}
		div.startLine = function(x, y, p, shift)
		{
			if(shift && lastInput.x) {
				var lx = lastInput.x, ly = lastInput.y;
				started = true;
				div.goLine(x,y,p);
				div.endLine();
			} else {
				started = true;
				realsize = Math.max(1, p*size);	
				var average = getAverage(x, y);
				
				blendCol = {
					r: average.r,
					g: average.g,
					b: average.b,
					a: average.a
				};
				
				mixr = color.r*(1-blendCol.a) + (blending*blendCol.r + color.r*(1-blending))*blendCol.a;
				mixg = color.g*(1-blendCol.a) + (blending*blendCol.g + color.g*(1-blending))*blendCol.a;
				mixb = color.b*(1-blendCol.a) + (blending*blendCol.b + color.b*(1-blending))*blendCol.a;
				mixr = parseInt(mixr);
				mixg = parseInt(mixg);
				mixb = parseInt(mixb);

				drawDot(x, y);
				lastX = x;
				lastY = y;
				lastInput.x = x;
				lastInput.y = y;
				lastDot = realsize * spacing;
			}
		};
		div.goLine = function(x, y, p)
		{
			if(!started)
				return;
			realsize = Math.max(1, p*size);

			var mouseDist = Math.sqrt(Math.pow(x - lastX, 2.0) + Math.pow(y - lastY, 2.0));
			var eX = (x - lastX) / mouseDist;
			var eY = (y - lastY)  / mouseDist;
			var loopDist;
			var bdist = realsize * spacing;
			
			var average = getAverage(x, y);
			
			blendCol.r = blendCol.r*(1-average.a) + (blendMix*average.r + blendCol.r*(1-blendMix))*average.a;
			blendCol.g = blendCol.g*(1-average.a) + (blendMix*average.g + blendCol.g*(1-blendMix))*average.a;
			blendCol.b = blendCol.b*(1-average.a) + (blendMix*average.b + blendCol.b*(1-blendMix))*average.a;
			blendCol.a = Math.min(1, blendCol.a + average.a);
			
			mixr = color.r*(1-blendCol.a) + (blending*blendCol.r + color.r*(1-blending))*blendCol.a;
			mixg = color.g*(1-blendCol.a) + (blending*blendCol.g + color.g*(1-blending))*blendCol.a;
			mixb = color.b*(1-blendCol.a) + (blending*blendCol.b + color.b*(1-blending))*blendCol.a;
			mixr = parseInt(mixr);
			mixg = parseInt(mixg);
			mixb = parseInt(mixb);
			
			
			for(loopDist = lastDot; loopDist <= mouseDist; loopDist += bdist)
			{
				drawDot(lastX + eX * loopDist, lastY + eY * loopDist);
			}
			lastDot = loopDist - mouseDist;
			lastX = x;
			lastY = y;
			lastInput.x = x;
			lastInput.y = y;
		};
		div.endLine = function() {
			started = false;
		};
		//outside can replace this and offer access to the pcCanvas
		div.requestCanvas = function() {
			return false;
		};
		return div;
	},

	//similar to harmony
	sketchy: function(sizeWatcher)
	{
		var div = document.createElement("div"); // the gui
		div.name = "Sketchy";
		var color;
		var context;
		var size = 1, opacity = 0.2;
		var lastX, lastY;
		var blending = 0.5;
		var scale = 1;
		var started = false;
		var lastInput = {};
		sizeWatcher(size);
		div.getSize = function()
		{
			return size/2;
		};

		var points = [];
		var count = 0;
		var mixmode = [
			function(c1, c2)
			{
				return c1;
			},
			function(c1, c2)
			{
				var result = new RGB(c1.r, c1.g, c1.b);
				result.r *= c2.r/255;
				result.g *= c2.g/255;
				result.b *= c2.b/255;
				return result;
			},
			function(c1, c2)
			{
				var result = new RGB(c1.r, c1.g, c1.b);
				result.r *= c2.r/255;
				result.g *= c2.g/255;
				result.b *= c2.b/255;
				return result;
			}
		];
		
		function init()
		{
			var opacitySlider = new BV.pcSlider({label:"Opacity", w:250, h:30, start:0, end:100, startval:opacity*100, func:function(val)
			{
				lastInput = {};
				opacity = val/100;
			}});
			var blendSlider = new BV.pcSlider({label:"Blending", w:250, h:30, start:0, end:100, startval:blending*100, func:function(val)
			{
				lastInput = {};
				blending = val/100;
			}});
			var sizeSlider = new BV.pcSlider({label:"Size", w:250, h:30, start:1, end:20, startval:size, func:function(val)
			{
				lastInput = {};
				size = val;
				sizeWatcher(size/2);
			}});
			var scaleSlider = new BV.pcSlider({label:"Scale", w:250, h:30, start:1, end:20, startval:scale, func:function(val)
			{
				lastInput = {};
				scale = val;
			}});
			var clearbutton = document.createElement("button");
			clearbutton.innerHTML = "Clear History";
			opacitySlider.style.marginTop = "10px";
			blendSlider.style.marginTop = "10px";
			scaleSlider.style.marginTop = "10px";
			clearbutton.style.marginTop = "10px";
			div.appendChild(sizeSlider);
			div.appendChild(opacitySlider);
			div.appendChild(blendSlider);
			div.appendChild(scaleSlider);
		};
		init();
		

		div.setColor = function(c)
		{
			lastInput = {};
			color = c;
		};
		div.setContext = function(c)
		{
			lastInput = {};
			context = c;
		}
		div.startLine = function(x, y, pressure, shift)
		{
			if(shift && lastInput.x) {
				var lx = lastInput.x, ly = lastInput.y;
				started = true;
				div.goLine(x,y,pressure);
				div.endLine();
			} else {
				started = true;
				lastX = x;
				lastY = y;
				lastInput.x = x;
				lastInput.y = y;
			}
		};
		div.goLine = function(x, y, pressure)
		{
			if(!started)
				return;
			var e,b,a,g;
			x = parseInt(x);
			y = parseInt(y);
			points.push([x,y]);
			
			context.save();
			context.lineWidth = size;
			
			var mixr = color.r, mixg = color.g, mixb = color.b;
			if(x+5 >= 0 && y+5 >= 0 && x-5 < context.canvas.width-1 && y-5 < context.canvas.height-1)
			{
				mixr = 0;
				mixg = 0;
				mixb = 0;
				var mixx = Math.min(context.canvas.width-1, Math.max(0, x-5));
				var mixy = Math.min(context.canvas.height-1, Math.max(0, y-5));
				var mixw = Math.min(context.canvas.width-1, Math.max(0, x+5));
				var mixh = Math.min(context.canvas.height-1, Math.max(0, y+5));
				mixw -= mixx;
				mixh -= mixy;
				if(mixw > 0 && mixh > 0) {
					var imdat = context.getImageData(mixx, mixy, mixw, mixh);
					var countmix = 0;
					for(var i = 0; i < imdat.data.length; i+=4)
					{
						mixr += imdat.data[i+0];
						mixg += imdat.data[i+1];
						mixb += imdat.data[i+2];
						countmix++;
					}
					mixr /= countmix;
					mixg /= countmix;
					mixb /= countmix;
				}
			}
			var mixed = mixmode[0](new RGB(mixr, mixg, mixb), color);
			mixr = parseInt(blending*mixed.r + color.r*(1-blending));
			mixg = parseInt(blending*mixed.g + color.g*(1-blending));
			mixb = parseInt(blending*mixed.b + color.b*(1-blending));
			context.strokeStyle="rgba("+mixr+", "+mixg+", "+mixb+", " + opacity + ")";
			context.beginPath();
			context.moveTo(lastX, lastY);
			context.lineTo(x,y);
			context.stroke();
			context.strokeStyle="rgba("+mixr+", "+mixg+", "+mixb+", " + opacity + ")";
			//drawDot(x, y);
			for(e = 0; e < points.length; e++)
			{
				b = points[e][0] - points[count][0];
				a = points[e][1] - points[count][1];
				g=b*b+a*a;
				if(g<4000*scale*scale&&Math.random()>g/2000/scale/scale)
				{
					context.beginPath();
					context.moveTo(points[count][0]+(b*0.3),points[count][1]+(a*0.3));
					context.lineTo(points[e][0]-(b*0.3),points[e][1]-(a*0.3));
					context.stroke()
				}
			}
			count++;
			context.restore();
			lastX = x;
			lastY = y;
			lastInput.x = x;
			lastInput.y = y;
		};
		div.endLine = function()
		{
			started = false;
			count = 0;
			points = [];
		};
		return div;
	},
	eraser: function(sizeWatcher)
	{
		var div = document.createElement("div"); // the gui
		div.name = "Eraser";
		var context;
		var size = 10, spacing = 0.5, opacity = 0.5;
		var lastDot, lastX, lastY;
		var lastInput = {};
		started = false;
		sizeWatcher(size);
		div.getSize = function()
		{
			return size;
		};
		
		function init()
		{
			var sizeSlider = new BV.pcSlider({label:"Size", w:250, h:30, start:1, end:100, startval:0.25, func:function(val)
			{
				size = val;
				lastInput = {};
				sizeWatcher(size);
			}, spline: [[0,1],[0.5,20],[1,100]]});
			var opacitySlider = new BV.pcSlider({label:"Opacity", w:250, h:30, start:0, end:100, startval:50, func:function(val)
			{
				lastInput = {};
				opacity = val/100;
			}});
			var spacingSlider = new BV.pcSlider({label:"Spacing", w:250, h:30, start:0.05, end:10, startval:0.5*15, func:function(val)
			{
				lastInput = {};
				spacing = val/15;
			}});
			opacitySlider.style.marginTop = "10px";
			spacingSlider.style.marginTop = "10px";
			div.appendChild(sizeSlider);
			div.appendChild(opacitySlider);
			div.appendChild(spacingSlider);
		};
		init();
		
		function drawDot(x, y)
		{
			context.save();
			context.globalCompositeOperation="destination-out";
			var radgrad = context.createRadialGradient(realsize,realsize,0,realsize,realsize,realsize);
			radgrad.addColorStop(0, "rgba(0, 0, 0, "+opacity+")");
			radgrad.addColorStop(1, "rgba(0, 0, 0, 0)");
			context.fillStyle = radgrad;
			context.translate(x-realsize, y-realsize);
			context.fillRect(0,0,realsize*2,realsize*2);
			context.restore();
		}
		
		div.setColor = function(c)
		{
			lastInput = {};
		};
		div.setContext = function(c)
		{
			context = c;
			lastInput = {};
		}
		div.startLine = function(x, y, p, shift)
		{
			if(shift && lastInput.x) {
				var lx = lastInput.x, ly = lastInput.y;
				started = true;
				div.goLine(x,y,p);
				div.endLine();
			} else {
				started = true;
				realsize = Math.max(1, p*size);
				drawDot(x, y);
				lastX = x;
				lastY = y;
				lastDot = size * spacing;
				lastInput.x = x;
				lastInput.y = y;
			}
		};
		div.goLine = function(x, y, p) {
			if(!started)
				return
			realsize = Math.max(1, p*size);
			var mouseDist = Math.sqrt(Math.pow(x - lastX, 2.0) + Math.pow(y - lastY, 2.0));
			var eX = (x - lastX) / mouseDist;
			var eY = (y - lastY)  / mouseDist;
			var loopDist;
			var bdist = realsize * spacing;
			for(loopDist = lastDot; loopDist <= mouseDist; loopDist += bdist)
			{
				drawDot(lastX + eX * loopDist, lastY + eY * loopDist);
			}
			lastDot = loopDist - mouseDist;
			lastX = x;
			lastY = y;
			lastInput.x = x;
			lastInput.y = y;
		};
		div.endLine = function()
		{
			started = false;
		};
		return div;
	}
};
