Array.implement({  
	shuffle: function() {  
		//destination array  
		for(var j, x, i = this.length; i; j = parseInt(Math.random() * i), x = this[--i], this[i] = this[j], this[j] = x);  
		return this;  
	}
});

/**
 * a List fetches its children
 */
List = function(type) {
	this.list = []
	this.type = type
	
	this.list.random = function() {
		return this[$random(0, this.length - 1)]
	}.bind(this.list)
}
List.prototype = {
	load_from_json: function(url, onFinish) {
		onFinish = onFinish || null
		try {
			var json_req = new Request.JSON({'url': url,
				'onSuccess':function(r, onFinish){
					// pull all media
					try {
						for (var i=0; i < r.media_list.length; i++) {
							var media = new this.type(r.media_list[i])
							this.list.push(media)
						}
						if ($chk(onFinish)) onFinish(this.list, r)
					} catch(e) {
						// some error
					}
				}.bindWithEvent(this, onFinish),
				'onFailure':function(r){
					window.console.log(r)
				}.bind(this)
			}).get()
		} catch(e) {
			// some error
		}		
	}
}


File = function(data) {
	if ($defined(data)) $extend(this, data);
}
File.prototype = {
	id: 0,
	mediaId: 0,
	fileName: '',
	url:'',
	width: 0,
	height: 0,
	type: null,
	use: null
}
Media = function (data) {
	if ($defined(data)) $extend(this, data);
	this.file_types = {};
	this.file_uses = {};
	for (var i = 0; i < this.files.length; i++) {
		var file = new File(this.files[i])
		
		this.file_types[file.type] = this.file_types[file.type] || []
		this.file_types[file.type].push(file)
		
		this.file_uses[file.use || 'NORMAL'] = this.file_uses[file.use || 'NORMAL'] || []
		this.file_uses[file.use || 'NORMAL'].push(file)
		
		this.files[i] = file
	}
}
Media.prototype = {
	id: null,
	title: '',
	description: '',
	credits: '',
	displayOrder: 0,
	copyleft:false,
	visible: false,
	releaseDate: null,
	type: null,
	files: [],
	file_types: {},
	file_uses: {},
	
	create_thumb_view: function(attrs) {
		var defaults = {
			'id':this.id,
			'src':this.file_uses['THUMBNAIL'][0].url || ''
		}
		
		if (attrs) {
			$extend(defaults, attrs)
		}
		var view = new Element('img', defaults)
		return view
	},
	create_view: function(attrs) {
		var defaults = {
			'id':this.id,
			'class':'slide',
			'src':this.file_uses['NORMAL'][0].url || ''
		}
		
		if (attrs) {
			$extend(defaults, attrs)
		}
		var view = new Element('img', defaults)
		view.addEvent('click', function(){
			this.show()
		}.bind(this))
		
		return view
	},
	buffer_view: function(attrs) {
		this.view = this.create_view(attrs)
	},
	get_view: function(attrs) {
		if (this.view) return this.view
		else return this.create_view(attrs)
	},
	/**
	 * show this media in a seperate layer
	 */
	show: function () {
		openIbox(contextPath + '/media/showLayer/' + this.id + '.page','height=' + (this.file_uses['NORMAL'][0].height+300) + '&width=' + (this.file_uses['NORMAL'][0].width+30) );
	}
}

/**
 * preloads a randomized buffer of medias 
 */
RandomBuffer = function(list, size, buffer_cb) {
	this.size = size
	this.list = list
	this.seeded = this.seed()
	this.buffers = []
	this.cb = buffer_cb
	this.current_buffer = []
	this.increase(2)
}
RandomBuffer.prototype = {
	current_buffer: [],
	shift: function() {
		if (!this.current_buffer.length && (this.buffers.length > 1 || this.increase())) {
			this.current_buffer = this.buffers.shift()
		}
		return this.current_buffer.shift()
	},
	/**
	 * we increase the preload-buffer by count times
	 * @return the new buffers length
	 */
	increase: function(count) {
		count = count || 1
		var current_buffers = this.buffers.length
		
		// seed new media
		if (this.seeded.length < this.size * count * 2) {
			var f = function() {
				this.seeded.combine(this.seed())
			}
			f.delay(1, this)
		}
		for (var j = 0; j < count; j++) {
			var new_buffer = []
			for (var i = 0; i < this.size; i++) {
				var buffered = this.list[this.seeded.shift()]
				// async call to buffer
				var t_id = this.cb.delay(1, buffered)
				// put into queue
				new_buffer.push(buffered)
			}
			this.buffers.push(new_buffer)
		}
		return this.buffers.length
	},
	/**
	 * @return a randomized array 
	 */
	seed: function () {
		var ground = []
		for (var i = 0; i < this.list.length; ground.push(i++)); 
		return ground.shuffle()
	}
}

Gallery = function() {}
Gallery.prototype = {
	/**
	 * creates the view as a table
	 */
	create_table_view: function(list, columns) {
		var table = new Element('table',{'class':'grid'}).grab(tbody = new Element('tbody'))
		var i = 0
		var current_row = null
		while (i < list.length) {
			var current_media = list[i]
			if (!current_media.visible) {
				i++
				continue
			}
			if (i % columns == 0) {
				if (current_row) tbody.grab(current_row)
				var current_row = new Element('tr')
			}
			current_row.grab(
				new Element('td').grab(
					wrap = new Element('div',{
						'class':'wrap',
						'styles':{
							'padding':'5px'
						}
					})
				)
			)
			wrap.grab(current_media.create_thumb_view({'width':(650 - columns * 10)/ columns})
				.addEvent('mouseover', function(e){
						this.addClass('hover')
					}.bindWithEvent(wrap)
				)
				.addEvent('mouseout', function(e){
						this.removeClass('hover')
					}.bindWithEvent(wrap)
				)
				.addEvent('click', function(e){
						this.show()
					}.bindWithEvent(current_media)
				)
			)
			i++
		}
		return table
	},
	create_slide_show: function(list, period){
		period = period || 6000
		var stage = new Element('div', {'class':'stage'})
		$extend(stage, {
			current_slide: null,
			random_buffer: new RandomBuffer(list, 5, function(){
				this.buffer_view({'styles':{'opacity':'0'}})
			}),
			shift: function() {
				this.grab(this.current_slide = this.random_buffer.shift().get_view({'styles':{'opacity':'0'}}))
				var fx = new Fx.Tween(this.current_slide)
				fx.start('opacity', '0', '1')
			}
		})
		stage.shift()
		this.slide_show_timer = function(list){
			var next_slide = this.random_buffer.shift().get_view()
			this.grab(next_slide)
			var fxUp = new Fx.Tween(next_slide)
			fxUp.start('opacity', '0', '1')

			if (this.current_slide) {
				var fxDown = new Fx.Tween(this.current_slide)
				fxDown.addEvent('complete', function(e){
					e.dispose()
				}.pass(this.current_slide))
				fxDown.start('opacity', '1', '0')
			}
			this.current_slide = next_slide
			
		}.periodical(period, stage, [list])
		return stage
	}
}


