package CharacterExport
{
	import flash.display.*;
	import Defs.CharacterDef;
	import djarts.core.*;
	import djarts.display.*;
	import flash.geom.*;
	import flash.utils.*;

	public class CharacterTypeGraphic extends MovieClip
	{
		protected var graphicId;
		protected var characterDef:CharacterDef;
		protected var force;
		protected var sequence;
		
		protected var sequenceLabel;
		
		protected var graphic:MovieClip;
		
		protected var length;
		public override function get totalFrames():int
		{
			return length;
		}
		
		protected var externalAnimationData:ExternalAnimationInfo;
		
		public function CharacterTypeGraphic(graphicId, characterDef:CharacterDef, force, sequence, externalAnimationData:ExternalAnimationInfo)
		{
			this.graphicId = graphicId;
			this.characterDef = characterDef;
			this.force = force;
			this.sequence = sequence;
			this.externalAnimationData = externalAnimationData;
			
			this.graphic = GraphicFactory.Instance().GetGraphicByID(graphicId);
			
			if (externalAnimationData)
			{
				PrepareAnimationPieces();
			}
			
			addChild(graphic);
		}
		
		public function GetGraphicChildren():Array
		{
			var ret:Array = [];
			for (var i = 0; i < graphic.numChildren; i++)
			{
				ret.push(graphic.getChildAt(i));
			}
			return ret;
		}
		
		protected var lastFrameSetPieces:Dictionary = new Dictionary();
		public function IsForcedPiece(clip:DisplayObject)
		{
			return (clip in lastFrameSetPieces);
		}
		
		public function SetFrame(frame)
		{
			
			frame = Math.max(1, Math.min(length, frame));
			if (externalAnimationData)
			{
				CacheManager.Instance().ProcessAnimationData(graphic, sequence, frame, this.externalAnimationData, false, force);
			} else {
				lastFrameSetPieces = new Dictionary();
				makeAllChildrenGoToFrame(graphic, frame, null, graphic, force);
			}
		}
		
		public function PrepareAnimationPieces()
		{
			if (externalAnimationData.UsingSingleSequence)
			{
				for (var i = 0; i < graphic.numChildren; i++)
				{
					graphic.getChildAt(i).visible = false;
				}
				for (var i in externalAnimationData.Data[sequence])
				{
					graphic[externalAnimationData.PieceName(sequence, i)].visible = true;
				}
			} else {
				Utils.RecursivelyStop(graphic, sequence + 1);
			}
			
			sequenceLabel = graphic.currentFrameLabel;
			if (externalAnimationData.UsingSingleSequence) sequenceLabel = externalAnimationData.SeqNames[sequence];
			
			for (var i in externalAnimationData.Data[sequence])
			{
				var subclip = graphic[externalAnimationData.PieceName(sequence, i)];
				if (subclip)
				{
					var clipName = null;
					for (var j = 0; j < externalAnimationData.Data[sequence][i].name.length; j++)
					{
						if (externalAnimationData.Data[sequence][i].name[j] != "")
						{
							clipName = externalAnimationData.Data[sequence][i].name[j];
							break;
						}
					}
					var checked = false;
					if (clipName != null)
					{							
						checked = this.CheckClipName(subclip, clipName, force, graphic, null, sequence + 1, sequenceLabel);
					}
					if (subclip is MovieClip && subclip.visible && !checked)
					{
						this.makeAllChildrenGoToFrame(subclip, sequence + 1, null, graphic, force, sequenceLabel);
					}
				}
			}				
			this.length = externalAnimationData.Data[sequence][0].present.length;
		}
		
		protected function CheckClipName(mc:MovieClip, clipName, force, rootClip, mat:Matrix, frame, rootLabel, keepGoing = true)
		{
			var nextExceptClip = null;
			if (clipName == null) clipName = mc.name;
			if (force != null && force[clipName] && force[clipName].hasOwnProperty("color")) {
				var ct:ColorTransform = mc.transform.colorTransform;
				ct.color = force[clipName].color;
				mc.transform.colorTransform = ct;
			}
			if (force != null && force[clipName] && force[clipName].hasOwnProperty("frame")) {
				
				this.lastFrameSetPieces[mc] = true;
				
				var cForce = force[clipName];
				var cFrame = cForce.frame;
				
				
				var totalTransform:Matrix = mc.transform.matrix.clone();
				if (mat) totalTransform.concat(mat);
				
				if (cFrame is String)
				{
					var l = FindLabel(mc, cFrame);
					if (l == -1)
					{
						var additional = null; //NoFrameCallback(mc, force[clipName],rootLabel);
						
						// def = cForce
						// rootLabel = lowerLabel
						var lowerLabel = rootLabel.toLowerCase();
						try {
							mc.gotoAndStop(cForce.blank);
							Utils.RecursivelyStop(mc, cForce.blank);
						} catch (e:Error)
						{
						}
						
						if (cFrame != "Default")
						{
							var additional = GraphicFactory.Instance().GetGraphic(cFrame);
							if (!GraphicFactory.Instance().LoadedOK(additional))
							{
								// problem! this should have been loaded already...
								additional = null;
								throw new Error("Tried a custom clip that wasn't already loaded!");
							} else {
								additional = FixClip(additional, cForce, lowerLabel);
								
								additional.name = "wrap_clip_no_play_" + additional.currentFrame;
								force["wrap_clip_no_play_" + additional.currentFrame] = {frame:additional.currentFrame, keep_going:false};
							}
						}						
						
						if (additional)
						{
							
							var belowChildren:Array;
							var aboveChildren:Array;
							if (force.search_for_subreplacing && clipName in force.search_for_subreplacing)
							{
								var testField = "__copy_clips_for_" + clipName + "__";
								if (testField in mc)
								{
									belowChildren = mc[testField].below;
									aboveChildren = mc[testField].above;
								} else {
									for (var f = 1; f <= mc.totalFrames; f++)
									{
										Utils.RecursivelyStop(mc, f);
										if (mc.currentFrameLabel != cForce.blank)
										{
											for (var i = 0; i < mc.numChildren; i++)
											{
												var child = mc.getChildAt(i);
												if (child is MovieClip)
												{
													var childName = child.name;
													if (child.name in force)
													{
														if (!belowChildren) belowChildren = [];
														belowChildren.push({name:child.name,
																			transform:child.transform,
																			tag:"__only_frame_label_" + force[child.name].blank});
													}
												} else {
													break;
												}
											}
											if (i < mc.numChildren)
											{
												for (var i = mc.numChildren - 1; i >= 0; i--)
												{
													var child = mc.getChildAt(i);
													if (child is MovieClip)
													{
														var childName = child.name;
														if (child.name in force)
														{
															if (!aboveChildren) aboveChildren = [];;
															aboveChildren.push({name:child.name,
																				transform:child.transform,
																				tag:"__only_frame_label_" + force[child.name].blank});
														}
													} else {
														break;
													}
												}
											}
											break;
										}
									}
									mc[testField] = {above:aboveChildren, below:belowChildren};
								}
							}
							
							if (cForce.blank)
							{
								try {
									mc.gotoAndStop(cForce.blank);
								} catch (e:Error) {}
								if (mc.currentFrameLabel == cForce.blank)
								{
									for (var i = 0; i < mc.numChildren; i++)
									{
										var child = mc.getChildAt(i);
										if (child is MovieClip && child.name in cForce)
										{
											// leave it!
										} else {
											mc.removeChildAt(i);
											i--;
										}
									}
								}
							}
							
							var prevChild = mc.getChildByName(additional.name);
							if (prevChild)
							{
								mc.removeChild(prevChild);
							}
							
							if (belowChildren)
							{
								var tmp = additional;
								additional = new MovieClip();
								
								for (var i = 0; i < belowChildren.length; i++)
								{
									var newClip:MovieClip = new MovieClip();
									newClip.name = belowChildren[i].name;
									newClip.transform = belowChildren[i].transform;
									newClip[belowChildren[i].tag] = true;
									additional.addChild(newClip);
								}
								additional.addChild(tmp);
								additional[tmp.name] = tmp;
								nextExceptClip = tmp;
							} else {
								mc[additional.name] = additional;
								nextExceptClip = additional;
							}
							if (aboveChildren)
							{
								for (var i = aboveChildren.length - 1; i >= 0; i--)
								{
									var newClip:MovieClip = new MovieClip();
									newClip.name = aboveChildren[i].name;
									newClip.transform = aboveChildren[i].transform;
									newClip[aboveChildren[i].tag] = true;
									additional.addChild(newClip);
								}
							}
							
							mc.addChild(additional);
							
							if (force[clipName].keep_going)
							{
								makeAllChildrenGoToFrame(additional, frame, totalTransform, rootClip, force, rootLabel, nextExceptClip);
							}
							nextExceptClip = additional;
						} else {
							Utils.RecursivelyStop(mc, 1);
						}
					} else {
						if (l != -1) mc.gotoAndStop(l);
					}

				} else {
					mc.gotoAndStop(force[clipName].frame);

				}

				

				if (force[clipName].keep_going && keepGoing)
				{
					makeAllChildrenGoToFrame(mc, frame, totalTransform, rootClip, force, "", nextExceptClip);
				}
				return true;
			}
			return false;
		}
		
		/* m: clip to set all children to a certain frame
		 * f: frame to set the children to
		 * mat: the current total transform to reach this point in the hierarchy
		 * force: same as above
		*/
		private function makeAllChildrenGoToFrame(m:MovieClip, f:int, mat:Matrix, rootClip, force = null, rootLabel = "", exceptClip = null):void
		{
			
			for (var i:int = 0; i < m.numChildren; i++) {
				var c:* = m.getChildAt(i);
				if (c is MovieClip && c != exceptClip) {
					var mc:MovieClip = c as MovieClip;
					
					// the total transformation applied to this clip from all parent transforms
					var totalTransform:Matrix;
					
					if (!CheckClipName(mc, null, force, rootClip, mat, f, rootLabel))
					{
						// this clip wasn't specified as being on any particular frame
						if (!c.frameAdded) c.frameAdded = f;

						c.gotoAndStop(((f - c.frameAdded) % c.totalFrames) + 1);
						totalTransform = mc.transform.matrix.clone();
						if (mat) totalTransform.concat(mat);
						makeAllChildrenGoToFrame(c, f, totalTransform, rootClip, force, rootLabel);
						
						// we upscale the clip before rendering just in case
						// the clip has been scaled up (we don't want to lose resolution)
						var upscale = 2.0;
						
						if (mc.name.indexOf("SEPARATE") != -1) {
							throw new Error("SEPARATE not supported!");
							/*
							var n = mc.name;
							var localTransform = new Matrix(1.0 / upscale, 0, 0, 1.0 / upscale);
							localTransform.concat(totalTransform);
							if (!cacheSeparate.hasOwnProperty(n)) {
								if (mc.name.indexOf("BYFRAME") != -1)
								{
									cacheSeparate[n] = new CacheByFrame();
									cacheSeparate[n].buildCacheFromClip(mc, -1, null, new Matrix(upscale, 0, 0, upscale));
								} else {
									cacheSeparate[n] = new CachedMovieClip();
									cacheSeparate[n].buildCacheFromClip(mc, -1, true, null, new Matrix(upscale, 0, 0, upscale));
								}
								// render this clip as if it has not been transformed
								
								
								cacheSeparate[n].gotoAndPlay(1);
								positionData[n] = [];
								
								cacheSeparate[n].name = n;
								addChild(cacheSeparate[n]);
								
								
								if (n.indexOf("BACK") != -1) {
									setChildIndex(cacheSeparate[n], 0);
								}
								
								cacheSeparate[n].transform.matrix = localTransform;
								this[n] = cacheSeparate[n];
								this.setPropertyIsEnumerable(n, true);
							}
							positionData[n][currentFrame] = localTransform;
							mc.visible = false;
							*/
						}
					}
				}
			}
		}
		
		protected function FindLabel(mc:MovieClip, name)
		{
			var l:Array = mc.currentLabels;
			for (var i in l)
			{
				if (l[i].name == name) return l[i].frame;
			}
			if (("__only_frame_label_" + name) in mc)
			{
				return 1;
			}
			return -1;
		}
		
		protected function FixClip(mc, def, rootLabel)
		{
			if (rootLabel.indexOf("right") != -1 || rootLabel.indexOf("left") != -1)
			{
				mc.gotoAndStop(1);
			}
			if (rootLabel.indexOf("back") != -1)
			{
				mc.gotoAndStop(2);
			}
			if (rootLabel.indexOf("front") != -1)
			{
				mc.gotoAndStop(3);
			}
			return mc;
		}
		
	}
}