package GraphicExport
{
	import Defs.GraphicDef;
	import flash.display.MovieClip;
	import djarts.utils.*;
	import djarts.display.GraphicFactory;
	import CharacterExport.*;
	import flash.utils.ByteArray;
	import Packer.SpriteSheet;
	import flash.utils.Endian;
	import Packer.GraphicPacker;
	import Packer.AsyncGraphicPacker;
	import flash.net.FileReference;
	import flash.display.Bitmap;
	import com.adobe.serialization.json.JSON;	
	import flash.geom.Matrix;
	
	public class ScenePacker
	{
		protected var doneCallback;
		protected var callbackParam;
		protected var holder:MovieClip;

		protected var sheets:Array = new Array();
		protected var output:ByteArray = new ByteArray();		
		
		public function ScenePacker(callback, callbackParam, holder:MovieClip, outputFormat = "binary")
		{
			this.doneCallback = callback;
			this.callbackParam = callbackParam;
			this.holder = holder;
			this.outputFormat = outputFormat;
			graphicPacker.outputFormat = outputFormat;
		}

		var currentDef:GraphicDef = null;
		public function ExportDef(def:GraphicDef)
		{
			this.currentDef = def;
			graphicPacker.outputAssetName = def.Name+"_graphics";
			var graphic = GraphicFactory.Instance().GetGraphicByID(def.ID, null, SceneGraphicLoaded);
			if (GraphicFactory.Instance().LoadedOK(graphic)) SceneGraphicLoaded(null, graphic);
		}

		
		var clipData:Array;
		protected function SceneGraphicLoaded(c, g:MovieClip)
		{
			
			// read out the clips and transform data for the items on the graphic.
			clipData = new Array();

			//Need to find any drawing objects that are not movie clips and give them thier own clip so they process correctly.
			var gSymbols:MovieClip = new MovieClip();
			var symbolsToMove = new Array();
			for (var i = 0; i < g.numChildren; i++)
			{
				var child = g.getChildAt(i) as MovieClip;
				if (child is MovieClip) symbolsToMove.push(child);
			}

			for (var i = 0; i < symbolsToMove.length; i++) gSymbols.addChild(symbolsToMove[i]);
			
			//if there are still drawing objects that were not movie clips add them as the background.
			if (g.width > 0) gSymbols.addChildAt(g, 0);
			
			//This reorganized clip is now the clip to export.
			g = gSymbols;
			
			//Chunk Large Children... This breaks up clips that are large than 1024 into smaller pieces.
			//This helps with large backgrounds and doesn't require 2048x2048 textures which end up wasting a lot of space.
			for (var i = 0; i < g.numChildren; i++)
			{
				var child = g.getChildAt(i) as MovieClip;
				if (child is MovieClip && (child.width > 1024 || child.height > 1024))
				{
					var chunkedChild = new ChunkedGraphic(child, 500);
					_strace("Chunked child graphic, added "+chunkedChild.numChildren+" chunks!");
					g.removeChildAt(i);
					var chunkID = 0;
					
					var toAdd = chunkedChild.numChildren;
					
					while(chunkedChild.numChildren > 0)
					{
						var chunkedChildChunk = chunkedChild.getChildAt(0);
						chunkedChildChunk.name = child.name +"_chunk"+chunkID++;
						
						var m1:Matrix = chunkedChildChunk.transform.matrix.clone();
						//var m2:Matrix = child.transform.matrix.clone();
						
						m1.concat(child.transform.matrix);
						//m2.concat(chunkedChildChunk.transform.matrix);
						
						chunkedChildChunk.transform.matrix = m1;
						
						/*
						// since the scale information is removed (by exporting the sprite sheet at scale)
						// we only need the corrected x/y components here
						chunkedChildChunk.x = m1.tx;
						chunkedChildChunk.y = m1.ty;
						*/
						
						//chunkedChildChunk.x += chunkedChild.x;
						//chunkedChildChunk.y += chunkedChild.y;
						
						g.addChildAt(chunkedChildChunk, i);
					}
					
					i += Math.max(0, toAdd - 1);
				}
			}

			for (var i = 0; i < g.numChildren; i++)
			{
				if (g.getChildAt(i) is MovieClip)
				{
					var child:MovieClip = g.getChildAt(i) as MovieClip;
					var frameCount = GetLongestSequenceLength(child);

					var type = GraphicDef.EXPORT_FORMAT_SPRITESHEET;
					if (frameCount > 1)
					{
						type = GraphicDef.EXPORT_FORMAT_SKELANIM;
					}
					
					var name = child.name;
					
					var transformData = CharacterExporter.Decompose(child.transform.matrix);
					//var childData = {"clip":child, "index":i, "type":type, "name":name, "x":child.x, "y": child.y, "scaleX":transformData.sx,  "scaleY":transformData.sy, "rotation":transformData.rot};
					var childData = {"clip":child, "index":i, "type":type, "name":name, "x":child.x, "y": child.y, "scaleX":1,  "scaleY":1, "rotation":transformData.rot};
					clipData.push(childData);
					
					var str = "";
					for(var key in childData)
					{
						str += key + " => " + childData[key] + " ";
					}
					_strace(str);
				}
			}
			
			ExportNext();
		}

		var clipIndex:int = 0;
		protected function ExportNext()
		{
			if (clipIndex < clipData.length)
			{
				var data = clipData[clipIndex++];
				switch(data.type)
				{
					case GraphicDef.EXPORT_FORMAT_SKELANIM : AddSkeletalClip(data.clip, Math.max(data.scaleX, data.scaleY) ); break;
					case GraphicDef.EXPORT_FORMAT_SPRITESHEET : AddGraphicClip(data.clip); break;
				}
				
			} else {
				ProcessGraphicExporter();
			}
		}

		protected function ProcessGraphicExporter()
		{
			//actually does the writing to sprite sheets and creating the output bytes.
			graphicPacker.ExportClips(graphicExporterClipList, ProcessGraphicExporterDone);
			this.holder.AddClip(new Bitmap(graphicPacker.GetSheets()[0].GetBitmap() ) );
		}
		
		protected function ProcessGraphicExporterDone(output)
		{
			Done();
		}
		
		protected var skelExporters:Array = new Array();
		var library:PieceLibrary = new PieceLibrary();
		protected function AddSkeletalClip(clip:MovieClip, upscale:Number)
		{
			var characterExporterPreviewClip = new MovieClip();
			this.holder.AddClip(characterExporterPreviewClip);
			
			var exporter:CharacterExporter = new CharacterExporter(characterExporterPreviewClip);
			skelExporters.push(exporter);
			exporter.Upscale = upscale;
			exporter.GraphicName = clip.name;
			exporter.ProcessFromClip(clip, ExportNext, library);
		}

		var graphicExporterClipList:Array = new Array();
		protected var graphicPacker:AsyncGraphicPacker = new AsyncGraphicPacker();
		protected function AddGraphicClip(clip:MovieClip)
		{
			graphicExporterClipList.push(clip);
			ExportNext();
		}
		
		protected function GetLongestSequenceLength(clip:MovieClip, count:int = 1):int
		{
			if (clip.totalFrames > count) count = clip.totalFrames;
			for (var i = 0; i < clip.numChildren; i++)
			{
				if (clip.getChildAt(i) is MovieClip)
				{
					count = GetLongestSequenceLength(clip.getChildAt(i) as MovieClip, count);
				}
			}
			return count;
		}
		
		var outputFormat = "binary";
		
		protected function Done()
		{
			this.library.EnsureAllUsedPiecesHaveNames();
			
			var exporter:ExportBuilder = new ExportBuilder(skelExporters, -1, -1, library);

			if (outputFormat == "json_png_set")
			{
				var fileExportSet = new FileExportSet();
				
				var skelPNGSet = exporter.GetJSONPNGSet();
				var graphicPNGSet = graphicPacker.GetJSONPNGSet();

				skelPNGSet.AssetName = this.currentDef.Name+"_skel";
				graphicPNGSet.AssetName = this.currentDef.Name+"_graphics";				

				//graphicPNGSet.JSONData.meta.image = this.currentDef.Name+"_graphics.png";
				
				var jsonOutput = {};
				jsonOutput.name = currentDef.Name;
				jsonOutput.format = "scene";
				if (skelPNGSet.PNGSheets.length > 0) jsonOutput.skeletal_asset = this.currentDef.Name + "_skel.json";
				jsonOutput.graphic_asset = this.currentDef.Name + "_graphics.json";
				
				jsonOutput.clips = [];
				for(var i = 0; i < clipData.length; i++)
				{
					var jsonClipData = {};
					
					jsonClipData.type = clipData[i].type;
					jsonClipData.name = clipData[i].name;
					
					jsonClipData.x = clipData[i].x;
					jsonClipData.y = clipData[i].y;
					jsonClipData.sx = clipData[i].scaleX;
					jsonClipData.sy = clipData[i].scaleY;
					jsonClipData.rot = clipData[i].rotation;

					jsonOutput.clips.push(jsonClipData);
				}
				
				var jsonDataOutput = com.adobe.serialization.json.JSON.encode(jsonOutput);
				fileExportSet.Add(currentDef.Name+".json", jsonDataOutput);
				fileExportSet.AddJSONPNGSet(graphicPNGSet);
				if (skelPNGSet.PNGSheets.length > 0) fileExportSet.AddJSONPNGSet(skelPNGSet);
				
				if (doneCallback) doneCallback(fileExportSet, callbackParam);
				
			} else {
			
				var skelData:ByteArray = exporter.Output;
				var graphicData:ByteArray = graphicPacker.GetOutput();
				
				var output:ByteArray = new ByteArray();
				output.endian = Endian.LITTLE_ENDIAN;

				output.writeUnsignedInt(currentDef.Name.length);
				output.writeMultiByte(currentDef.Name, "us-ascii");
				
				output.writeUnsignedInt(clipData.length);
				for(var i = 0; i < clipData.length; i++)
				{
					output.writeUnsignedInt(clipData[i].type);
					output.writeUnsignedInt(clipData[i].name.length);
					output.writeMultiByte(clipData[i].name, "us-ascii");
					
					output.writeInt(clipData[i].x);
					output.writeInt(clipData[i].y);
					output.writeDouble(clipData[i].scaleX);
					output.writeDouble(clipData[i].scaleY);
					output.writeDouble(clipData[i].rotation);
				}
				
				output.writeUnsignedInt(skelData.length);
				output.writeBytes(skelData);
				
				output.writeUnsignedInt(graphicData.length);
				output.writeBytes(graphicData);
				
				if (doneCallback) doneCallback(output, callbackParam);
			}
			
			//Clear the Preview
			//while(this.holder.numChildren > 0) holder.removeChildAt(0);
			
			
			
		}
	}
}