module jg {
	export interface BrowserInfo {
		chrome?:bool;
		webkit?:bool;
		safari?:bool;
		opera?:bool;
		msie?:bool;
		mozilla?:bool;
		version?:string;
	}
	export class JGUtil {
		static browser:BrowserInfo;

		static getCenterPoint(p:CommonOffset):CommonOffset {
			var a = <CommonArea>p;
			if (a.width && a.height) {
				return {
					x: p.x + a.width / 2,
					y: p.y + a.height / 2
				}
			}
			return p;
		}

		static getMargin(p:CommonOffset):CommonOffset {
			var a = <CommonArea>p;
			if (a.width && a.height) {
				return {
					x: a.width / 2,
					y: a.height / 2
				}
			}
			return {x:0, y:0};
		}

		static intersect(p1:CommonOffset, p2:CommonOffset):bool {
			var a1 = <CommonArea>p1;
			var a2 = <CommonArea>p2;
			if (a1.width && a1.height) {
				if (a2.width && a2.height) {
					//area:area
					return a1.x <= (a2.x+a2.width) &&
					       a2.x <= (a1.x+a1.width) &&
					       a1.y <= (a2.y+a2.height) &&
					       a2.y <= (a1.y+a1.height);
				} else {
					//area:point
					return a2.x >= a1.x && a2.x <= (a1.x + a1.width)
					    && a2.y >= a1.y && a2.y <= (a1.y + a1.height);
				}
			} else if (a2.width && a2.height) {
				//point:area
				return a1.x >= a2.x && a1.x <= (a2.x + a2.width)
				    && a1.y >= a2.y && a1.y <= (a2.y + a2.height);
			}

			//point:point
			return p1.x == p2.x && p1.y == p2.y;
		}

		static getDistance(p1:CommonOffset, p2:CommonOffset):CommonOffset {
			var _p1 = JGUtil.getCenterPoint(p1);
			var _p2 = JGUtil.getCenterPoint(p2);
			return {
				x: Math.abs(_p1.x - _p2.x),
				y: Math.abs(_p1.y - _p2.y)
			}
		}

		static getMovePoint(p1:CommonOffset, p2:CommonOffset, power?:number, maxMove?:number):CommonOffset {
			var _p1 = JGUtil.getCenterPoint(p1);
			var _p2 = JGUtil.getCenterPoint(p2);

			if (! power)
				power = 1;
			var ret = {
				x: (_p1.x - _p2.x) * power,
				y: (_p1.y - _p2.y) * power
			}
			var absx = Math.abs(ret.x);
			var absy = Math.abs(ret.y);
			var max = Math.max(absx, absy);
			var xper = absx / max;
			var yper = absy / max;
			if (maxMove) {
				if (absx > maxMove || absy > maxMove) {
					if (ret.x < 0)
						ret.x = -maxMove * xper;
					else
						ret.x = maxMove * xper;
					if (ret.y < 0)
						ret.y = -maxMove * yper;
					else
						ret.y = maxMove * yper;
				}
			}
			return ret;
		}

		static getDirectionAngle(p1: CommonOffset, p2:CommonOffset, minDistance?:number):Angle {
			var _p1 = JGUtil.getCenterPoint(p1);
			var _p2 = JGUtil.getCenterPoint(p2);
			var xp = Math.abs(_p1.x - _p2.x);
			var yp = Math.abs(_p1.y - _p2.y);

			if (minDistance && Math.max(xp, yp) < minDistance)
				return null;

			if (xp > yp)
				return (_p1.x > _p2.x) ? Angle.Left : Angle.Right;
			else
				return (_p1.y > _p2.y) ? Angle.Up : Angle.Down;
		}

		static getDirectionKeytype(p1: CommonOffset, p2:CommonOffset, minDistance?:number):Keytype {
			var _p1 = JGUtil.getCenterPoint(p1);
			var _p2 = JGUtil.getCenterPoint(p2);
			var xp = Math.abs(_p1.x - _p2.x);
			var yp = Math.abs(_p1.y - _p2.y);

			if (minDistance && Math.max(xp, yp) < minDistance)
				return null;

			if (xp > yp)
				return (_p1.x > _p2.x) ? Keytype.Left : Keytype.Right;
			else
				return (_p1.y > _p2.y) ? Keytype.Up : Keytype.Down;
		}

		static homingX(p1:CommonOffset, p2:CommonOffset, speed:number, t:number):bool {
			var m1 = JGUtil.getMargin(p1);
			var m2 = JGUtil.getMargin(p2);
			var x = p2.x + m2.x - m1.x;
			if (p1.x > x) {
				p1.x -= speed * t;
				if (p1.x < x) {
					p1.x = x;
					return true;
				}
				return false;
			} else if (p1.x < x) {
				p1.x += speed * t;
				if (p1.x > x) {
					p1.x = x;
					return true;
				}
				return false;
			}
			return true;
		}

		static homingY(p1:CommonOffset, p2:CommonOffset, speed:number, t:number):bool {
			var m1 = JGUtil.getMargin(p1);
			var m2 = JGUtil.getMargin(p2);
			var y = p2.y + m2.y - m1.y;
			if (p1.y > y) {
				p1.y -= speed * t;
				if (p1.y < y) {
					p1.y = y;
					return true;
				}
				return false;
			} else if (p1.y < y) {
				p1.y += speed * t;
				if (p1.y > y) {
					p1.y = y;
					return true;
				}
				return false;
			}
			return true;
		}

