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

CanvasRenderer = 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 canvas element.
	var canvas = document.createElement("canvas");

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

	// Check if the browser supports canvas.
	if (canvas && canvas.getContext) {
		// Style the canvas.
		canvas.style.border = "1px solid #000";

		// Set canvas dimensions.
		canvas.width = this.Width;
		canvas.height = this.Height;
	} else {
		// Make a placeholder for the canvas element.
		canvas = document.createElement("div");

		// Style the placeholder.
		with (canvas.style) {
			background = "#ddd";
			border = "1px solid #f00";
			height = this.Height + "px";
			overflow = "hidden";
			width = this.Width + "px";
		}

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

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

		// Replace render function.
		this.Render = null;

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

	// Store a reference to the canvas element.
	this.Canvas = canvas;

	// Add this canvas to the parent element.
	parent.appendChild(canvas);

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

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

CanvasRenderer.prototype = {
	// A reference to the canvas element being used.
	Canvas: null,

	// 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,

	// Renders the world.
	Render: function () {
		// Get a 2D context object from the canvas.
		var context = this.Canvas.getContext("2d");
		with (context) {
			// Save the current context state, before changing anything.
			save();

			// Clear the canvas.
			fillStyle = "#aaa";
			fillRect(0, 0, this.Canvas.width, this.Canvas.height);

			// Set scaling.
			scale(this.Scale, this.Scale);

			// 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;

			fillStyle = "#fff";
			fillRect(minX, minY, maxX - minX, maxY - minY);

			// Set the drawing style for atoms and constraints.
			fillStyle = "#000";
			lineWidth = 3;
			strokeStyle = "#000";

			// 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) {
					beginPath();
					arc(o.CurrentX, o.CurrentY, o.Radius, 0, Math.PI * 2, true);
					fill();
				} else if (o instanceof Constraint) {
					beginPath();
					moveTo(o.Atom1.CurrentX, o.Atom1.CurrentY);
					lineTo(o.Atom2.CurrentX, o.Atom2.CurrentY);
					stroke();
				} else if (o instanceof Obstacle) {
					fillRect(o.X, o.Y, o.Width, o.Height);
				}
			}

			// Restore the context state.
			restore();
		}
	},

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

		if (this.Canvas.getContext) {
			this.Canvas.height = h;
			this.Canvas.width = w;
		} else {
			with (this.Canvas.style) {
				height = h + "px";
				width = w + "px";
			}

			this.Canvas.childNodes[0].style.lineHeight = h + "px";
		}
	}
};