// VML renderer for Verlet physics engine
// by Andreas Blixt <andreasblixt@msn.com> 2007
// Free for use. Please include this header.

VMLRenderer = function (world, parent) {
	// Make sure a world was passed.
	if (!(world instanceof World)) throw "First argument must be an instance of World.";

	// Store a reference to the specified world.
	this.World = world;

	// If no parent element was passed, use the body element.
	if (!parent) parent = document.getElementsByTagName("body")[0];
	if (!parent) throw "Failed to find <body> element in DOM.";

	// Create a container.
	var container = document.createElement("div");

	// Style the container.
	with (container.style) {
		background = "#aaa";
		border = "1px solid #000";
		height = this.Height + "px";
		overflow = "hidden";
		width = this.Width + "px";
	}

	// Get a reference to this object.
	var thiz = this;

	// Check if the browser supports methods required for VML to work.
	if (document.namespaces && document.namespaces.add && document.createStyleSheet) {
		// Initialize VML support.
		if (!VMLRenderer.Initialized) {
			document.namespaces.add("v", "urn:schemas-microsoft-com:vml");

			var style = document.createStyleSheet();
			style.addRule("v\\:*", "behavior: url(#default#VML);");

			VMLRenderer.Initialized = true;
		}

		// Use non-flow layout for viewport.
		container.style.position = "relative";

		// Create a VML shape.
		var shape = document.createElement("v:shape");
		with (shape.style) {
			position = "absolute";
			left = "0";
			top = "0";
		
			width = "100%";
			height = "100%";
		}

		shape.setAttribute("fillcolor", "#fff");
		shape.setAttribute("strokecolor", "#000");
		shape.setAttribute("strokeweight", "3px");

		// Add shape to container.
		container.appendChild(shape);

		// Store a reference to the shape element.
		this.Shape = shape;
	} else {
		// Restyle the container.
		with (container.style) {
			background = "#ddd";
			border = "1px solid #f00";
		}

		// Create an element which will hold an error message.
		var p = document.createElement("p");
		p.appendChild(document.createTextNode("No VML support found."));

		// Style the error message.
		with (p.style) {
			color = "#f00";
			fontFamily = "Arial, Helvetica, sans-serif";
			fontSize = "small";
			fontWeight = "bold";
			margin = "0";
			padding = "0";
			textAlign = "center";
			whiteSpace = "nowrap";
		}

		// Replace render function.
		this.Render = function () {
			thiz.Container.style.width = thiz.Width + "px";
			thiz.Container.style.height = thiz.Height + "px";
			thiz.Container.childNodes[0].style.lineHeight = thiz.Height + "px";
		};

		// Add the error message to the container.
		container.appendChild(p);
	}

	// Store a reference to the container element.
	this.Container = container;

	// Hijack the update function of the world.
	var update = world.Update;
	world.Update = function () { update.call(world); thiz.Render.call(thiz); };

	// Add container to the parent element.
	parent.appendChild(container);

	// Render.
	this.Render();
};

VMLRenderer.Initialized = false;

VMLRenderer.prototype = {
	// A reference to the world which will be rendered.
	World: null,

	// The scale at which to draw the scene.
	Scale: 1.0,

	// The dimensions of the viewport of this renderer.
	Width: 400, Height: 300,

	// A reference to the container element.
	Container: null,

	// A reference to the VML shape.
	Shape: null,

	// Renders the world.
	Render: function () {
		// Apply scale.
		this.Shape.setAttribute("coordsize", Math.round(this.Container.clientWidth / this.Scale) + "," + Math.round(this.Container.clientHeight / this.Scale));
		this.Shape.setAttribute("strokeweight", Math.ceil(3 * this.Scale) + "px");

		// Show the playable area.
		var minX = isNaN(this.World.BoundaryMinX) ? 0 : this.World.BoundaryMinX;
		var minY = isNaN(this.World.BoundaryMinY) ? 0 : this.World.BoundaryMinY;
		var maxX = isNaN(this.World.BoundaryMaxX) ? this.Canvas.width : this.World.BoundaryMaxX;
		var maxY = isNaN(this.World.BoundaryMaxY) ? this.Canvas.height : this.World.BoundaryMaxY;

		var path = "m" + minX + "," + minY + " ns l" + maxX + "," + minY + "," + maxX + "," + maxY + "," + minX + "," + maxY + " x e";

		// Loop through all the objects in the world.
		for (var i in this.World.All) {
			var o = this.World.All[i];

			if (o instanceof Atom) {
				var r = Math.round(o.Radius);
				var x1 = Math.round(o.CurrentX) - r;
				var y1 = Math.round(o.CurrentY) - r;
				var x2 = x1 + r * 2;
				var y2 = y1 + r * 2;

				path += " ar" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + (x1 + 1) + "," + y1 + "," + (x1 + 1) + "," + y1 + " e";
			} else if (o instanceof Constraint) {
				path += " m" + Math.round(o.Atom1.CurrentX) + "," + Math.round(o.Atom1.CurrentY) + " nf l" + Math.round(o.Atom2.CurrentX) + "," + Math.round(o.Atom2.CurrentY) + " e";
			} else if (o instanceof Obstacle) {
				path += " m" + o.X + "," + o.Y + " nf r" + o.Width + ",0,0," + o.Height + ",-" + o.Width + ",0 x e";
			}
		}

		// Apply path.
		this.Shape.setAttribute("path", path);
	},

	// Resizes the viewport of this renderer.
	Size: function (w, h) {
		this.Width = w;
		this.Height = h;

		with (this.Container.style) {
			height = h + "px";
			width = w + "px";
		}
	}
};