(function(){
	
	var WIN=this,
	EASY=WIN.ev;
/**
 * On s'assure que le namespace EV existe
 */
	if (!EASY) {
		EASY = {};
	}


	/** On ne fait rien si la classe Timeline a deja ete initialisee */
	if (!EASY.Timeline) {
	/**
	 * This is the EV.Timeline constructor.
	 *
	 * Timeline works as a Java thread, and manages to periodicaly invoke a
	 * TimelineLister method. The timeline instance offers the methods to start and
	 * stop the process. Implementation of behaviour is given to the implementation
	 * of TimelineLister.
	 *
	 * Usage:
	 *  var timeline=new Timeline(100);
	 *  function MyTimelineListener() {
	 *    this.throwTimelineEvent=function(timelineEvent){
	 *      // Implement here behaviour of listener
	 *    }
	 *  }
	 *  MyTimelineListener.prototype=new TimelineListener();
	 *  var myTimelineListener=new MyTimelineListener();
	 *  timeline.addTimelineListener(myTimelineListener);
	 *    ...
	 *  // starts timeline
	 *  timeline.start();
	 *    ...
	 *  // stops timeline
	 *  timeline.stop();
	 *
	 *
	 *
	 * @param _delay Delay in ms between each invokation of
	 *   TimelineListener#throwTimelineEvent(TimelineEvent).
	 * @throws If _delay is undefined.
	 * @throws If _delay is null.
	 * @throws If _delay is not a number.
	 * @throws If _delay is negative or null.
	 * @throws If _delay is < 20.
	 */
	EASY.Timeline = function(_delay) {
		if (!WIN.Classe) {throw new Error("Namespace 'Classe' is not defined");}
		if (typeof(_delay) !== 'number') {throw new Error('delay is not a number');}
		if (_delay <= 0) {throw new Error('delay is negative');}
		if (_delay < 20) {throw new Error('delay is too small (minimal value is 20)');}

		/**
		 * This private field refers to the TimelineListener Array used by this instance
		 * to manage their behaviour. Each delay (ms), #throwTimelineEvent(TimelineEvent)
		 * method of each instance of TimelineListener is invoked, providing this timeline
		 * is running.
		 */
		var timelineListenerArray = [];
		/**
		 * This method adds a TimelineListener instance as a listener of this
		 * Timeline.
		 *
		 * @param _timelineListener a TimelineListener that will listen to this
		 * Timeline.
		 * @throws If _timelineListener is undefined.
		 * @throws If _timelineListener is null.
		 * @throws If _timelineListener is not an instance of TimelineListener.
		 */
		this.addTimelineListener = function(_timelineListener) {
			if (!_timelineListener) {throw new Error('timelineListener is not valid');}
			if (!(WIN.Classe.isInstanceOf(_timelineListener, WIN.TimelineListener))) {throw new Error('timelineListener is not a TimelineListener instance');}
			timelineListenerArray.push(_timelineListener);
		};
		/**
		 * This method removes a TimelineListener instance from this
		 * Timeline if it exists. If it doesn't the method does nothing.
		 *
		 * @param _timelineListener a TimelineListener that should be
		 *        listening to this Timeline.
		 * @throws If _timelineListener is undefined.
		 * @throws If _timelineListener is null.
		 * @throws If _timelineListener is not an instance of TimelineListener.
		 */
		this.removeTimelineListener = function(_timelineListener) {
			var i;
			if (!_timelineListener) {throw new Error('timelineListener is not valid');}
			var l = timelineListenerArray.length;
			for (i = 0; i < l; ++i) {
				if (timelineListenerArray[i] === _timelineListener) {
					timelineListenerArray.splice(i, 1);
					break;
				}
			}
		};
		/**
		 * This private flag defines if this Timeline is running (i.e. started).
		 */
		var running = !1;
		/**
		 * Indicates if the timeline is allready running.
		 */
		this.isRunning = function() {
			return running;
		};

		/**
		 * This private property defines this instance reference
		 * that can be used in inner methods.
		 */
		var thisTimeline = this;

		/**
		 * this private field holds the count of
		 * timelineListener#throwTimelineEvent(TimelineEvent) invokations since last
		 * #start(), this count is given in TimelineEvent instances.
		 */
		var executionCount = 0;

		/**
		 * This private method executes itself recursively while this instance is running.
		 * Mainly it invokes the #throwTimelineEvent(TimelineEvent) method of each
		 * TimelineListener of this instance.
		 *
		 * @throws If an error occured while invoking #throwTimelineEvent(TimelineEvent)
		 * method on one of the TimelineListener Array
		 */
		function execute() {
			var eventType = EASY.TimelineEventType.RUNNING,i;
			if (!running) {
				// Si l'état n'est plus 'running', l'evenement est STOP
				eventType = EASY.TimelineEventType.STOP;
			}
			else if (!executionCount) {
				// Si le nombre d'execution est nul, l'evenement est START
				eventType = EASY.TimelineEventType.START;
			}
			var eventTmp = new WIN.TimelineEvent(executionCount, eventType, thisTimeline);
			var l = timelineListenerArray.length;
			function fireEvent(_timelineListener, _event) {
				if (!_timelineListener) { return; }
				try {
					_timelineListener.throwTimelineEvent(_event);
				}
				catch (e) {
					WIN.setTimeout(function() { throw e; }, 0);
				}
			}
			for (i = 0; i < l; ++i) {
				fireEvent(timelineListenerArray[i], eventTmp);
			}
			if (eventType !== EASY.TimelineEventType.STOP) {
				WIN.setTimeout(function() { execute(); }, _delay);
				++executionCount;
			}
		}

		/**
		 * Starts this Timeline, i.e. this Timeline becomes running.
		 * @throws If thi Timeline is allready running.
		 */
		this.start = function() {
			if (running) {throw new Error('timeline allready running');}
			running = !0;
			executionCount = 0;
			execute();
		};
		/**
		 * Stops this Timeline. It becomes not running. If this Timeline is allready
		 * not running, the method does nothing.
		 */
		this.stop = function() {
			// WARNING: This will stop this Timeline only during next delay step.
			running = !1;
		};

		/**
		 * overrides #toString()
		 */
		this.toString = function() {
			return 'Timeline{delay=' + _delay + ', ' + (running ? 'running' : 'stopped') + '}';
		};
	};

	/**
	 * Singleton declared to hold timeline's events type enumeration, i.e.
	 * START, RUNNING, and STOP.
	 */
	EASY.TimelineEventType = {
		START: 0,
		RUNNING: 1,
		STOP: 2,
		toString: function(value) {
			switch (value) {
				case this.START: return 'START';
				case this.RUNNING: return 'RUNNING';
				case this.STOP: return 'STOP';
				default: return 'n.c';
			}
		}
	};

	/**
	 * Abstract class designed to control a Timeline, this controls is a listener
	 * whom #throwTimelineEvent(TimelineEvent) abstract method is invoked periodically by
	 * the Timeline instance it controls. All TimelineListener subclass must
	 * implement this method.
	 */
	WIN.TimelineListener = function() {
		/**
		 * The method periodically invoked by the controled Timeline, it defines the
		 * Timeline's behaviour.
		 * @param timlineEvent Contains a TimelineEvent instance produced by the
		 *   controled Timeline.
		 * @throws If the method has not been implemented.
		 */
		this.throwTimelineEvent = function(timelineEvent) {
			throw new Error('#throwTimelineEvent() must be overridden');
		};
	};

	/**
	 * Defines the event produced by a Timeline instance when it invokes
	 * TimelineListener#throwTimelineEvent(TimelineEvent).
	 * @param _executionCount Step number from Timeline's instance last #start()
	 *   invokation.
	 * @param _type Instance of TimelineEventType holding this TimelineEvent's type.
	 * @param _source The Timeline instance who produced this event.
	 */
	WIN.TimelineEvent = function(_executionCount,_type,_source) {
		if (_executionCount === undefined || _executionCount === null) {throw new Error('executionCount is not valid');}
		if (typeof(_type) !== 'number') {throw new Error('window.TimelineEvent#<init>: type is not a number ['+_type+']');}
		if (_type !== EASY.TimelineEventType.START && _type !== EASY.TimelineEventType.RUNNING && _type !== EASY.TimelineEventType.STOP) {throw new Error('type is not START, RUNNING, or STOP');}
		if (!_source) {throw new Error('source is not valid');}
		if (!(_source instanceof WIN.Timeline || _source instanceof EASY.Timeline)) {throw new Error('source is not instance of Timeline');}
		var date = new Date();
		this.getType = function() {
			return _type;
		};
		this.toString = function() {
			return 'TimelineEvent{executionCount=' + _executionCount + ', type=' + EASY.TimelineEventType.toString(_type) + ', date=' + date + ', source=' + _source + '}';
		};
		this.getDate = function() {
			return date;
		};
		this.getCount = function() {
			return _executionCount;
		};
		this.getSource = function() {
			return _source;
		};
	};

	/**
	 * FIXME : ceci assure seulement la compatibilite
	 */
	WIN.Timeline = EASY.Timeline;

	/**
	 * FIXME : ceci assure seulement la compatibilite
	 */
	WIN.TimelineEventType = function() {
		return EASY.TimelineEventType;
	};
}
	
	
	
	
	
	
	
	
	
}());


