define(["lib/utils"],
function(utils) {
	"use strict";

	function distSq(dx, dy) {
		return dx * dx + dy * dy;
	}

	function distance(dx, dy) {
		return Math.sqrt(dx * dx + dy * dy);
	}

	function SDF(w, h) {
		var X = new Int16Array(w * h);
		var Y = new Int16Array(w * h);
		this.X = X;
		this.Y = Y;

		this.generateSDF = function() {
			var x, y, pos, dx, dy, dsq;

			function compareAndUpdate(offsetx, offsety) {
				var nx = x + offsetx, ny = y + offsety;
				if (nx >= 0 && ny >= 0 && nx < w && ny < h) {
					var npos = ny * w + nx;
					var ndx = X[npos] + offsetx;
					var ndy = Y[npos] + offsety;
					var ndsq = distSq(ndx, ndy);
					if (ndsq < dsq) {
						dx = ndx;
						dy = ndy;
						dsq = ndsq;
					}
				}
			}

			for (y = 0; y < h; y++) {
				for (x = 0; x < w; x++) {
					pos = y * w + x;
					dx = X[pos];
					dy = Y[pos];
					dsq = distSq(dx, dy);

					compareAndUpdate(-1,  0);
					compareAndUpdate(-1, -1);
					compareAndUpdate( 0, -1);
					compareAndUpdate( 1, -1);

					X[pos] = dx;
					Y[pos] = dy;
				}

				for (x = w - 1; x >= 0; x--) {
					pos = y * w + x;
					dx = X[pos];
					dy = Y[pos];
					dsq = distSq(dx, dy);

					compareAndUpdate(1, 0);

					X[pos] = dx;
					Y[pos] = dy;
				}
			}

			for (y = h - 1; y >= 0; y--) {
				for (x = w - 1; x >= 0; x--) {
					pos = y * w + x;
					dx = X[pos];
					dy = Y[pos];
					dsq = distSq(dx, dy);

					compareAndUpdate( 1,  0);
					compareAndUpdate( 1,  1);
					compareAndUpdate( 0,  1);
					compareAndUpdate(-1,  1);

					X[pos] = dx;
					Y[pos] = dy;
				}

				for (x = 0; x < w; x++) {
					pos = y * w + x;
					dx = X[pos];
					dy = Y[pos];
					dsq = distSq(dx, dy);

					compareAndUpdate(-1, 0);

					X[pos] = dx;
					Y[pos] = dy;
				}
			}
		};
	}


	function sdfize(image) {
		var w = image.width, h = image.height;
		var g1 = new SDF(w, h);
		var g2 = new SDF(w, h);

		var x, y, v, pos;

		var INF = 9999;
		for (y = 0; y < h; y++) {
			for (x = 0; x < w; x++) {
				pos = y * w + x;
				v = image.data[4 * pos] + image.data[4 * pos + 1] + image.data[4 * pos + 2];
				if (v < 3 * 128) {
					g1.X[pos] = 0;
					g1.Y[pos] = 0;
					g2.X[pos] = INF;
					g2.Y[pos] = INF;
				} else {
					g1.X[pos] = INF;
					g1.Y[pos] = INF;
					g2.X[pos] = 0;
					g2.Y[pos] = 0;
				}
			}
		}

		g1.generateSDF();
		g2.generateSDF();

		var canvas = document.createElement('canvas');
		// document.body.appendChild(canvas);
		canvas.width = w;
		canvas.height = h;
		var ctx = canvas.getContext("2d");
		var result = ctx.getImageData(0, 0, canvas.width, canvas.height);
		var scale = 256 / Math.max(w, h);
		for (y = 0; y < h; y++) {
			for (x = 0; x < w; x++) {
				pos = y * w + x;
				var dist1 = distance(g1.X[pos], g1.Y[pos]);
				var dist2 = distance(g2.X[pos], g2.Y[pos]);
				var dist = dist1 - dist2;

				var c = 128 + scale * dist;
				result.data[4 * pos + 0] = utils.clamp(c, 0, 255);
				result.data[4 * pos + 1] = utils.clamp(c, 0, 255);
				result.data[4 * pos + 2] = utils.clamp(c, 0, 255);
				result.data[4 * pos + 3] = 255;
			}
		}
		ctx.putImageData(result, 0, 0);
		return result;
	}

	return {
		sdf: sdfize
	};
});
