var isIE = !!(document && document.all);

function format2dec(num) {
	return parseInt(num * 100) / 100;
}

function Animation(config) {
	// Cache properties into local instance vars.
	if (!config) config = {};
	this.config = config;

	this.win = config.win || window;
	this.varName = config.varName;
	this.refresh = config.refresh || 100;
	this.elId = config.elId;
	this.el = this.win.document.getElementById(this.elId);

	// Reset the image counter to the start of the show.
	this.counter = 0;
	// Have we been given a start delay? If so, auto-play after delay.
	if (config.startDelay) {
		this.timerId = this.win.setTimeout(this.varName + '.play()', config.startDelay);		
	}
}

Animation.prototype.pause = function() {
	if (this.timerId) this.win.clearTimeout(this.timerId);
}
Animation.prototype.play = function() {
	if (this.timerId) this.win.clearTimeout(this.timerId);

	var el = this.el,
		s = el && el.style,
		props = this.config.props;
	if (!s || !props) return this;

	var c = this.counter,
		stillPlaying = false;

	// Walk the hashtable of animated properties.
	for (var p in props) {
		// For this animated property "p"...
		var pconfig = props[p],
			values = pconfig.values,
			units = pconfig.units;

		// Measure the array of values for this animated property, if we haven't already measured.
		if (pconfig.length == null) pconfig.length = values && values.length || 0;

		if (c >= pconfig.length) {
			// No more values left in array.  Stop playing this property's animation.
			continue;
		} else {
			// Values still left.  Apply animated property value.

			// Support for animating a property of a (different) element.			
			var sApply = s;
			if (pconfig.elId && (pconfig.elId != this.elId)) {
				var elApply = this.win.document.getElementById(pconfig.elId);
				sApply = elApply && elApply.style;
			}

			// Do we have an element to apply the animation to?  If so, do it now.			
			if (sApply) {
				switch(p)
				{
					case 'opacity':
						if (isIE) {
							sApply.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + values[c] + ')';
						} else {
							sApply.MozOpacity = values[c] / 100;
						}
						break;
					default:
						s[p] = units ? values[c] + units : values[c];
						break;
				}
			}
			
			// If this is the first "frame" of animation, unhide element if so requested.
			if (c == 0 && this.config.unhide) {
				sApply.visibility = 'visible';
			}
			
			// Are there further values still pending?  If so, we should continue playing this animation.
			stillPlaying = (c < pconfig.length - 1);
			
		}
	}

	this.counter++;
	if (stillPlaying && this.varName) {
		this.timerId = this.win.setTimeout(this.varName + '.play()', this.refresh);
	} else if (this.config.callback) {
		if (this.config.callbackDelay) {
			this.win.setTimeout(this.config.callback, this.config.callbackDelay);
		} else {
			this.config.callback();
		}
	}

	return this;
}

function SlideShow(props) {
	// Cache properties into local instance vars.
	if (!props) props = {};
	this.props = props;
	this.imgs = props.imgs || [];
	this.imgs_len = this.imgs.length;
	this.win = props.win || window;
	this.varName = props.varName;
	this.refresh = props.refresh || 5000;
	this.refresh_ken = props.refresh_ken || 200;
	this.kenMargin = this.kenMargin || 100;
	this.kenBurns = !!(props.kenBurns && this.varName);
	this.elId = props.elId;
	var el = this.el = this.win.document.getElementById(this.elId);

	// Set the IE fade transition for the HTML image.	
	if (el) {
		if (isIE) el.style.filter = 'progid:DXImageTransform.Microsoft.Fade(duration=2,overlap=1)';
	}
	
	// Reset the image counter to the start of the show.
	this.counter = -1;
	this.isPlaying = false;	
	
	// Preload the image files, unless explictly prohibited.
	this.preload = (props.preload !== false);
	if (this.preload)  this._preloadImgs();
}

SlideShow.prototype._preloadImgs = function () {
	var imgs = this.imgs,
		len = this.imgs_len;
	for (var i = 0; i < len; i++) {
		var src = imgs[i].src;
		if (src) {
			var img = new Image();
			img.src = '../images/' + src;
		}
	}
}

SlideShow.prototype.jump = function (counter) {
	this.pause();
	this.show(counter);
	return true;
}
SlideShow.prototype.show = function (counter) {
	this.counter = counter;
	var el = this.el;
	if (!el) return;
	if (isIE) el.filters[0].Apply();
	el.style.backgroundImage = 'url(../images/' + this.imgs[counter].src + ')';
	if (this.kenBurns) this.initKen();
	if (this.props.onShow) this.props.onShow(counter);
	if (isIE) {
		el.filters[0].Play();
	}
}
SlideShow.prototype.toggle = function() {
	if (this.isPlaying) 
		this.pause();
	else
		this.play();
	return !!this.isPlaying;
}
SlideShow.prototype.play = function () {
	if (this.timerId) this.win.clearTimeout(this.timerId);
	this.counter++;
	if (this.counter >= this.imgs_len) {
		this.counter = 0;
	}
	this.show(this.counter);	
	if (this.varName) this.timerId = this.win.setTimeout(this.varName + '.play()', this.refresh);
	this.isPlaying = true;
	if (this.props.onToggle) this.props.onToggle(true);
	return this;
}
SlideShow.prototype.pause = function() {
	if (this.timerId) this.win.clearTimeout(this.timerId);
	if (this.kenTimerId) this.win.clearTimeout(this.kenTimerId);
	this.timerId = this.kenTimerId = null;
	this.isPlaying = false;
	if (this.props.onToggle) this.props.onToggle(false);
	return this;
}

SlideShow.prototype.initKen = function() {
	if (!this.el) return;
	
	this.kenLength = this.imgs[this.counter].kenLength;
	if (this.kenLength == null) this.kenLength = parseInt(this.kenMargin * (0.25 + 0.75 * Math.random()));
	
	this.kenAngle = this.imgs[this.counter].kenAngle;
	if (this.kenAngle == null) {
		this.kenAngle = parseInt(Math.random() * 7) * Math.PI / 4;
	} else {
		this.kenAngle = this.kenAngle * Math.PI / 4;
	}
	
	this.kenLeft = format2dec(Math.min(0, - this.kenLength * Math.cos(this.kenAngle))); // - this.kenMargin - this.kenLength * Math.cos(this.kenAngle) ;
	this.kenTop = format2dec(Math.min(0, - this.kenLength * Math.sin(this.kenAngle))); // - this.kenMargin - this.kenLength * Math.sin(this.kenAngle) ;
	this.kenIncrLength = this.kenLength * this.refresh_ken / this.refresh;
	var l = format2dec(this.kenIncrLength * Math.cos(this.kenAngle));
	if (Math.abs(l) < 1) {
		l = 0;
	} else {
		l = (l < 0) ? -1 : 1;
	}
	this.kenIncrLeft = l;
	
	var t = format2dec(this.kenIncrLength * Math.sin(this.kenAngle));
	if (Math.abs(t) < 1) {
		t = 0;
	} else {
		t = (t < 0) ? -1 : 1;
	}
	this.kenIncrTop = t;
	
	this.ken();
}


SlideShow.prototype.ken = function() {
	if (this.kenTimerId) this.win.clearTimeout(this.kenTimerId);

	this.kenLeft += this.kenIncrLeft;
	this.kenTop += this.kenIncrTop;
	this.el.style.backgroundPosition = String(this.kenLeft) + 'px ' + String(this.kenTop) + 'px'; 

	this.kenTimerId = this.win.setTimeout(this.varName + '.ken()', this.refresh_ken);
}

