interface BrowserInfo {
	chrome?:bool;
	webkit?:bool;
	safari?:bool;
	opera?:bool;
	msie?:bool;
	mozilla?:bool;
	version?:string;
}
class JGUtil {
	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) {
			if (_p1.x > _p2.x) {
				return Angle.left;
			} else {
				return Angle.right;
			}
		} else {
			if (_p1.y > _p2.y) {
				return Angle.up;
			} else {
				return 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) {
			if (_p1.x > _p2.x) {
				return Keytype.left;
			} else {
				return Keytype.right;
			}
		} else {
			if (_p1.y > _p2.y) {
				return Keytype.up;
			} else {
				return 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 browser:BrowserInfo;

	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();
	}
}