package com.transmote.utils.ui {
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.system.LoaderContext;
	
	/**
	 * An interface for accessing symbol definitions in the Library
	 * of a SWF embedded in the application.
	 * Modified from Grant Skinner's FlashLib class:
	 * http://www.gskinner.com/blog/archives/2007/03/using_flash_sym.html
	 * 
	 * To init, pass into the constructor a Class instance that refers to a SWF embedded via the Embed meta tag.
	 * Ensure embeddedLibrary is inited before attempting to access symbol classes/instances by handling an
	 * Event.INIT from this instance; failure to wait for init can result in runtime errors.
	 * 
	 * Symbol classes can be accessed via getSymbolDefinition().
	 * Symbol instances can be generated via getSymbolInstance().
	 * 
	 * @author	Eric Socolofsky
	 * @url		http://transmote.com/
	 */
	public class EmbeddedLibrary extends EventDispatcher {
		protected var embeddedLibrary:Class;
		private var loader:Loader;
		
		/**
		 * Constructor.
		 * 
		 * @param	embeddedLibrary		A Class instance that refers to a SWF embedded via the Embed meta tag.
		 */
		public function EmbeddedLibrary (embeddedLibrary:Class) {
			this.embeddedLibrary = embeddedLibrary;
			this.init();
		}
		
		/**
		 * Returns true if the EmbeddedLibrary is fully loaded and initialized,
		 * and the symbols within are prepared for access via Actionscript.
		 */
		public function get inited () :Boolean {
			return (this.loader.content != null);
		}
		
		/**
		 * Generate an instance of the library symbol.
		 * 
		 * @param	className	The symbol's Class name.
		 */
		public function getSymbolInstance (symbolClassName:String) :* {
			var symbolClass:Class = this.getSymbolDefinition(symbolClassName);
			return (symbolClass ? new symbolClass() : null);
		}
		
		/**
		 * Access the library symbol's Class,
		 * which can be used to instantiate symbol instances.
		 * 
		 * @param	className	The symbol's Class name.
		 */
		public function getSymbolDefinition (className:String) :Class {
			try {
				if (this.inited) {
					return (this.loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class);
				} else {
					return null;
				}
			} catch (e:ReferenceError) {
				//provide slightly more informative error
				throw new ReferenceError("No class '"+ className +"' exists in library swf.");
			}
			return null;
		}
		
		/**
		 * Free up the EmbeddedLibrary for garbage collection.
		 */
		public function dispose () :void {
			this.embeddedLibrary = null;
			if (this.loader) {
				this.loader.contentLoaderInfo.removeEventListener(Event.INIT, this.onInited);
				this.loader.unload();
			}
		}
		
		/**
		 * @private
		 * Must be inited at least one frame before any resources are available;
		 * be sure to call init() well in advance of any attempt to access library resources.
		 */
		private function init () :void {
			this.loader = new Loader();
			this.loader.contentLoaderInfo.addEventListener(Event.INIT, this.onInited);
			
			// AIR requires LoaderContext.allowLoadBytesCodeExecution;
			// but non-AIR applications do not support it.
			var loaderContext:LoaderContext = new LoaderContext();
			//loaderContext.allowLoadBytesCodeExecution = true;
			this.loader.loadBytes(new this.embeddedLibrary(), loaderContext);
		}
		
		private function onInited (evt:Event) :void {
			this.loader.contentLoaderInfo.removeEventListener(Event.INIT, this.onInited);
			this.dispatchEvent(new Event(Event.INIT));
		} 
	}
}