
function d2( a, b ) { return Math.pow( a.x - b.x, 2 ) + Math.pow( a.y - b.y, 2 ) }
function xml( ns, n, a, c ) {
	var e = document.createElementNS( ns, n );
	if( a ) for( k in a ) e.setAttribute( k, a[k] );
	if( c ) for( var i = 0 ; i < c.length ; ++i ) e.appendChild( c[i] );
	return e;
}
function svg( n, a, c ) { return xml( "http://www.w3.org/2000/svg", n, a, c ); }
function html( n, a, c ) { return xml( "http://www.w3.org/1999/xhtml", n, a, c ); }
function text( t ) { return document.createTextNode( t ); }
function remove( e ) { e.parentNode.removeChild(e); };


function init( e, txt ) {
	// params:
	var max_height = 6000;
	var line_height = 50;
	var width = 1000, height = 300;
	var tol = 1.5;
	var queue_length = 2;

	// init.
	var button = -1;


	var msg = svg( "foreignObject", { x: 0, y: 0, width: "100%", height: "100%" },
		[ html( "p", {}, [ text( "Enter your handwritten comment here!" ) ] ),
		html( "p", { "class": "instr" }, [ text( "Ctrl/⌥/⌘+Click to delete." ) ] ) ]
	);

	// does it support svg:foreignObject? if yes, we suppose full SVG support.
	var old_browser = !( msg.width && msg.width.baseVal );

	var paper = svg( "g", { "class": "paper" } );
	for( var y = line_height ; y <= max_height - line_height ; y += line_height )
		paper.appendChild( svg( "line", { "class": "h", x1: 0, y1: y, x2: "100%", y2: y } ) );
	for( var x = line_height ; x < width ; x += line_height )
		paper.appendChild( svg( "line", { "class": "v", x1: x, y1: 0, x2: x, y2: "100%" } ) );

	var c = svg( "g" );
	var b = svg( "g", {}, [ svg( "rect", { x: 0, y: 0, width: "100%", height: "100%" } ), c ] );

	var templ = svg( "polyline", { "class": "drawing" } );
	var root = svg( "svg", { version: "1.0", preserveAspectRatio: "xMinYMin meet", width: "100%", viewBox: "0 0 " + width + " " + height }, [ msg, paper, b ] );
	var wrap = html( "div", { "class": "doodlewrap" }, [ root ] );
	e.appendChild( wrap );

	var update_height = function() {
		if( old_browser ) {
			// calculate height in pixels for Firefox2
			var scale = root.width.baseVal.value;
			root.height.baseVal.value = height / width * scale;
		} else {
			// make Firefox3 update the box
			root.setAttribute( "height", "100%" );
			root.removeAttribute( "height" );
		}
	};
	update_height();


	var current_line = null;

	var P = function( x, y ) {
		var p = root.createSVGPoint();
		p.x = x;
		p.y = y;
		return p;
	}, point = function(evt) {
		return P( evt.clientX, evt.clientY ).matrixTransform( root.getScreenCTM().inverse() );
	}, round = function(p) {
		return P( Math.round(p.x), Math.round(p.y) );
	}, avg = function(l) {
		var x = 0.0, y = 0.0;
		for( var i = 0 ; i < l.length ; ++i ) {
			x += l[i].x;
			y += l[i].y;
		}
		return P( x / l.length, y / l.length );
	}, enlarge = function() {
		try {
			var bb = c.getBBox();
			var h = bb.height + bb.y;
			if( h > root.viewBox.baseVal.height - 100 && root.viewBox.baseVal.height < max_height ) {
				height = root.viewBox.baseVal.height = Math.min( h + 100, max_height );
				update_height();
			}
		} catch(e) {}
	}, deserialize = function() {
		var tokens = txt.value.split( ' ' );
		if( tokens[0] == 'DOODLE_START' ) {
			var e = null, ser = "";
			var push = function() { if( e ) {
				e.setAttribute( "__serialised", ser );
				c.appendChild( e );
			} };
			for( var i = 1, l = tokens.length ; i < l ; ++i ) {
				if( tokens[i][0] == 'L' ) {
					push();
					ser = "L ";
					e = svg( "polyline", { "class": "simple drawn" } );
				} else if( tokens[i][0] == 'C' ) {
					push();
					e = null;
					var d = tokens[i].split( ':' );
					if( d.length == 3 )
						c.appendChild( svg( "circle", {
							stroke: "none", fill: "black", "__serialised": tokens[i] + " ",
							r: 2.2, cx: +d[1], cy: +d[2] } ) );
				} else if( e && e.points ) {
					var d = tokens[i].split( ':' );
					if( d.length == 2 ) {
						ser += tokens[i] + " ";
						e.points.appendItem( P( +d[0], +d[1] ) );
					}
				}
			}
			push();
			remove( msg );
			msg = null;
			enlarge();
		} else txt.value = "";
	}, serialize = function() {
		var ser = "";
		var doit = function( l ) { for( var i = 0 ; i < l.length ; ++i ) {
			var c = l[i].getAttribute( "__serialised" );
			if( c ) ser += c + " ";
		} };
		doit( root.getElementsByTagName( "circle" ) );
		doit( root.getElementsByTagName( "polyline" ) );
		txt.value = "DOODLE_START " + ser;
	};

	deserialize();

	var queue = [];

	root.addEventListener( "mousedown", function(evt) {
		evt.preventDefault(); evt.stopPropagation();
		if( current_line ) return;
		button = evt.button;
		current_line = templ.cloneNode(true);
		queue.push( point(evt) );
		//current_line.points.appendItem( point(evt) );
		c.appendChild(current_line);
		c.setAttribute( "class", "drawing" );
	}, true );

	root.addEventListener( "mousemove", function(evt) {
		evt.preventDefault(); evt.stopPropagation();
		if( current_line ) {
			if( evt.button == button ) {
				queue.push( point(evt) );
				if( queue.length >= queue_length ) {
					current_line.points.appendItem( round( avg( queue ) ) );
					queue.shift();
				}
				//current_line.points.appendItem( point(evt) );
				enlarge();
			} else {
				remove( current_line );
				current_line == null;
			}
		}
	}, true );

	var small = function() {
		if( current_line.points.numberOfItems < 3 ) return true;
		var bb = current_line.getBBox();
		return bb.width + bb.height < 5;
	}, overlaps = function( a, b ) {
		return Math.abs( a.x - b.x ) <= (a.width + b.width)
		    && Math.abs( a.y - b.y ) <= (a.height + b.height);
	}, box = function( p, s ) {
		return { x: p.x - s, y: p.y - s, width: 2 * s, height: 2 * s };
	}, outer = function( a, b ) {
		return {
			x: Math.min( a.x, b.x ),
			y: Math.min( a.y, b.y ),
			width: Math.max( a.x + a.width, b.x + b.width ) - Math.min( a.x, b.x ),
			height: Math.max( a.y + a.height, b.y + b.height ) - Math.min( a.y, b.y )
		};
	}


	var mouseup = function(evt) {
	//	evt.preventDefault(); evt.stopPropagation();
		c.className.baseVal = "";
		queue = [];
		if( evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey ) { // delete!
			var pt = box( point(evt), 5 );
			var cc = current_line.points.numberOfItems > 2 ? outer( current_line.getBBox(), pt ) : pt;
			remove( current_line );
			current_line = null;
			// arr, SVGSVGElement::getIntersectionList & checkIntersection not implemented!
			// thus dirrty method.
			var kill = function( l ) {
				for( var i = 0 ; i < l.length ; ++i )
					if( overlaps( cc, l[i].getBBox() ) )
						remove( l[i] );
			};
			kill( c.getElementsByTagName("polyline") );
			kill( c.getElementsByTagName("circle") );
			serialize();
		} else if( current_line ) { try {
			if( small() ) {
				var p = round( point(evt) );
				if( p.x > 0 && p.y > 0 && p.x < width && p.y < height ) {
					var ser = "C:" + p.x + ":" + p.y + " ";
					txt.value += ser;
					c.appendChild( svg( "circle", { cx: p.x, cy: p.y, r: 2.2, "__serialised": ser } ) );
				}
				remove( current_line );
			} else {

				var e = current_line;
				var tol2 = Math.pow( tol, 2 );

				var vt = [];
				for( var i = 0, l = e.points.numberOfItems ; i < l ; ++i ) {
					var q = e.points.getItem( i );
					vt.push( { x: q.x, y: q.y, m: false } );
				}

				vt[0].m = vt[ vt.length - 1].m = true;

				var simplifyDP = function( j, k ) {
					if( k <= j + 1 ) return;

					var p0 = vt[j], p1 = vt[k];
					var u = { x: p1.x - p0.x, y: p1.y - p0.y };
					var cu = Math.pow( u.x, 2 ) + Math.pow( u.y, 2 );
					var maxd2 = 0, maxi = j;

					for( var i = j + 1 ; i < k ; ++i ) {
						var cw = (vt[i].x - p0.x) * u.x + (vt[i].y - p0.y) * u.y;
						var dv2 = 1e9;
						if( cw <= 0 ) dv2 = d2( vt[i], p0 );
						else if( cu <= cw ) dv2 = d2( vt[i], p1 );
						else dv2 = d2( vt[i], { x: p0.x + cw / cu * u.x, y: p0.y + cw / cu * u.y } );
						if( dv2 > maxd2 ) {
							maxi = i;
							maxd2 = dv2;
						}
					}

					if( maxd2 > tol2 ) {
						vt[ maxi ].m = true;
						simplifyDP( j, maxi ); simplifyDP( maxi, k );
					}
				};
				simplifyDP( 0, vt.length - 1 );

				e.points.clear();
				var ser = "L ";
				for( var i = 0, l = vt.length ; i < l ; ++i ) if( vt[i].m ) {
					e.points.appendItem( P( vt[i].x, vt[i].y ) );
					ser += vt[i].x + ":" + vt[i].y + " ";
				}
				e.setAttribute( "__serialised", ser );
				txt.value += ser;
				e.className.baseVal = "simple drawn";

				if( ser.length > 50 && msg ) {
					remove( msg );
					msg = null;
				}
			} } finally {
				current_line = null;
			}
		}
	};

//	b.addEventListener( "mouseup", mouseup, false );
	document.addEventListener( "mousemove", function(evt) {
		// arrr drag&drop kills!
		evt.preventDefault(); evt.stopPropagation();
		if( evt.target != root && evt.target.ownerSVGElement != root ) mouseup( evt );
	}, false );
	// we keep missing mouseups; desperate "solution":
	window.addEventListener( "mouseup", mouseup, false );

};



