/**
 *
 * Based on:
 *   canvas-text <http://code.google.com/p/canvas-text/>
 *   uuCanvas.js <http://code.google.com/p/uupaa-js-spinoff/>
 *
 */
/**
 * Copyright (c) 2008 Fabien Ménager
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/**
 * Copyright (c) 2008,2009 Takao Obara.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


(function() {
	var _canvas = document.createElement("canvas");
	if (!_canvas || !_canvas.getContext)
		return;

	_canvas.width = 1;
	_canvas.height = 1;
	var _ctx = _canvas.getContext('2d');
	var proto = window.CanvasRenderingContext2D ? window.CanvasRenderingContext2D.prototype :  _ctx.__proto__;

	window.CanvasUtils = {
		isCopyOkWithTransparent: false,
	};


	// check globalCompositeOperation = copy
	// http://code.google.com/p/chromium/issues/detail?id=6609
	_ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
	_ctx.fillRect(0, 0, 1, 1);
	_ctx.globalCompositeOperation = "copy";
	_ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
	_ctx.fillRect(0, 0, 1, 1);
	try {
		window.CanvasUtils.isCopyOkWithTransparent = (_ctx.getImageData(0, 0, 1, 1).data[3] === 128);
	} catch (ex) {}


	// https://bugzilla.mozilla.org/show_bug.cgi?id=436932
	if (!("mozImageSmoothingEnabled" in proto))
		proto.mozImageSmoothingEnabled = true;

	if ("fillText" in proto)
		return;


	window.TextMetrics = function (width) {
		this.width = parseFloat(width, 10);
	}
	proto.font = "10px sans-serif";
	proto.textBaseline = "alphabetic"; // XXX not supported
	proto.textAlign = "start";


	// Mozilla Extensions
	if ("mozDrawText" in proto) {
		proto.measureText = function(text) {
			this.save();
			this.mozTextStyle = this.font;
			var width = this.mozMeasureText(text);
			this.restore();
			return new TextMetrics(width);
		}

		proto.fillText = function(text, x, y, maxWidth) {
			this.save();
			this.mozTextStyle = this.font;
			var width = this.mozMeasureText(text);
			var scale = 1;
			if (maxWidth && width > maxWidth) {
				scale = maxWidth / width;
				width = maxWidth;
			}

			var offsetX = 0, offsetY = 0;
			var direction = document.defaultView.getComputedStyle(this.canvas, "").direction;
			switch(this.textAlign) {
			case "left": break;
			case "right": offsetX = -width; break;
			case "center": offsetX = -width / 2; break;
			case "start": offsetX = (direction == "rtl") ? -width : 0; break;
			case "end": offsetX = (direction == "ltr") ? -width : 0; break;
			}

			this.translate(x + offsetX, y + offsetY);
			this.scale(scale, 1);
			this.mozDrawText(text);
			this.restore();
		}

		return;
	}


	// SVG
	var _svgAvailable = false;
	var _svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
	try {
		_ctx.drawImage(_svgEl, 0, 0);
		_svgAvailable = true;
	} catch (ex) {}

	if (_svgAvailable) {
		function svge(nodeName) {
			return document.createElementNS("http://www.w3.org/2000/svg", nodeName);
		}

		function attr(el, hash) {
			for (var i in hash) {
				el.setAttributeNS(null, i, hash[i]);
			}
		}

		function createShadow(color, sx, sy, blur) {
			/*
			 * http://www.w3.org/TR/2007/WD-SVGFilter12-20070501/#feDropShadowElement
			 *
			 * The result of a 'feDropShadow' filter primitive is equivalent to the
			 * following:
			 * <feGaussianBlur in="alpha-channel-of-feDropShadow-in" stdDeviation="stdDeviation-of-feDropShadow"/>
			 * <feOffset dx="dx-of-feDropShadow" dy="dy-of-feDropShadow" result="offsetblur"/>
			 * <feFlood flood-color="flood-color-of-feDropShadow" flood-opacity="flood-opacity-of-feDropShadow"/>
			 * <feComposite in2="offsetblur" operator="in"/>
			 * <feMerge>
			 *   <feMergeNode/>
			 *   <feMergeNode in="in-of-feDropShadow"/>
			 * </feMerge>
			 */
			 var defsEl = svge("defs");

			var filterEl = svge("filter");
			defsEl.appendChild(filterEl);
			attr(filterEl, {
				id: "shadow",
				filterUnits: "userSpaceOnUse"
			});

			/*
			 * http://www.whatwg.org/specs/web-apps/current-work/#shadows
			 *
			 * If shadowBlur is less than 8, let σ be half the value of shadowBlur;
			 * otherwise, let σ be the square root of multiplying
			 * the value of shadowBlur by 2.
			 */
			var blurEl = svge("feGaussianBlur");
			filterEl.appendChild(blurEl);
			attr(blurEl, {
				"in": "SourceAlpha",
				stdDeviation: (blur < 8) ? blur / 2 : Math.sqrt(blur * 2)
			});

			var offsetEl = svge("feOffset");
			filterEl.appendChild(offsetEl);
			attr(offsetEl, {
				result: "offsetblur",
				dx: sx,
				dy: sy
			});

			var floodEl = svge("feFlood");
			filterEl.appendChild(floodEl);
			attr(floodEl, {
				"flood-color": color
			});

			var compositeEl = svge("feComposite");
			filterEl.appendChild(compositeEl);
			attr(compositeEl, {
				in2: "offsetblur",
				operator: "in"
			});

			var mergeEl = svge("feMerge");
			filterEl.appendChild(mergeEl);

			var mergeNodeEl = svge("feMergeNode");
			mergeEl.appendChild(mergeNodeEl);

			mergeNodeEl = svge("feMergeNode");
			mergeEl.appendChild(mergeNodeEl);
			attr(mergeNodeEl, {
				"in": "SourceGraphic"
			});

			return defsEl;
		}

		proto.measureText = function(text) {
			var svgEl = svge("svg");
			var textEl = svge("text");
			svgEl.appendChild(textEl);
			attr(textEl, {
				font: this.font
			});
			textEl.textContent = text;
			var width = textEl.getComputedTextLength();
			return new TextMetrics(width);
		}

		proto.fillText = function(text, x, y, maxWidth) {
			var width = this.measureText(text).width;
			var scale = 1;
			if (width > maxWidth) {
				scale = maxWidth / width;
				width = maxWidth;
			}
			var offsetX = 0, offsetY = 0;
			var direction = document.defaultView.getComputedStyle(this.canvas, "").direction;
			switch(this.textAlign) {
			case "left": break;
			case "right": offsetX = -width; break;
			case "center": offsetX = -width / 2; break;
			case "start": offsetX = (direction == "rtl") ? -width : 0; break;
			case "end": offsetX = (direction == "ltr") ? -width : 0; break;
			}



			var svgEl = svge("svg");
			/*
			 * http://www.whatwg.org/specs/web-apps/current-work/#when-shadows-are-drawn
			 *
			 * Shadows are only drawn if the opacity component of the alpha
			 * component of the color of shadowColor is non-zero and either
			 * the shadowBlur is non-zero, or the shadowOffsetX  is non-zero,
			 * or the shadowOffsetY is non-zero.
			 */
			// XXX check opacity component
			if (this.shadowOffsetX != 0 || this.shadowOffsetY != 0 || this.shadowBlur != 0) {
				// bad quality...
				// svgEl.appendChild(createShadow(this.shadowColor, this.shadowOffsetX, this.shadowOffsetY, this.shadowBlur));
			}


			var textEl = svge("text");
			textEl.textContent = text;
			blur = this.shadowBlur;
			attr(textEl, {
				font: this.font,
			});
			svgEl.appendChild(textEl);

			var boxWidth = textEl.getBBox().width;
			var boxHeight = textEl.getBBox().height;
			attr(svgEl, {
					width: boxWidth * 2 + "px",
					height: boxHeight * 2 + "px"
			});

			attr(textEl, {
				x: blur + "px",
				y: blur + boxHeight + "px",
				fill: this.fillStyle,
				// filter: "url(#shadow)",
				transform: "scale(" + scale + ", 1)"
			});

			document.body.appendChild(svgEl);
			this.save()
			this.drawImage(svgEl, x + offsetX - blur, y + offsetY - blur - boxHeight);
			this.restore();
			document.body.removeChild(svgEl);
		}

		return;
	}

	// Safari 3...
	proto.measureText = function(text) {
		return new TextMetrics(0);
	}
	proto.fillText = function(text, x, y, maxWidth) {}
})();


