package com.xitri.managers{
	import flash.display.Sprite;
	import flash.events.Event;
	
	/**
	 * @author Romanko Denis (Stormit) http://xitri.com
	 * @version 3.0
	 */
	
	public class TaskManager extends Sprite{
		static public const ALL_TASKS_DONE:String = "allTasksDone";
		
		protected var tasks:Array;
		protected var pauseCount:Number = 0;
		protected var isStarted:Boolean = false;
		protected var isPaused:Boolean = false;
		protected var result:Boolean;
		public var cycle:Boolean;
		
		/**
		 * Constructor
		 * @param cycle Boolean value indicating whether or not use cycle mode.
		 * 
		 */		
		public function TaskManager(cycle:Boolean = false) {
			this.cycle = cycle;
			tasks = [];
		}
		
		/**
		 * Add task to the end of queue. This function will called by ENTER_FRAME event yet will not return true. 
		 * @param func Function that will called
		 * @param params Array of parameters that will passed to function
		 * @param ignoreCycle Boolean value. If true function will call once (it makes sense if cycle=true)
		 * 
		 */		
		public function addTask(func:Function, params:Array = null, ignoreCycle:Boolean = false):void {
			tasks.push({func:func, params:params, ignoreCycle:ignoreCycle});
			start();
		}
		
		/**
		 * Add task to the end of queue. This function will called once. After that the following task will be carried out.
		 * @param func Function that will called
		 * @param params Array of parameters that will passed to function
		 * @param ignoreCycle Boolean value. If true function will call once (it makes sense if cycle=true)
		 * 
		 */
		public function addInstantTask(func:Function, params:Array = null, ignoreCycle:Boolean = false):void {
			tasks.push({func:func, params:params, ignoreCycle:ignoreCycle, isInstant:true});
			start();
		}
		
		/**
		 * Add task and makes it first in queue. This function will called by ENTER_FRAME event yet will not return true. 
		 * @param func Function that will called
		 * @param params Array of parameters that will passed to function
		 * @param ignoreCycle Boolean value. If true function will call once (it makes sense if cycle=true)
		 * 
		 */	
		public function addUrgentTask(func:Function, params:Array = null, ignoreCycle:Boolean = false):void{
			tasks.unshift({func:func, params:params, ignoreCycle:ignoreCycle});
			start();
		}
		
		/**
		 * Add task and makes it first in queue. This function will called once. After that the following task will be carried out.
		 * @param func Function that will called
		 * @param params Array of parameters that will passed to function
		 * @param ignoreCycle Boolean value. If true function will call once (it makes sense if cycle=true)
		 * 
		 */
		public function addUrgentInstantTask(func:Function, params:Array = null, ignoreCycle:Boolean = false):void{
			tasks.unshift({func:func, params:params, ignoreCycle:ignoreCycle, isInstant:true});
			start();
		}
		
		/**
		 * Add pause between tasks 
		 * @param t Quantity of frames which will last a pause
		 * @param ignoreCycle Boolean value. If true function will call once (it makes sense if cycle=true)
		 * 
		 */			
		public function addPause(t:int, ignoreCycle:Boolean = false):void {
			addTask(timerFrame, [t], ignoreCycle);
		}
		
		/**
		 * @private
		 * Function that implements a pause task. Adds to queue when addPause() is caused.
		 * @param t int The internal counter
		 * 
		 */		
		protected function timerFrame(t:int):void {
			pauseCount++;
			if(pauseCount >= t) {
				pauseCount = 0;
				nextTask();
			}
		}
		
		/**
		 * Remove all tasks from queue. In fact stops the TaskManager object. 
		 * 
		 */		
		public function removeAllTasks():void {
			tasks = [];
		}
		
		/**
		 * Passes to the next task. Usually this function is caused automatically when the current task returns result true. But you can cause it compulsorily if that is demanded by the logic of your application.
		 * @param ignoreCycle Boolean value. If true current task will not be added to the end of queue (it makes sense if cycle=true)
		 * 
		 */		
		public function nextTask(ignoreCycle:Boolean = false):void {
			if(cycle && !ignoreCycle) {
				tasks.push(tasks.shift());
			} else {
				tasks.shift();
			}
		}
		
		/**
		 * @private 
		 * Starts TaskManager. It is caused when the new task is added.
		 */		
		protected function start():void {
			if(!isStarted) {
				addEventListener(Event.ENTER_FRAME, step, false, 0, true);
				isStarted = true;
				isPaused = false;
			}
		}
		
		/**
		 * @private 
		 * Stop TaskManager. It is caused when the last task return true.
		 */	
		protected function stop():void {
			removeEventListener(Event.ENTER_FRAME, step);
			isStarted = false;
		}
		
		/**
		 * @private 
		 * It is main function that caused by ENTER_FRAME and calls current task. 
		 * When all tasks are done dispatch an ALL_TASK_DONE event
		 * @param e Event object
		 * 
		 */		
		protected function step(e:Event):void {
			var curTask:Object = tasks[0] as Object;
			if(curTask) {
				result = (curTask.func as Function).apply(this, curTask.params);
				if(curTask == tasks[0] && (curTask.isInstant || result)) {
					nextTask(curTask.ignoreCycle);
				}
			} else {
				stop();
				dispatchEvent(new Event(ALL_TASKS_DONE));
			}
		}
		
		/**
		 * Set on/off pause.
		 * Contains boolean value
		 * 
		 */		
		public function get pause():Boolean {
			return isPaused;
		}
			
		public function set pause(value:Boolean):void {
			if(value && !isPaused) {
				isPaused = true;
				removeEventListener(Event.ENTER_FRAME, step);
			} else if(tasks[0] && isPaused) {
				isPaused = false;
				addEventListener(Event.ENTER_FRAME, step, false, 0, true);
			}
		}
		
		/**
		 * Read only.
		 * Contains quantity of tasks in queue.
		 * 
		 */		
		public function get tasksNum():int {
			return tasks.length;
		}
	}
}