/**
 * @author Bjørge Næss
 * @copyright Bjørge Næss (c) 2008
 *
 */
var Slider = Class.create({

	initialize: function(element, options) {

		element = $(element);

		this.options = {
			previewer: null,
			caption: null,
			previousButton: null,
			nextButton: null,
			playButton: null,
			counter: null,
			fading: true,
			loader: new Image(),
			auto: true
		}

		Object.extend(this.options, options || { });

		this.options.previewer = $(this.options.previewer)
		this.options.caption = $(this.options.caption)
		this.options.counter = $(this.options.counter)

		if (element.tagName.toLowerCase() == 'ul') {
			// Then, create the wrapper
			this.element=new Element('div', {'class': 'slider'})
			// and wrap the ul
			element.wrap(this.element)
			this.ulEl = element;
		}
		else {
			this.element = element
			this.element.addClassName('slider')
			this.ulEl = this.element.down('ul');
			if (!this.ulEl) return alert('No UL element contained in given element: '+element.id)
		}
		if (this.options.previousButton) {
			this.previousButton = $(this.options.previousButton)
			this.previousButton.observe('click', this.previous.bind(this))
		}
		if (this.options.nextButton) {
			this.nextButton = $(this.options.nextButton)
			this.nextButton.observe('click', this.next.bind(this))
		}

		if (this.options.playButton) {
			this.playButton = $(this.options.playButton)
			this.playButton.addClassName(this.options.auto ? 'pause' : 'play')
			this.playButton.observe('click', this.playpause.bind(this))
		}

		this.firstElement = this.ulEl.down('li')
		if (!this.firstElement) {
			this.previousButton.hide()
			this.playButton.hide()
			this.nextButton.hide()
			if (this.options.caption) this.options.caption.removeClassName('disabled')
			this.options.previewer.up().update('No images')
			return false;
		}

		this.lastElement = this.firstElement.siblings().last()

		this.currentElement = null

		if (this.options.caption) this.options.caption.addClassName('disabled')

		this._cache = new Hash()

		this.ulEl.observe('click', this.centerOnClick.bindAsEventListener(this))

		Event.observe(document, 'keydown', function(e) {
			if (e.keyCode == Event.KEY_RIGHT) this.next()
			if (e.keyCode == Event.KEY_LEFT) this.previous()
		}.bind(this))

		//Event.observe(this.options.previewer, 'click', this.next.bind(this))

		this._imgStartDragListener = this.imgStartDragListener.bind(this)
		this._imgOnDragListener = this.imgOnDragListener.bind(this)
		this._imgEndDragListener = this.imgEndDragListener.bind(this)
		Event.observe(window, 'load', this.startUp.bind(this))

		this.fadeTimer = null
	},
	startUp: function() {
		this.viewport = this.element.getDimensions()
		this.alignMiddle()
		this._hashChangeCheck()
		setInterval(this._hashChangeCheck.bind(this), 200)
		if (!this.currentElement) this.first()
		//this.updateButtonsStyle()
	},
	imgStartDragListener: function(event) {
		this.playpause(false)
		this._dragging = true
		this._lastPos = [parseInt(Event.pointerX(event)),parseInt(Event.pointerY(event))]
		this.options.previewer.addClassName('dragging')
	},
	imgOnDragListener: function(event) {
		if (!this._dragging) return

		var currx = parseInt(Event.pointerX(event))
		var curry = parseInt(Event.pointerY(event))
		var deltax = currx - this._lastPos[0]
		var deltay = curry - this._lastPos[1]

		var currbgpos = this.options.previewer.style.backgroundPosition.split(' ', 2)

		var isNumber = function(num) {return num != NaN && Object.isNumber(num) || num === 0}
		var pixNum = function(pxstr) {
			var m = pxstr.match(/(^-?\d+px|^0+[^1-9]+$)/);
			return m ? parseInt(m[1]) : null
		}
		if (isNumber(pixNum(currbgpos[0]))) currbgpos[0] = (pixNum(currbgpos[0]) + deltax)+'px'
		if (isNumber(pixNum(currbgpos[1]))) currbgpos[1] = (pixNum(currbgpos[1]) + deltay)+'px'

		this.options.previewer.style.backgroundPosition = currbgpos[0]+' '+currbgpos[1]
		this._lastPos = [currx, curry]
	},
	imgEndDragListener: function(event) {
		this._dragging=false
		this.options.previewer.removeClassName('dragging')
	},
	disableDrag: function() {
		this.options.previewer.stopObserving('mousedown', this._imgStartDragListener)
		this.options.previewer.stopObserving('mousemove', this._imgOnDragListener)
		this.options.previewer.stopObserving('mouseup', this._imgEndDragListener)
		this.options.previewer.style.backgroundPosition = 'center bottom'
		this.options.previewer.style.backgroundRepeat = 'no-repeat' 
		this.options.previewer.removeClassName('draggable')
	},
	enableDrag: function() {
		this.options.previewer.observe('mousedown', this._imgStartDragListener)
		this.options.previewer.observe('mousemove', this._imgOnDragListener)
		this.options.previewer.observe('mouseup', this._imgEndDragListener)
		this.options.previewer.addClassName('draggable')
	},
	imageLoaded: function(img) {
		this.options.previewer.setOpacity(.00001)
		this.options.previewer.style.backgroundImage = 'url('+img.src+')'
		var margins = [this.options.previewer.getWidth(), this.options.previewer.getHeight()]

		this.disableDrag()
		var bgposx = 'center'
		var bgposy = 'bottom'
		var repeat = 'no-repeat'
		//$('bjoergenaess').update(margins)
		if (img.width > margins[0] || img.height > margins[1]) {
			if (img.width > margins[0]) {
				bgposx = 0
				repeat = "repeat-x"
			}
			if (img.height > margins[1]) {
				bgposy = 0
				repeat = (repeat == 'no-repeat') ? 'repeat-y' : 'repeat'
			}
			this.options.previewer.style.backgroundRepeat = repeat
			this.options.previewer.style.backgroundPosition = bgposx+ ' '+ bgposy
			this.enableDrag()
		}

		this.options.previewer.appear({duration: 2.0})
		if (this.options.auto) this.fadeTimer = setTimeout(this.fadeNext.bind(this), 6500)

		if (this.options.caption) {
			if (!this.options.caption.innerHTML) this.options.caption.addClassName('disabled')
			else  this.options.caption.removeClassName('disabled')
			var margin = 800 - img.width;
			if (margin < 0) margin = 0
			this.options.caption.style.marginLeft=(margin/2)+'px'
			if (img.width +  margin/2 < 670)
				this.options.caption.style.width = img.width+'px'
			else
				this.options.caption.style.width = (670-(margin/2))+'px'
		}
		this.currentImage = img
	},
	centerOnClick: function(event) {
		var liEl = event.findElement('li')
		this.showImage(liEl)
		this.moveTo(liEl)
		Event.stop(event)
	},
	showImage: function(liEl) {

		var capt = liEl.down('img').alt
		this.options.caption.update(capt)

		var url = liEl.down('a').href

		if (this.options.previewer) {
			var img = this._cache.get(url)
			if (!img) {
				this.disableDrag()
				this.options.previewer.style.backgroundImage='url('+this.options.loader+')'
				img = new Image()
				this._cache.set(url, img)
				Event.observe(img, 'load', this.imageLoaded.curry(img).bind(this))
				img.src = url
			}
			else this.imageLoaded(img)
		}
		if (this.options.counter) {
			var offset = liEl.previousSiblings().length +1
			var total = offset + liEl.nextSiblings().length
			this.options.counter.update(offset+' / '+total)
		}
	},
	moveTo: function(liEl) {

		var current = this.element.down('.current')
		if (current) current.removeClassName('current')

		var it = this.firstElement
		var left = 0;
		while (it && it != liEl) {
			left += it.getWidth()
			it = it.next('li')
		}

		var remaining = this.getWidth() - left

		var element_center = Element.getWidth(liEl) / 2
		var viewport_center = this.viewport.width / 2
		var new_pos = left*-1 + (viewport_center - element_center)

		if (new_pos > 0) new_pos = 0

		//console.log((viewport_center - liEl.getWidth()) + remaining <= this.viewport.width)
		if ((viewport_center - element_center) + remaining <= this.viewport.width) {
			new_pos = (this.getWidth() - this.viewport.width)*-1
		}
		//alert('('+viewport_center+' - '+element_center+') + '+remaining+' <= '+this.viewport.width)
		new Effect.Move(this.ulEl, {
		  x: new_pos, y: 0, mode: 'absolute',
		  transition: Effect.Transitions.sinoidal
		}); /**/

		liEl.addClassName('current')

		this.currentElement = liEl

		var img_id = (this.currentElement == this.firstElement) ? null : liEl.id.substring(4)
		UrlSegment.setHashSegment('img', img_id)
	},
	getWidth: function() {
		var list = this.ulEl.select('li');
		var width = 0
		for (var i = 0; i < list.length; i++) {
			width += Element.getWidth(list[i])
		}
		return width;
	},
	getCenterElement: function() {
		var center = this.viewport.width / 2

		var list = this.ulEl.select('li');

		var left = parseInt(this.ulEl.style.left) || 0

		//console.log(left)
		var element;

		for (var i = 0; i < list.length; i++) {
			element = list[i]
			left += element.getWidth()
			if (left >= center) break
		}

		return element
	},
	getNextOutside: function() {

		var liEl = (this.currentElement) ? this.currentElement : this.firstElement
		liEl = liEl.next('li')
		while (liEl) {
			if (!this.inViewport(liEl)) return liEl
			liEl = liEl.next('li')
		}
		return null
	},
	getPreviousOutside: function() {
		var liEl = (this.currentElement) ? this.currentElement : this.firstElement
		liEl = liEl.previous('li')
		while (liEl) {
			if (!this.inViewport(liEl)) return liEl
			liEl = liEl.previous('li')
		}
		return null
	},
	inViewport: function(liEl) {
		var current = this.firstElement
		var el_start = 0;
		while (current && current != liEl) {
			el_start += current.getWidth()
			current = current.next('li')
		}

		var vp_start = parseInt(this.ulEl.style.left)*-1 || 0
		var vp_end = vp_start + this.viewport.width

		var el_end = el_start + liEl.getWidth()

		//console.log([el_start, '-', el_end], ' / ', [vp_start,'-', vp_end])
		return el_start >= vp_start && el_end <= vp_end

	},
	_hashChangeCheck: function() {

		var img_id = UrlSegment.getHashSegment('img')

		if (this.last_img_id == img_id) return
		else if (!img_id) {
			this.showImage(this.firstElement)
			this.moveTo(this.firstElement)
		}

		this.last_img_id = img_id

		var current = this.ulEl.select('li').find(function(liEl){
			return liEl.id=='img_'+img_id
		})
		if (current && current != this.currentElement) {
			this.showImage(current)
			this.moveTo(current)
		}
	},
	fadeNext: function() {
		var next = this.currentElement.next('li') ||  this.firstElement
		if (!next || next == this.currentElement) return
		if (this.firstElement == this.lastElement) return
		clearTimeout(this.fadeTimer)
		if (!this.options.auto) return
		this.options.caption.update()
		this.fader = new Effect.Fade(this.options.previewer, {
			duration: 2.0,
			afterFinish: function() {
				this.next()
			}.bind(this)
		})
	},
	playpause: function(bool) {
		if (bool !== true && bool !== false) bool = !this.options.auto
		this.playButton.removeClassName('play')
		this.playButton.removeClassName('pause')
		this.options.auto = bool
		this.playButton.addClassName(bool ? 'pause' : 'play')
		if (bool) this.fadeTimer = setTimeout(this.fadeNext.bind(this), 1000)
		else clearTimeout(this.fadeTimer)

	},
	first: function() {

		if (!this.firstElement) return

		this.showImage(this.firstElement)
		this.moveTo(this.firstElement)
	},
	next: function (event) {
		clearTimeout(this.fadeTimer)

		if (this.fader) this.fader.cancel()

		if (!this.currentElement) return
		//var next = this.getNextOutside()
		//if (next) this.moveTo(next)
		var next = this.currentElement.next('li') ||  this.firstElement
		if (!next || next == this.currentElement) return
		this.moveTo(next)
		this.showImage(next)
	},
	previous: function(event) {
		clearTimeout(this.fadeTimer)

		if (this.fader) this.fader.cancel()

		if (!this.currentElement) return
		//var prev = this.getPreviousOutside()
		//if (prev) this.moveTo(prev)
		var prev = this.currentElement.previous('li') || this.lastElement
		if (!prev || prev == this.currentElement) return

		this.currentElement = prev
		this.moveTo(this.currentElement)
		this.showImage(this.currentElement)
	},
	alignMiddle: function() {

		var list = this.ulEl.select('li');

		// Get height of highest image
		var maxheight = list.collect(function(li) {return li.getHeight()}).max()

		// Adjust heights
		for (var i = 0; i < list.length; i++) {
		   var liEl = list[i]
			var top = (maxheight - liEl.getHeight()) / 2
			liEl.style.marginTop = top + 'px'
		}
		this.element.style.height = maxheight+'px'
		this.viewport.height = maxheight
	}
});
