package classes.matuto.loaders
{
	import br.com.ninja.debug.NinjaDebug;
	import br.com.ninja.event.NinjaLoaderEvent;
	
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;

	public class MtLoader extends EventDispatcher
	{
		
		public static var DISPLAY 	:uint = 0;
		public static var DATA 		:uint = 1;
		
		private var loaders_arr		:Array;
		private var loaders_dic		:Dictionary;
		
		private var isRunning			:Boolean = false;
		private var isCalculated		:Boolean = false;
		private var calculateTotal		:Boolean;
		private var total_calculated 	:uint = 0;
		private var bytesTotal			:uint = 0;
		private var bytesLoaded			:uint = 0;
		private var index				:uint = 0;
		private var totalLoaded			:uint = 0;
		private var currentLoader		:*;
		
		
		public function MtLoader()
		{
			loaders_arr = new Array();
			loaders_dic = new Dictionary(true);			
		}
		
		/**
		 * 
		 * */
		public function add(id :String, url :*, type :uint = 0, context :* = null) :EventDispatcher
		{			
			var loader 	:*;
			var info	:*;
			
			var _url 		:String;
			var _request	:URLRequest;
			
			if(url is String) {
				_url	 = url;
				_request = new URLRequest(_url);
			}else{
				_request 	= url;
				_url 		= url.url;
			} 
			
			if( type==DISPLAY ){
				
				loader = new Loader();
				
				info = loader.contentLoaderInfo;
				
				info.addEventListener( Event.INIT, init_handle );
				
			}else if( type==DATA ){
				
				loader = new URLLoader();
				
				info = loader;
				
				info.addEventListener( SecurityErrorEvent.SECURITY_ERROR, securityerror_handle );
				
			}
			
			
			loaders_dic[info] = { id:id, request:_request, context:context };
			
			/*
			if(loader is Loader) loader.load(_request, context);
			else loader.load(_request);
			*/
			
			loaders_arr.push(info);
			
			return loader;
			
		}
		
		
		private function addListeners(eventDispatcher :EventDispatcher) :void
		{
			//if(!eventDispatcher.hasEventListener(Event.OPEN))
			//{
				eventDispatcher.addEventListener( Event.OPEN, open_handle );
				eventDispatcher.addEventListener( Event.COMPLETE, complete_handle );
				eventDispatcher.addEventListener( ProgressEvent.PROGRESS, progress_handle );
				eventDispatcher.addEventListener( HTTPStatusEvent.HTTP_STATUS, httpstatus_handle );
				eventDispatcher.addEventListener( IOErrorEvent.IO_ERROR, ioerro_handle );
			//}
		}
		
		private function removeListeners(eventDispatcher :EventDispatcher) :void
		{
			
			var loader :*;
				
			if(eventDispatcher is LoaderInfo) {
				try{
					loader = Object(eventDispatcher).loader;
				}catch(e :Error){
					
				}
			}else loader = eventDispatcher;
			
			try{
				loader.close();
			}catch(e :Error){
				
			}
			
			eventDispatcher.removeEventListener( Event.OPEN, open_handle );
			eventDispatcher.removeEventListener( Event.COMPLETE, complete_handle );
			eventDispatcher.removeEventListener( ProgressEvent.PROGRESS, progress_handle );
			eventDispatcher.removeEventListener( HTTPStatusEvent.HTTP_STATUS, httpstatus_handle );
			eventDispatcher.removeEventListener( IOErrorEvent.IO_ERROR, ioerro_handle );
			
		}
		
		
		private function init_handle(e :Event) :void
		{
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.INIT, getInfo(e.target) ) );
		}
		
		
		private function open_handle(e :Event) :void
		{
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.OPEN, getInfo(e.target) ) );
		}
		
		private function somaBytes(bytes :Number) :void
		{
			bytesTotal += bytes;
			
			total_calculated++;
			
			if(total_calculated>=loaders_arr.length) {
				isCalculated 	= true;
				index 			= 0;
			}
		}
		
		private function progress_handle(e :ProgressEvent) :void
		{
			
			if(!isCalculated && currentLoader!=e.target) {
				
				currentLoader = e.target;
				
				//trace("CALCULANDO: " + loaders_dic[currentLoader].id);
				if(calculateTotal)
				{					
					somaBytes(e.bytesTotal);
					
					removeListeners(currentLoader as EventDispatcher);
					
					loadNext();
				}else{
					bytesLoaded	= 0;
					bytesTotal 	= e.bytesTotal;
				}
				
			}
			
			if(isCalculated || !calculateTotal) dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.PROGRESS, getInfo(e.target) ) );
			
		}
		
		
		private function complete_handle(e :Event) :void
		{
			totalLoaded++;
			
			var info :Object = getInfo(e.target);
			
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.COMPLETE, info) );
			
			if(info.pct==1 && totalLoaded==loaders_arr.length) dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.COMPLETE_ALL, info) );
			
			bytesLoaded += e.target.bytesTotal;
			//NinjaDebug.info("TOTAL BYTES AUTOMATICO: " + bytesTotal);
			loadNext();
		}
		
		
		private function httpstatus_handle(e :HTTPStatusEvent) :void
		{
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.HTTP_STATUS, getInfo(e.target) ) );
		}
		
		
		private function ioerro_handle(e :IOErrorEvent) :void
		{
			if(!hasEventListener(IOErrorEvent.IO_ERROR)) NinjaDebug.error(e);
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.IO_ERROR, getInfo(e.target) ) );
		}
		
		
		private function securityerror_handle(e :SecurityErrorEvent) :void
		{
			if(!hasEventListener(SecurityErrorEvent.SECURITY_ERROR)) NinjaDebug.error(e);
			dispatch( new NinjaLoaderEvent(NinjaLoaderEvent.SECURITY_ERROR, getInfo(e.target) ) );
		}
		
		
		private function getInfo(target :*) :Object
		{
			
			var info :Object = new Object();
			
			info.loader			= target;
			if(loaders_dic[target]) info.id = loaders_dic[target].id;
			info.bytesLoaded 	= Math.min(bytesLoaded + target.bytesLoaded, bytesTotal);
			info.bytesTotal 	= bytesTotal;
			info.pct			= calculateTotal ? info.bytesLoaded/info.bytesTotal : totalLoaded/loaders_arr.length;
			
			return info;
		}
		
		
		private function dispatch(event :NinjaLoaderEvent) :void
		{
			if(isCalculated || !calculateTotal) dispatchEvent(event);
		}
		
		
		public function start(calculateTotal :Boolean = true, totalbytes :Number = NaN) :void
		{
			if(!isRunning) {
				if(!isNaN(totalbytes)) {
					 this.bytesTotal 	= totalbytes;
					total_calculated 	= loaders_arr.length;
					isCalculated 		= true;
					index 				= 0;
				}
				this.calculateTotal = calculateTotal;
				isRunning = true;
				loadNext();
			}
		}
		
		public function stop() :void
		{
			
		}
		
		public function clear() :void
		{
			
			for(var i :int=0; i<loaders_arr.length; i++){
				var loader :* = loaders_arr[i];
				removeListeners(loader);
			}
			
			isRunning		 = false;
			isCalculated	 = false;
			total_calculated = 0;
			bytesTotal		 = 0;
			bytesLoaded		 = 0;
			index			 = 0;
			totalLoaded		 = 0;
			
			loaders_arr = new Array();
			loaders_dic = new Dictionary();
		}
		
		
		public function getContent(id :String) :*
		{
			var content :*;
			
			for(var i :* in loaders_dic){
				if(loaders_dic[i].id == id){
					if(i is LoaderInfo) content = i.content;
					else content = i.data;
				}
			}
			
			return content;
		}
		
		
		private function loadNext() :void
		{
			
			var loader :* = loaders_arr[index];
			
			if(loader)
			{
			
				var info :Object = loaders_dic[loader];
				
				var request :URLRequest = info.request;
				var context	:* 			= info.context;
				
				addListeners(loader);
				
				if(loader is LoaderInfo) {
					try{
						loader.loader.load(request, context);
					}catch(e :Error){
						
					}
				}else loader.load(request);
				
				index++;
				
			}
			
		}
		
	}
}