function init_all() {
	if( !document.createElementNS || !document.doctype || /*!document.doctype.publicId
	 || document.doctype.publicId.indexOf("SVG") <= 5 ||*/ !svg( "svg" ).getScreenCTM ) return;

	// only good browsers should make it to this point.
	// unfortunately, Opera does, too. But the script doesn't work there...

	document.getElementsByTagName( "head" )[0].appendChild( html( "link", { rel: "stylesheet", href: "http://pascal.germroth.name/wp/wp-content/plugins/pg-doodle/doodle.css", type: "text/css" } ) );
	var areas = document.getElementsByTagName( "textarea" );
	for( var i = 0 ; i < areas.length ; ++i ) ( function( area ) {
		var p = area.parentNode;
		var id = area.id
		if( area.value.indexOf( "DOODLE_START" ) == 0 ) {
			remove( area );
			var txt = html( "input", { name: id, id: id, type: "hidden", value: area.value } );
			p.appendChild( txt );
			init( p, txt );
		} else {
			var btn = html( "button", { type: "button", title: "This will erase your text comment though." }, [ text( "Draw something!" ) ] );
			var q = html( "p", { "class": "enabledraw" }, [ btn ] );
			btn.addEventListener( "click", function(event) {
				remove( area );
				var txt = html( "input", { name: id, id: id, type: "hidden", value: "DOODLE_START " } );
				p.appendChild( txt );
				init( p, txt );
				remove( q );
			}, false );
			p.parentNode.insertBefore( q, p );
		}
	} )( areas[i] );
};

if( window.addEventListener )
	window.addEventListener( "load", init_all, false );

/* vim: set ft=javascript : */

