package 
{
	import flash.display.*;
	import flash.geom.*;
	public class Utils
	{
		public static function GetChildren(clip:MovieClip):Array
		{
			var ret:Array = [];
			for (var i = 0; i < clip.numChildren; i++)
			{
				ret.push(clip.getChildAt(i));
			}
			return ret;
		}
		
		/*
		 * angle difference
		 *  (range -Math.PI to Math.PI)
		 */
		public static function GetAngleDiff(a, b)
		{
			var angleDiff = b - a;
			
			while (angleDiff > Math.PI)
			{
				angleDiff -= Math.PI * 2;
			}
			while (angleDiff < -Math.PI)
			{
				angleDiff += Math.PI * 2;
			}
			return angleDiff;
		}
		
		/*
		 * angle difference
		 *  (range 0 to Math.PI)
		 */
		public static function GetAngleDiffAbs(a, b)
		{
			var angleDiff = GetAngleDiff(a, b);
			while (angleDiff < 0)
			{
				angleDiff += Math.PI * 2;
			}
			
			if (angleDiff > Math.PI)
			{
				angleDiff = Math.PI * 2 - angleDiff;
			}
			
			return angleDiff;
		}
		
		public static function GetTransform(from:Matrix, to:Matrix)
		{
			var i:Matrix = from.clone();
			i.invert();
			var m:Matrix = to.clone();
			m.concat(i);
			return m;
		}
		
		public static function Decompose(m:Matrix)
		{
			var s:Point = ScaleFromMat(m);
			m = m.clone();
			
			var sxUnit = s.x >= 0 ? 1 : -1;
			var syUnit = s.y >= 0 ? 1 : -1;
			
			m.scale(sxUnit, syUnit);
			
			//m.a /= sxUnit;
			//m.d /= syUnit;
			var rot = RotFromMat(m);
			return {sx:s.x, sy:s.y, rot:rot};
		}
		
		public static function RotFromMat(m:Matrix)
		{
			return Math.atan2(-m.c, m.a);
		}
		
		public static function ScaleFromMat(m:Matrix):Point
		{
			var xAxisX = m.a;
			var xAxisY = m.c;
			
			var yAxisX = m.b;
			var yAxisY = m.d;
			
			var sx = Math.sqrt(xAxisX * xAxisX + xAxisY * xAxisY);
			if (m.a < 0) sx *= -1;
			
			var sy = Math.sqrt(yAxisX * yAxisX + yAxisY * yAxisY);
			if (m.d < 0) sy *= -1;
			
			return new Point(sx, sy);
		}
		
		public static function TransformRect(r:Rectangle, m:Matrix)
		{
			var p1:Point = new Point(r.left, r.top);
			var p2:Point = new Point(r.right, r.top);
			var p3:Point = new Point(r.left, r.bottom);
			var p4:Point = new Point(r.right, r.bottom);
			
			p1 = m.transformPoint(p1);
			p2 = m.transformPoint(p2);
			p3 = m.transformPoint(p3);
			p4 = m.transformPoint(p4);
			
			r.left = Math.min(p1.x, p2.x, p3.x, p4.x);
			r.top = Math.min(p1.y, p2.y, p3.y, p4.y);
			r.right = Math.max(p1.x, p2.x, p3.x, p4.x);
			r.bottom = Math.max(p1.y, p2.y, p3.y, p4.y);
		}
		
		public static function RoundRect(r:Rectangle)
		{
			r.left = Math.floor(r.left);
			r.top = Math.floor(r.top);
			r.right = Math.ceil(r.right);
			r.bottom = Math.ceil(r.bottom);
		}
		
		public static function ScaleRect(r:Rectangle, s)
		{
			r.left *= s;
			r.top *= s;
			r.right *= s;
			r.bottom *= s;
		}
		
		public static function RecursivelyStop(clip)
		{
			if (clip is MovieClip)
			{
				clip.stop();
			}
			if (clip is DisplayObjectContainer)
			{
				for (var i = 0; i < clip.numChildren; i++)
				{				
					RecursivelyStop(clip.getChildAt(i));
				}
			}
		}
		
		public static function WeirdGotoFrame(clip:MovieClip, frame)
		{
			var dir = clip.currentFrame > frame ? -1 : 1;
			while (clip.currentFrame != frame)
			{
				RecurseDir(clip, dir);
			}
		}
		
		protected static function RecurseDir(clip:MovieClip, dir)
		{
			for (var i = 0; i < clip.numChildren; i++)
			{
				var child = clip.getChildAt(i);
				if (child is MovieClip)
				{
					RecurseDir(child, dir);
				}
			}
			if (dir == 1)
			{
				clip.nextFrame();
			} else {
				clip.prevFrame();
			}
		}
		
		protected static var tempSpace:BitmapData = null;
		protected static var tempSpaceRect:Rectangle = new Rectangle();
		protected static var tempSpaceMatrix:Matrix = new Matrix();
		protected static var tempSpaceZero:Point = new Point();
		protected static var tempSpaceColorTransform:ColorTransform = new ColorTransform(0,0,0,0,0,0,0,255);
		protected static var lastDrawOffset:Point = new Point();
		public static function GetAccurateBounds(clip:DisplayObject, useMatrix:Matrix = null):Rectangle
		{
			if (!tempSpace) tempSpace = new BitmapData(2048, 2048, true, 0x0);
			
			var oldPad = 0;
			var padAmount = 5;
			
			var ret:Rectangle = new Rectangle();
			
			var baseBounds:Rectangle = clip.getBounds(clip);
			
			var boundsClip:DisplayObject = null;
			if (clip is DisplayObjectContainer)
			{
				boundsClip = (clip as DisplayObjectContainer).getChildByName("__bounds__");				
			}
			
			if (boundsClip != null)
			{
				baseBounds = boundsClip.getBounds(clip);
			}
			
			if (useMatrix)
			{
				TransformAABBRect(baseBounds, useMatrix);
			}
			
			if (boundsClip != null)
			{
				return baseBounds;
			}
			
			var minW = Math.pow(2, Math.ceil(Math.log(baseBounds.width + padAmount * 2) / Math.log(2)));
			var minH = Math.pow(2, Math.ceil(Math.log(baseBounds.height + padAmount * 2) / Math.log(2)));
			if (tempSpace == null)
			{
				tempSpace = new BitmapData(minW, minH, true, 0x0);
			}
			if (tempSpace.width < minW || tempSpace.height < minH)
			{
				tempSpace = new BitmapData(Math.max(tempSpace.width, minW), Math.max(tempSpace.height, minH), true, 0x0);
			}
			
			var r:Rectangle = new Rectangle();
			
			baseBounds.left = Math.floor(baseBounds.left);
			baseBounds.right = Math.ceil(baseBounds.right);
				
			baseBounds.top = Math.floor(baseBounds.top);
			baseBounds.bottom = Math.ceil(baseBounds.bottom);
			
			var i;
			
			var needsChecking = true;
			while (needsChecking)
			{
				needsChecking = false;
				r.left = baseBounds.left - padAmount;
				r.right = baseBounds.right + padAmount;
				
				r.top = baseBounds.top - padAmount;
				r.bottom = baseBounds.bottom + padAmount;
				
				ret = r.clone();
				
				tempSpaceRect.x = tempSpaceRect.y = 0;
				tempSpaceRect.width = tempSpace.width;
				tempSpaceRect.height = tempSpace.height;
				tempSpace.fillRect(tempSpaceRect, 0x0);
				
				tempSpaceMatrix.identity();
				if (useMatrix)
				{
					tempSpaceMatrix.a = useMatrix.a;
					tempSpaceMatrix.b = useMatrix.b;
					tempSpaceMatrix.c = useMatrix.c;
					tempSpaceMatrix.d = useMatrix.d;
					tempSpaceMatrix.tx = useMatrix.tx;
					tempSpaceMatrix.ty = useMatrix.ty;
				}
				tempSpaceMatrix.translate(-r.left, -r.top);
				
				lastDrawOffset.x = -r.left;
				lastDrawOffset.y = -r.top;
				tempSpace.draw(clip, tempSpaceMatrix);
				
				tempSpaceRect.width = 1;
				tempSpaceRect.height = r.height;
				
				var sideMovedIn = 0;
				for (i = 0; i < r.width; i++)
				{
					tempSpaceRect.x = i;
					if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect))
					{
						break;
					} else {
						ret.left++;
						sideMovedIn++;
					}
				}
				if (ret.left < baseBounds.left - oldPad - 1)
				{
					oldPad = padAmount;
					padAmount += 20;
					needsChecking = true;
					continue;
				}
				
				for (i = r.width - 1; i >= sideMovedIn; i--)
				{
					tempSpaceRect.x = i;
					if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect))
					{
						break;
					} else {
						ret.right--;
					}
				}
				if (ret.right > baseBounds.right + oldPad + 1 && sideMovedIn <= baseBounds.right + oldPad + 1)
				{
					oldPad = padAmount;
					padAmount += 20;
					needsChecking = true;
					continue;
				}
				
				sideMovedIn = 0;
				tempSpaceRect.width = r.width;
				tempSpaceRect.height = 1;
				tempSpaceRect.x = 0;
				for (i = 0; i < r.height; i++)
				{
					tempSpaceRect.y = i;
					if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect))
					{
						break;
					} else {
						ret.top++;
						sideMovedIn++;
					}
				}
				if (ret.top < baseBounds.top - oldPad - 1)
				{
					oldPad = padAmount;
					padAmount += 20;
					needsChecking = true;
					continue;
				}
				
				for (i = r.height - 1; i >= sideMovedIn; i--)
				{
					tempSpaceRect.y = i;
					if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect))
					{
						break;
					} else {
						ret.bottom--;
					}
				}
				if (ret.bottom > baseBounds.bottom + oldPad + 1 && sideMovedIn <= baseBounds.bottom + oldPad + 1)
				{
					oldPad = padAmount;
					padAmount += 20;
					needsChecking = true;
					continue;
				}
			}
			return ret;
		}
		
		public static function TransformAABBRect(r:Rectangle, m:Matrix)
		{
			var o1X = r.left;
			var o1Y = r.top;
			
			var o2X = r.right;
			var o2Y = r.top;
			
			var o3X = r.left;
			var o3Y = r.bottom;
			
			var o4X = r.right;
			var o4Y = r.bottom;
			
			var n1X = o1X * m.a + o1Y * m.c + m.tx;
			var n1Y = o1X * m.b + o1Y * m.d + m.ty;
			
			var n2X = o2X * m.a + o2Y * m.c + m.tx;
			var n2Y = o2X * m.b + o2Y * m.d + m.ty;
			
			var n3X = o3X * m.a + o3Y * m.c + m.tx;
			var n3Y = o3X * m.b + o3Y * m.d + m.ty;
			
			var n4X = o4X * m.a + o4Y * m.c + m.tx;
			var n4Y = o4X * m.b + o4Y * m.d + m.ty;
			
			r.left = Math.min(n1X, n2X, n3X, n4X);
			r.right = Math.max(n1X, n2X, n3X, n4X);
			
			r.top = Math.min(n1Y, n2Y, n3Y, n4Y);
			r.bottom = Math.max(n1Y, n2Y, n3Y, n4Y);
		}
	}
}