		static homing(p1:CommonOffset, p2:CommonOffset, speed:number, t:number):bool {
			var m1 = JGUtil.getMargin(p1);
			var m2 = JGUtil.getMargin(p2);
			var p = {
				x: p2.x + m2.x - m1.x,
				y: p2.y + m2.y - m1.y
			}
			var xng, yng;
			if (p1.x > p.x) {
				p1.x -= speed * t;
				if (p1.x < p.x)
					p1.x = p.x;
				else
					xng = true;
			} else if (p1.x < p.x) {
				p1.x += speed * t;
				if (p1.x > p.x)
					p1.x = p.x;
				else
					xng = true;
			}

			if (p1.y > p.y) {
				p1.y -= speed * t;
				if (p1.y < p.y)
					p1.y = p.y;
				else
					yng = true;
			} else if (p1.y < p.y) {
				p1.y += speed * t;
				if (p1.y > p.y)
					p1.y = p.y;
				else
					yng = true;
			}

			return xng || yng ? false : true;
		}

		//bubble sort
		static orderDrawY() {
			var e = this.entities, len = e.length;
			for (var i=1; i<len; i++) {
				for (var j=i; j>0; j--) {
					if (e[j].y >= e[j-1].y)
						break;
					//slow e.splice(j-1, 2, e[j],e[j-1]);
					var tmp = e[j];
					e[j] = e[j-1];
					e[j-1] = tmp;
				}
			}
		}

		static createLinearGradient(rect:any, colors:string[], offsets?:number[]):CanvasGradient {
			var canvas = window.createCanvas(1, 1);
			var context = canvas.getContext("2d");

			if (typeof rect == "number") {
				rect = new Rectangle(arguments[0], arguments[1], arguments[2], arguments[3]);
				colors = arguments[4];
				offsets = arguments[5];
			}

			if (offsets == undefined) {
				offsets = [];
				var p = 1 / (colors.length-1);
				for (var i=0; i<colors.length; i++)
					offsets.push(i*p);
			}

			var gradient = context.createLinearGradient(rect.left, rect.top, rect.right, rect.bottom);
			for (var i=0; i<colors.length; i++)
				gradient.addColorStop(offsets[i], colors[i]);

			return gradient;
		}

		static createRadialGradient(rect:any, radius1:number, radius2:number, colors:string[], offsets?:number[]):CanvasGradient {
			var canvas = window.createCanvas(1, 1);
			var context = canvas.getContext("2d");

			if (typeof rect == "number") {
				rect = new Rectangle(arguments[0], arguments[1], arguments[2], arguments[3]);
				radius1 = arguments[4];
				radius2 = arguments[5];
				colors = arguments[6];
				offsets = arguments[7];
			}

			if (offsets == undefined) {
				offsets = [];
				var p = 1 / (colors.length-1);
				for (var i=0; i<colors.length; i++)
					offsets.push(i*p);
			}

			var gradient = context.createRadialGradient(rect.left, rect.top, radius1, rect.right, rect.bottom, radius2);
			for (var i=0; i<colors.length; i++)
				gradient.addColorStop(offsets[i], colors[i]);

			return gradient;
		}

		//repeat: repeat, repeat-x, repeat-y, no-repeat
		static createPattern(image:any, repeat?:string):CanvasPattern {
			var canvas = window.createCanvas(1, 1);
			var context = canvas.getContext("2d");
			return context.createPattern(image, repeat == undefined ? "repeat" : repeat);
		}

		static isStyleScale:bool;
		static isTransformMode() {
			if (JGUtil.isStyleScale === undefined) {
				var test = document.createElement("canvas");
				JGUtil.isStyleScale = test.style['webkitTransform'] === undefined;
			}
			return !JGUtil.isStyleScale;
		}

		static scaleCanvas(canvas:HTMLCanvasElement, size:CommonSize) {
			if (JGUtil.isTransformMode()) {
				canvas.style['webkitTransformOrigin'] = '0 0';
				canvas.style['webkitTransform'] = 'scale(' + Math.max(
					size.width / canvas.width,
					size.height / canvas.height
				) + ')';
			} else {
				canvas.style.width = size.width+"px";
				canvas.style.height = size.height+"px";
			}
		}

		static getBrowser() {
			if (JGUtil.browser)
				return JGUtil.browser;
			//this function arranged by jquery 1.8.3. http://jquery.com/ (jquery 1.9 deleted this source)
			//Note: jQuery 1.9のsupportは煩雑なので、uaを偽装されたらその偽装されたua通りに処理すればいいという方針
			var ua = navigator.userAgent.toLowerCase();

			var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
				/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
				/(msie) ([\w.]+)/.exec( ua ) ||
				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
				[];

			var ret:BrowserInfo = {}
			if ( match[1] ) {
				ret[match[1]] = true;
				ret["version"] = match[2];
			}

			if (ret.chrome)
				ret.webkit = true;
			else if (ret.webkit)
				ret.safari = true;

			JGUtil.browser = ret;

			return ret;
		}

		static setCrispEdges(game:Game, crispEdges:bool) {
			if (crispEdges)
				window["imageSmoothingEnabled"] = crispEdges;
			else
				delete window["imageSmoothingEnabled"];
			game.refresh();
		}

		static proxy(func:Function, self:any) {
			return function() {
				func.apply(self, arguments);
			}
		}
	}
}
