/*
VERSION:		2.94
	2.94	shortened the  detectLastFrame_delay  to 10 ms
	2.93	replacing an existing movieClip with various different files is more reliable than ever before.
	2.91	getBitmap() now returns a snapshot for SWF files  (just for the sake of consistent behavior,   even if it rarely makes sense to ever do this)
	2.9		removeClip() now always changes the old movieClip's depth to an unused layer to garantee that new movieClips can successfully replace old movieClips  (this later turns out to be unecessary)
	2.8		detectLastFrame() now loops every 20ms (approx) instead of 33ms to help ensure it detects the last frame before a SWF is removed  (the timing of setInterval isn't always precise)
	2.7		All placement is delayed by 0ms to allow time for callbacks to be defined by external code
	2.6		Added waitForUnload()
				Added onUnload() externally-defined callback to the return object
				Added onLoad() externally-defined callback to the return object
				Changed onLoadError() to remove the temporary empty movieClip container
				Changed the unload events to never fire if the file was not successfully loaded beforehand
				Changed content to have no value upon load-error
				Changed all success events to always fire regardless of what kind of file is loaded
	2.5		Added waitForInit() chain-able monad function,  which triggers 0ms after the file is loaded, ensuring its frame-one code has already initialized.
	2.4		Changed addBitmap() to refuse to export an image if a .swf file was loaded
	2.3		Added file to all event evt's
	2.2		added getBitmap() to the output object  +  Returned object emits real events
	2.1		added onAnimDone() callback function into returned loader object
	2.0		detects successful linkage even if movieClip is empty
	1.9		waitForLoad() & waitForAnim()	chain-able monads.  +  Reliable & instant movieClip replacement with onUnload() call.
	1.8		used "function" prefix to allow local scoping
	1.7		output "loader" includes a reference to the new movieClip as a variable named "content"
	1.6		onLoadInit() passes a reference to the loaded movieClip
	1.5		added "isVisible" flag to set the default visibility of the loaded image  (doesn't apply when result is BitmapData)


EVENTS		(emitted by return_obj)
	onLoad							runs after the file has loaded, but before its frame 1 code has run  &  before any of its functions exist  /  after the image has loaded
	onLoadInit					runs after the file has run its frame 1 code  /  after the image has loaded
	complete						exactly the same as onloadInit
	onAnimDone					runs the first time the loaded file reaches its last animation frame
	onLoadError					runs if the file cannot be loaded
	unload							runs when the file has been removed
	
	
RETURN OBJECT:
	{
		onLoad()					Externally-defined callback function, which runs when the file has been loaded, but before any of its code has run  &  before any of its functions exist
		onLoadInit()			Externally-defined callback function, which runs after the file has run its frame 1 code  (regardless of whether it can contain code)
		onAnimDone()			Externally-defined callback function, which runs the first time the loaded file reaches its last animation frame
		onLoadError()			Externally-defined callback function, which runs if the file cannot be loaded
		content						Reference to the internally created/loaded movieClip, which always contains an animation or a displayed bitmap
		waitForLoad()			Adds functions to a queue which fires when the file has been loaded, but before any of its code has run  &  before any of its functions exist
		waitForInit()			Adds functions to a queue which fires after the file has run its frame 1 code
		waitForAnim()			Adds functions to a queue which fires the first time the loaded file reaches its last animation frame
		waitForUnload()		Adds functions to a queue which fires when the file is removed
	}
	
	
NOTE:
	All events and callbacks will always run, no matter what kind of file/linkage is loaded... unless there's an error.
	If there's a load error, then only the error event & callback will fire,  and the "unload" event & callback will not run when it is removed
	

DESCRIPTION:
	Creates a movieClip as specified & loads an image, SWF, or bitmapData into it.
	The returned "loader" has chain-able functions:  waitForLoad  &  waitForAnim  (they're not promises, but they behave sort of like that)
	
	
DEFAULT RESULT:
	this.image_0
	
	
USAGE:
	#include "loadImage.as"
	loader = loadImage("test.jpg");
	
	loader
	.waitForLoad( passData )
	.waitForLoad( success )
	.waitForInit( revealThis )
	.waitForAnim( onAnimDone )
	.waitForLoad( success2 );
	.waitForUnload( whenRemoved )
	
	function success( mc, getBitmap ){
		trace("success");
	}
	loader.onLoadInit = success;
	loader.onLoadError = function(){
		trace("fail");
	}
	loader.onUnload = function( mc ){
		trace("removing clip: "+mc._name);
	}
	
	OR
	
	react.to("complete").from(loader).then = success;
	
	function onAnimDone( mc ){}
*/
function loadImage(file, target, newName, newDepth, isVisible)
{
	///////////////////////////////////////////////////////////////////////////////////////
	// dependencies
	#include "nextDepth.as"
	
	
	
	// END: dependencies
	///////////////////////////////////////////////////////////////////////////////////////
	// shared variables
	//if( isLoaded )					waitForLoad_trigger();
	//if( isInit )						waitForInit_trigger();
	//if( isDoneAnimating )		waitForAnim_trigger();
	var detectLastFrame_delay = 10;
	var return_obj;
	var isLoaded = false;
	var isInit = false;
	var isDoneAnimating = false;
	var waitForLoad_queue = [];
	var waitForInit_queue = [];
	var waitForAnim_queue = [];
	var waitForUnload_queue = [];
	var loadedBitmap = null;		// direct bitmap / cached bitmap snapshot of loaded content
	var detectTargetUnload = {};
	var detectLastFrame_interval = null
	var cleanupInterval = null;
	
	
	// END: shared variables
	///////////////////////////////////////////////////////////////////////////////////////
	// helper functions
	function once( func ){
		var done = false;
		return function () {
			return done ? void 0 : ((done = true), func.apply(this, arguments));
		}
	}// once()
	
	
	/*	This only sends the announcements that a movieClip has been removed.
			This does not remove any movieClips.
	*/
	function unload(){
		if(cleanupInterval !== null){
			clearInterval( cleanupInterval );
			cleanupInterval = null;
		}
		if( return_obj.content )
		{// if:  a file was successfully loaded before this unload
			// announce that unload has occurred
			waitForUnload_trigger();
			return_obj["onUnload"]( return_obj.content );
			return_obj["broadcastMessage"]("unload");
		}// if:  a file was successfully loaded before this unload
		//
		removeEvents();
	}// unload()
	var unload = once( unload );
	
	
	function makeOnUnload( placeIntoThis ){
		// if( !return_obj.content )				return;		// if file could not be loaded, then don't bother announcing it being unloaded,  because it was never loaded to begin with.
		if( contentExists() === false )		return;		// if there is no content => undefined
		if( placeIntoThis.onUnload )		return;		// do not replace an existing onUnload() function
		placeIntoThis.onUnload = unload;
	}// makeOnUnload()
	
	
	// clean-up events when target unloads  (target emits an "unload" event)
	function removeEvents(){
		// disable unload event
		detectTargetUnload.unload = undefined;		delete detectTargetUnload.unload;
		target.removeEventListener( "unload", detectTargetUnload );
		target.removeListener( detectTargetUnload );
		// remove ALL listeners from return_obj
		for(var L in return_obj["_listeners"]){
			var thisListener = return_obj["_listeners"][L];
			return_obj["removeListener"]( thisListener );
		}// for:  each listener
		// de-initialize AsBroadcaster
		return_obj["_listeners"] = undefined;		delete return_obj["_listeners"];
		return_obj["addListener"] = undefined;		delete return_obj["addListener"];
		return_obj["removeListener"] = undefined;		delete return_obj["removeListener"];
		return_obj["broadcastMessage"] = undefined;		delete return_obj["broadcastMessage"];
		target.removeListener( return_obj );
	}// removeEvents()
	
	
	function getBitmap(){
		// if( !return_obj.content )		return;		// if there is no content => undefined
		if( contentExists() === false )		return;		// if there is no content => undefined
		// if( typeof(file) === "string"  &&  file.substr(-4)===".swf" )		return;			// if a .swf file was loaded, then refuse to output a static bitmap
		
		if(!loadedBitmap){
			// take a snapshot of the loaded content
			var wid = return_obj.content._width;
			var hei = return_obj.content._height;
			// movieClips can contain nothing, so avoid outputting zero-size bitmaps
			if( !wid )		wid = 1;
			if( !hei )		hei = 1;
			loadedBitmap = new flash.display.BitmapData( wid,hei ,true,0);
			loadedBitmap.draw( return_obj.content );
		}// if:  no loadedBitmap exists for this content yet
		return loadedBitmap;
	} // getBitmap()
	
	
	function detectLastFrame( anim_mc ){
		// detect last animation frame
		var detectLastFrame = {
			loop:function(){
				//var anim_mc = target[newName];
				if( anim_mc._currentframe==0  ||  anim_mc._currentframe==undefined )
				{// if:  this clip was deleted
					// stop checking
					clearInterval( detectLastFrame_interval );
					// clear the queue
					waitForAnim_queue.splice(0, waitForAnim_queue.length);
				}// if:  this clip was deleted
				else if(anim_mc._currentframe == anim_mc._totalframes)
				{// if:  this clip is on its last animation frame
					// stop checking
					clearInterval( detectLastFrame_interval );
					// trigger the queue
					isDoneAnimating = true;
					waitForAnim_trigger();
					return_obj.onAnimDone( return_obj.content, return_obj.getBitmap );
					return_obj.broadcastMessage("onAnimDone", return_obj.content, return_obj.getBitmap, return_obj.file );
					delete return_obj.onAnimDone;
					// clear the queue
					waitForAnim_queue.splice(0, waitForAnim_queue.length);
				}// if:  this clip is on its last animation frame
			}// loop()
		}// detectLastFrame obj
		detectLastFrame_interval = setInterval( detectLastFrame.loop, detectLastFrame_delay );		// 33 might not always be fast enough
		// immediately check right now
		detectLastFrame.loop();
	}// detectLastFrame()
	detectLastFrame = once( detectLastFrame );
	
	
	function contentExists(){
		return (return_obj.content._currentframe !== undefined);		// exist => true,  not-exist => false
	}// contentExists()
	
	
	// END: helper functions
	///////////////////////////////////////////////////////////////////////////////////////
	// create + populate the return object
	// ... including its chain-able monad callbacks
	function waitForLoad_trigger(){
		for(var i=0; i<waitForLoad_queue.length; i++){
			//waitForLoad_queue[i]( return_obj.content );
			waitForLoad_queue[i].apply( return_obj.content, [return_obj.content, return_obj.getBitmap, return_obj.file] );
		}// for:  each callback in thie queue
		// empty the queue
		waitForLoad_queue.splice(0, waitForLoad_queue.length);
	}// waitForLoad_trigger()
	
	function waitForInit_trigger(){
		for(var i=0; i<waitForInit_queue.length; i++){
			//waitForInit_queue[i]( return_obj.content );
			waitForInit_queue[i].apply( return_obj.content, [return_obj.content, return_obj.getBitmap, return_obj.file] );
		}// for:  each callback in thie queue
		// empty the queue
		waitForInit_queue.splice(0, waitForInit_queue.length);
	}// waitForInit_trigger()
	
	function waitForAnim_trigger(){
		for(var i=0; i<waitForAnim_queue.length; i++){
			//waitForAnim_queue[i]( return_obj.content );
			waitForAnim_queue[i].apply( return_obj.content, [return_obj.content, return_obj.getBitmap, return_obj.file] );
		}// for:  each callback in thie queue
		// empty the queue
		waitForAnim_queue.splice(0, waitForAnim_queue.length);
	}// waitForAnim_trigger()
	
	function waitForUnload_trigger(){
		for(var i=0; i<waitForUnload_queue.length; i++){
			//waitForUnload_queue[i]( return_obj.content );
			waitForUnload_queue[i].apply( return_obj.content, [return_obj.content, return_obj.getBitmap, return_obj.file] );
		}// for:  each callback in thie queue
		// empty the queue
		waitForUnload_queue.splice(0, waitForUnload_queue.length);
	}// waitForUnload_trigger()
	
	
	
	// monad interface
	return_obj = {
		file:file,
		onLoadInit:function(){},
		onLoadError:function(){},
		onAnimDone:function(){},
		content: undefined,
		waitForLoad: function( newCallback ){
			if(isLoaded){
				newCallback.apply( return_obj.content, [return_obj.content, getBitmap, file] );
			}else{
				waitForLoad_queue.push( newCallback );
			}
			return return_obj;		// return parent object, making this a chain-able monad
		}, // waitForLoad()
		waitForInit: function( newCallback ){
			if(isInit){
				newCallback.apply( return_obj.content, [return_obj.content, getBitmap, file] );
			}else{
				waitForInit_queue.push( newCallback );
			}
			return return_obj;		// return parent object, making this a chain-able monad
		}, // waitForInit()
		waitForAnim: function( newCallback ){
			if( isDoneAnimating ){
				newCallback.apply( return_obj.content, [return_obj.content, getBitmap, file] );
			}else{
				waitForAnim_queue.push( newCallback );
			}
			return return_obj;		// return parent object, making this a chain-able monad
		}, // waitForAnim()
		waitForUnload: function( newCallback ){
			waitForUnload_queue.push( newCallback );
			return return_obj;		// return parent object, making this a chain-able monad
		}, // waitForUnload()
		getBitmap: getBitmap,
		unload: unload,
		removeEvents: removeEvents
	}// return_obj
	
	// Allow return_obj to emit events
	AsBroadcaster.initialize( return_obj );
	
	
	
	// END: create + populate the return object
	///////////////////////////////////////////////////////////////////////////////////////
	// detect unload => clean-up  (A)
	// add the ability to react to "unload" events in general
	detectTargetUnload.unload = unload;
	
	// react to "unload" from the new image's parent
	if(target.addEventListener){
		target.addEventListener( "unload", detectTargetUnload );
	}else if(target.addListener){
		target.addListener( detectTargetUnload );
	}
	
	// periodically check if the target goes missing  (last-resort fail-safe in case onUnload() fails to fire)
	var randStartDelay = Math.floor( Math.random() * 300 );		// this prevents lots of loadImage's from all checking at the same time
	setTimeout(function(){
		cleanupInterval = setInterval(function(){
			var exists = contentExists();
			if(exists)		return;		// content exists => continue checking
			
			// else:  "exists" = false
			
			// Announce that the movieClip no longer exists
			unload();
		}, 3000);
	}, randStartDelay);
	
	
	
	
	// END: detect unload => clean-up
	///////////////////////////////////////////////////////////////////////////////////////
	// definitions are done,
	// Begin actual program
	if(isVisible === undefined)		isVisible = true;
	// resolve optional parameters
	var target = target || this;
	var newDepth = (newDepth!=undefined) ? newDepth : nextDepth(target);
	var newName = newName || "image_"+newDepth;
	newName = String( newName );		// avoid unknown behavior from unexpected data-types
	
	
	
	// Delay placement by 0ms to allow time for callbacks to be defined by external code
	setTimeout( function(){
		// It is critical that this occurs immediately before placeClip  (and not outside of this setTimeout)  Otherwise linkages never work.
		removeClip( target[newName] );
		placeClip();
	}, 0 );		//  0ms delay  (setTimeout)
	
	
	function placeClip(){
		// try movieClip linkage  &  create container
		var newImage = target.attachMovie( file, newName, newDepth, {_visible:isVisible} );
		var newImage = target[newName];
		//if( newImage._width > 0  &&  newImage != target)
		if( target.getInstanceAtDepth(newDepth) !== undefined  &&  newImage != target )
		{// if:  movieClip loaded
			//trace("* MovieClip linkage");
			loadedBitmap = null;
			return_obj.content = newImage;
			isLoaded = true;
			// detect unload => clean-up  (B)
			makeOnUnload( return_obj.content );
			waitForLoad_trigger();
			return_obj.broadcastMessage("onLoad", return_obj.content, return_obj.getBitmap, file );
			return_obj["onLoad"]( return_obj.content, return_obj.getBitmap, file );
			detectLastFrame( return_obj.content );
			// Wait for 1ms to garantee that this consistently occurs after frame-1 code  (The previous setTimeout causes this particular 0ms to run before frame-1 code)
			setTimeout( function( file ){
				waitForInit_trigger();
				return_obj["onLoadInit"]( return_obj.content, return_obj.getBitmap, file );
				return_obj.broadcastMessage("onLoadInit", return_obj.content, return_obj.getBitmap, file );
				return_obj.broadcastMessage("complete", return_obj.content, return_obj.getBitmap, file );
			}, 1, file );
		}// if:  movieClip loaded
		
		
		
		// try bitmapData linkage or reference
		else
		//if(	newImage == target  ||
		//	 	newImage._width == undefined  ||
		//		newImage._width == 0)
		{// if:  no movieClip loaded
			// create container
			var newImage = target.createEmptyMovieClip(newName, newDepth);
			newImage._visible = isVisible;
			// // direct bitmapData
			if(file.generateFilterRect != undefined)
				var image_pic = file;
			// bitmap linkage
			if(file.generateFilterRect == undefined)
				var image_pic = flash.display.BitmapData.loadBitmap( file );
			
			newImage.attachBitmap(image_pic, 0, true, true);
			//if( newImage._width > 0 )
			if( newImage.getInstanceAtDepth(0) !== undefined )
			{// if:  bitmap loaded
				//trace("* Bitmap linkage");
				loadedBitmap = image_pic;
				return_obj.content = newImage;
				isLoaded = true;
				// detect unload => clean-up  (B)
				makeOnUnload( return_obj.content );
				waitForLoad_trigger();
				return_obj.broadcastMessage("onLoad", return_obj.content, return_obj.getBitmap, file );
				return_obj["onLoad"]( return_obj.content, return_obj.getBitmap, file );
				// bitmaps don't contain code, so init is already complete
				
				setTimeout( function( file ){
					waitForInit_trigger();
					return_obj["onLoadInit"]( return_obj.content, return_obj.getBitmap, file );
					return_obj.broadcastMessage("onLoadInit", return_obj.content, return_obj.getBitmap, file );
					return_obj.broadcastMessage("complete", return_obj.content, return_obj.getBitmap, file );
				}, 0, file );
				
				// bitmaps don't animate, so the animation is now done  (last frame is already displayed)
				isDoneAnimating = true;
				waitForAnim_trigger();
				return_obj.onAnimDone( return_obj.content, return_obj.getBitmap, file );
				return_obj.broadcastMessage("onAnimDone", return_obj.content, return_obj.getBitmap, file );
				delete return_obj.onAnimDone;
				/*
				setTimeout( function( file ){
					waitForInit_trigger();
					return_obj["onLoadInit"]( return_obj.content, return_obj.getBitmap, file );
					return_obj.broadcastMessage("onLoadInit", return_obj.content, return_obj.getBitmap, file );
					return_obj.broadcastMessage("complete", return_obj.content, return_obj.getBitmap, file );
				}, 0, file );
				*/
			}// if:  bitmap loaded
			
			
			
			else
			// try loading external file
			
			//if(	newImage._width == undefined  ||
			//newImage._width == 0)
			{// if:  no bitmap loaded
				// external file
				var loader = new MovieClipLoader();
				loadedBitmap = null;
				//return_obj.content = newImage;
				return_obj.content = undefined;
				
				loader.onLoadComplete = function( loadedClip, loadStatus ){
					newImage._visible = isVisible;
					loadedBitmap = null;
					return_obj.content = loadedClip;
					isLoaded = true;
					waitForLoad_trigger();
					// getBitmap()  won't work here because external files always have ZERO size at this point  (they're on frame-0 which doesn't exist in the timeline)
					return_obj.broadcastMessage("onLoad", return_obj.content, null, file );
					return_obj["onLoad"]( return_obj.content, null, file );
					// return_obj.broadcastMessage("onLoad", return_obj.content, return_obj.getBitmap, file );
					// return_obj["onLoad"]( return_obj.content, return_obj.getBitmap, file );
				}// onLoadComplete()
				loader.onLoadInit = function( loadedClip ){
					makeOnUnload( return_obj.content );
					detectLastFrame( return_obj.content );
					setTimeout( function( file ){
						waitForInit_trigger();
						return_obj["onLoadInit"]( return_obj.content, return_obj.getBitmap, file );
						return_obj.broadcastMessage("onLoadInit", return_obj.content, return_obj.getBitmap, file );
						return_obj.broadcastMessage("complete", return_obj.content, return_obj.getBitmap, file );
					}, 0, file );
				}// onLoadInit()
				loader.onLoadInit = once( loader.onLoadInit );
				loader.onLoadError = function(){
					if(newName)		removeClip( target[newName] );
					return_obj.content = undefined;		// This is redundant.  removeMovieClip() erases all references automatically,  but it's specified here to clarify the behavior for code-readibility.
					// clear the current queue because it won't be needed
					waitForLoad_queue.splice(0, waitForLoad_queue.length);
					waitForInit_queue.splice(0, waitForInit_queue.length);
					waitForAnim_queue.splice(0, waitForAnim_queue.length);
					var err_evt = {
						file: file
					}
					return_obj["onLoadError"]( err_evt );
					return_obj.broadcastMessage("onLoadError", err_evt );
				}// onLoadError()
				loader.onLoadError = once( loader.onLoadError );
				loader.loadClip( file, newImage );
				
				if(isLoaded)
				{// if:  instant load
					// pretend that it's not instant
					return_obj.content = target[newName];
					setTimeout( function( file ){
						loader.onLoadInit( return_obj.content, return_obj.getBitmap, file );
					}, 0, file);
				}// if:  instant load			
				//return return_obj;
				//return loader;
			}// if:  no bitmap loaded
		}// if:  no movieClip loaded
	} // placeClip()
	
	
	
	function removeClip( clip_mc ){
		if(!clip_mc)		return;
		
		// garantee clean-up  (because setTimeout normally prevents it)
		clip_mc.onUnload();
		delete clip_mc.onUnload;		// prevent double-calls
		
		// guarantee that loadClip() will work
		clip_mc.removeMovieClip();
	}// removeClip()
	
	
	
	
	return return_obj;
}// loadImage()