/**
 * Robi karuzelkę o określonej szerokości, podpiąć pod odpowiednie elementy
 * @author ChanibaL
 * @param mainElement - element w którym trzymać karuzele
 * @param imgs {String(obrazek):String(link)|Function}
 * @param opts {} [optional] Opcje do nadpisania, dostępne opcje:
 * 	mouseWheelElement - element dostający eventy kółka myszki (mainElement)
 * 	containerElement - element trzymający rzeczy do pokazania (.karuzela-main)
 * 	nextBtn - guzik następny (domyślnie sam się tworzy)
 * 	prevBtn - guzik poprzedni (domyślnie sam się tworzy)
 * 	baseHrefImg - dodawane przed obrazkiem ("")
 * 	baseHrefLink - dodawane przed linkiem ("")
 * 	onClick - wywoływane przy onclick elementu (domyślnie idzie href)
 * 	width - szerokość (400)
 * 	height - wysokość (300)
 * 	accelFactor - przyśpieszenie [0..1] (2/3)
 * 	minSpeed - minimalna prędkość (2)
 * 	autoNextHz - częstotliwość zmiany następnego, może być zero (1/7)
 * 	prefetch - ile elementów w bok ładować (1)
 * 	prefetchMin - j/w ale miniaturki (3)
 * 	fps - klatek animacji na sekundę (20)
 * 	loadingImage - zanim się nie uruchomi daje ten obrazek (ajax-loader.gif)
 * 	getPreviewFunc - funkcja zwracająca miniaturkę (function(f){return f.replace(/^imgs/, "min");})
 * 	mouseWheelBlockTime - czas blokowania myszki (guziki przyjmowane co...) (50ms)
 * 	randomStart - czy ma startować od losowego (true)
 */
