dojo.provide("dojox.widget.AutoRotator");
dojo.require("dojox.widget.Rotator");

(function(d){

	d.declare("dojox.widget.AutoRotator", dojox.widget.Rotator, {
		//	summary:
		//		A rotator that automatically transitions between child nodes.
		//
		//	description:
		//		Adds automatic rotating to the dojox.widget.Rotator.  The
		//		AutoRotator has parameters that control how user input can
		//		affect the rotator including a suspend when hovering over the
		//		rotator and pausing when the user manually advances to another
		//		pane.
		//
		//	example:
		//	|	<div dojoType="dojox.widget.AutoRotator" duration="3000">
		//	|		<div>
		//	|			Pane 1!
		//	|		</div>
		//	|		<div duration="5000">
		//	|			Pane 2 with an overrided duration!
		//	|		</div>
		//	|	</div>

		//	suspendOnHover: boolean
		//		Pause the rotator when the mouse hovers over it.
		suspendOnHover: false,

		//	duration: int
		//		The time in milliseconds before transitioning to the next pane.  The
		//		default value is 4000 (4 seconds).
		duration: 4000,

		//	autoStart: boolean
		//		Starts the timer to transition children upon creation.
		autoStart: true,

		//	pauseOnManualChange: boolean
		//		Pause the rotator when the pane is changed or a controller's next or
		//		previous buttons are clicked.
		pauseOnManualChange: false,

		//	cycles: int
		//		Number of cycles before pausing.
		cycles: -1,

		//	random: boolean
		//		Determines if the panes should cycle randomly.
		random: false,

		//	reverse: boolean
		//		Causes the rotator to rotate in reverse order.
		reverse: false,

		constructor: function(){
			//	summary:
			//		Initializes the timer and connect to the rotator.

			var _t = this;

			// validate the cycles counter
			if(_t.cycles-0 == _t.cycles && _t.cycles > 0){
				// we need to add 1 because we decrement cycles before the animation starts
				_t.cycles++;
			}else{
				_t.cycles = _t.cycles ? -1 : 0;
			}

			// wire up the mouse hover events
			_t._connects = [
				d.connect(_t._domNode, "onmouseover", function(){
					// temporarily suspend the cycling, but don't officially pause
					// it and don't allow suspending if we're transitioning
					if(_t.suspendOnHover && !_t.anim && !_t.wfe){
						var t = _t._endTime,
							n = _t._now();
						_t._suspended = true;
						_t._resetTimer();
						_t._resumeDuration = t > n ? t - n : 0.01;
					}
				}),

				d.connect(_t._domNode, "onmouseout", function(){
					// if we were playing, resume playback unless were in the
					// middle of a transition
					if(_t.suspendOnHover && !_t.anim){
						_t._suspended = false;
						if(_t.playing && !_t.wfe){
							_t.play(true);
						}
					}
				})
			];

			// everything is ready, so start
			if(_t.autoStart && _t.panes.length > 1){
				// start playing
				_t.play();
			}else{
				// since we're not playing, lets pause
				_t.pause();
			}
		},

		destroy: function(){
			//	summary:
			//		Disconnect the AutoRotator's events.
			d.forEach(this._connects, d.disconnect);
			this.inherited(arguments);
		},

		play: function(/*boolean?*/skipCycleDecrement, /*boolean?*/skipDuration){
			//	summary:
			//		Sets the state to "playing" and schedules the next cycle to run.
			this.playing = true;
			this._resetTimer();

			// don't decrement the count if we're resuming play
			if(skipCycleDecrement !== true && this.cycles > 0){
				this.cycles--;
			}

			if(this.cycles == 0){
				// we have reached the number of cycles, so pause
				this.pause();
			}else if(!this._suspended){
				this.onUpdate("play");
				// if we haven't been suspended, then grab the duration for this pane and
				// schedule a cycle to be run
				if(skipDuration){
					this._cycle();
				}else{
					var r = (this._resumeDuration || 0)-0,
						u = (r > 0 ? r : (this.panes[this.idx].duration || this.duration))-0;
					// call _cycle() after a duration and pass in false so it isn't manual
					this._resumeDuration = 0;
					this._endTime = this._now() + u;
					this._timer = setTimeout(d.hitch(this, "_cycle", false), u);
				}
			}
		},

		pause: function(){
			//	summary:
			//		Sets the state to "not playing" and clears the cycle timer.
			this.playing = this._suspended = false;
			this.cycles = -1;
			this._resetTimer();

			// notify the controllers we're paused
			this.onUpdate("pause");
		},

		_now: function(){
			//	summary:
			//		Helper function to return the current system time in milliseconds.
			return (new Date()).getTime(); /*int*/
		},

		_resetTimer: function(){
			//	summary:
			//		Resets the timer used to schedule the next transition.
			clearTimeout(this._timer);
		},

		_cycle: function(/*boolean|int?*/manual){
			//	summary:
			//		Cycles the rotator to the next/previous pane.
			var _t = this,
				i = _t.idx,
				j;

			if(_t.random){
				// make sure we don't randomly pick the pane we're already on
				do{
					j = Math.floor(Math.random() * _t.panes.length + 1);
				}while(j == i);
			}else{
				j = i + (_t.reverse ? -1 : 1)
			}

			// rotate!
			var def = _t.go(j);

			if(def){
				def.addCallback(function(/*boolean?*/skipDuration){
					_t.onUpdate("cycle");
					if(_t.playing){
						_t.play(false, skipDuration);
					}
				});
			}
		},

		onManualChange: function(/*string*/action){
			//	summary:
			//		Override the Rotator's onManualChange so we can pause.

			this.cycles = -1;

			// obviously we don't want to pause if play was just clicked
			if(action != "play"){
				this._resetTimer();
				if(this.pauseOnManualChange){
					this.pause();
				}
			}

			if(this.playing){
				this.play();
			}
		}
	});

})(dojo);