function karuzela(mainElement, imgs, opts) {
	var c, i;

	cfg={
		mouseWheelElement: mainElement,
		containerElement: function(){var c,i; for(i in c=mainElement.getElementsByTagName("div")) if(c[i].className == "karuzela-main") return c[i]; throw "requires a element with class=karuzela-main"}(),
		nextBtn: function(){e=document.createElement("button"); e.className="next"; mainElement.appendChild(e); e.appendChild(document.createTextNode(">")); return e;},
		prevBtn: function(){e=document.createElement("button"); e.className="prev"; mainElement.appendChild(e); e.appendChild(document.createTextNode("<")); return e;},
		onClick: function(){return true;},
		baseHrefImg: "",
		baseHrefLink: "",
		width: 400,
		height: 300,
		minSpeed: 2,
		accelFactor: 2/3,
		autoNextHz: 1/7,
		prefetch: 1,
		prefetchMin: 3,
		fps: 20,
		loadingImage: "ajax-loader.gif",
		getPreviewFunc: function(f){return f.replace(/img\//, "min/");},
		mouseWheelBlockTime: 15,
		randomStart: true
	};

	for(i in opts)
		if(cfg[i] != undefined)
			cfg[i]=opts[i];
		else
			throw "incorrect option: "+i;
	if(typeof cfg.nextBtn == "function")
		cfg.nextBtn=cfg.nextBtn();
	if(typeof cfg.prevBtn == "function")
		cfg.prevBtn=cfg.prevBtn();

	var imgObjects=[];

	function createImage(counter, autoload, image, action) {
		var img=document.createElement("img");
		var a=document.createElement("a");
		
		if(typeof action == "function")
			a.onclick=function(callback){
				return function() {
					callback();
					return false;
				}
			}(action);
		else
			a.href=cfg.baseHrefLink+action;

		img.src=cfg.baseHrefImg+(autoload // automatycznie wgraj pierwszy i kopię
			?image
			:cfg.loadingImage);
		img.alt="";

		for(var j in style={
			position:"absolute",
			left: cfg.width*counter+"px",
			top: "0px",
			width: cfg.width+"px",
			height: cfg.height+"px",
			display: "block",
			textAlign: "center"
		}) a.style[j]=style[j];

		a.appendChild(img);
		cfg.containerElement.appendChild(a);
		
		imgObjects.push({
			loaded: autoload?"full":false,
			x: cfg.width*counter,
			src: cfg.baseHrefImg+image,
			minSrc: cfg.getPreviewFunc(cfg.baseHrefImg+image),
			element: {a:a, img:img}
		});
	}

	if(cfg.randomStart) {
		c=0;
		for(i in imgs)
			c++;
		var breakPoint=Math.floor(Math.random()*c);
		var beforeBreak={};
		var afterBreak={};
	
		c=0;
		for(i in imgs) {
			(((++c) <= breakPoint)
				?beforeBreak
				:afterBreak)[i+""]=imgs[i]+"";
		}

		imgs=afterBreak;
		for(i in beforeBreak)
			imgs[i]=beforeBreak[i];
	}

	var counter=0, image;
	for(image in imgs)
		createImage(counter, (counter++ == 0), image, imgs[image]);
	
	for(image in imgs) {
		createImage(counter, true, image, imgs[image]); // dublowanie ostatniego
		break;
	}

	var scrollTo=cfg.containerElement.scrollLeft=Math.round(cfg.containerElement.scrollLeft/cfg.width)*cfg.width;
	var totalWidth=cfg.width*counter;	// bez powielonego

	scroller=new Interval(function() {
		var diff=scrollTo-cfg.containerElement.scrollLeft;
		var speed=Math.min(Math.abs(diff), Math.max(cfg.minSpeed, Math.pow(Math.abs(diff), cfg.accelFactor)));
		var s=cfg.containerElement.scrollLeft;

		if(diff > 0)
			s+=speed;
		else if(diff < 0)
			s-=speed;

		if(s < 0) {
			scrollTo+=totalWidth;
			s+=totalWidth;
		}
		else if(s >= totalWidth) {
			scrollTo-=totalWidth;
			s=0;
		}
		
		cfg.containerElement.scrollLeft=s;
		
		var center=s;
		for(var i in imgObjects) {
			var o=imgObjects[i];
			if(o.loaded == "full")
				continue;

			offset=Math.min(
				Math.abs(center-o.x),
				Math.min(
					Math.abs((center-totalWidth)-o.x),
					Math.abs((center+totalWidth)-o.x)
				)
			);
			
			if(offset <= cfg.width*cfg.prefetch) {
				imgObjects[i].element.img.src=imgObjects[i].src;
				imgObjects[i].loaded="full";
			} else if (offset <= cfg.width*cfg.prefetchMin && o.loaded == false) {
				imgObjects[i].element.img.src=imgObjects[i].minSrc;
				imgObjects[i].loaded="part";
			}
		}
	}, cfg.fps);

	if(cfg.autoNextHz != 0)
		var autoNext=new Interval(function() {
			scrollTo+=cfg.width;
		}, cfg.autoNextHz);

	function scrollBy(delta) {
		scrollTo+=delta;
	}

	cfg.nextBtn.onclick=function(cfg, scrollByFunc, autoNext) {
		return function(){
			scrollByFunc(cfg.width);
			if(autoNext)
				autoNext.stop();
		};
	}(cfg, scrollBy, autoNext)

	cfg.prevBtn.onclick=function(cfg, scrollByFunc, autoNext) {
		return function(){
			scrollByFunc(-cfg.width);
			if(autoNext)
				autoNext.stop();
		};
	}(cfg, scrollBy, autoNext)


	var blockWheel={v:false};

	// http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
	function wheelCallback(e) {
		e=e?e:window.event;

		if(!blockWheel.v) {
			var delta=e.detail?e.detail*-1:e.wheelDelta/40;
			if(delta < 0)
				cfg.nextBtn.onclick();
			else if(delta > 0)
				cfg.prevBtn.onclick();
		}

		blockWheel.v=true;
		setTimeout(function(blockWheel){return function(){blockWheel.v=false;}}(blockWheel), cfg.mouseWheelBlockTime);
			
		if(e.stopPropagation)
			e.stopPropagation();
		if(e.preventDefault)
			e.preventDefault();
		e.cancelBubble=true;
		e.cancel=true;
		e.returnValue=false;
		return false;
	}

	if(cfg.mouseWheelElement.addEventListener)
		cfg.mouseWheelElement.addEventListener("DOMMouseScroll", wheelCallback, false);
	else if(cfg.mouseWheelElement.attachEvent)
		cfg.mouseWheelElement.attachEvent("onmousewheel", wheelCallback);
	cfg.mouseWheelElement.onmousewheel=wheelCallback;
}

