
function makeSwitcher(userConfig) {

	function init(switcher) {

		// apply user configuration
		var uconf = userConfig;
		for (var key in uconf) {
			if (uconf.hasOwnProperty(key)) {
				switcher[key] = uconf[key];
			}
		}

		// grab frames elements
		if (!switcher.frames || !switcher.frames.length) {
			var element = null;

			if (switcher.parent) {
				if (typeof switcher.parent === 'string') {
					element = document.getElementById(switcher.parent);
				}
				else if (switcher.parent.nodeType && switcher.parent.nodeType === 1) {
					element = switcher.parent;
				}
			}

			if (element) {
				switcher.parent = element;

				var node = element.firstChild;
				while (node) {
					if (node.nodeType === 1) {
						switcher.frames.push(node);
					}
					node = node.nextSibling;
				}
			}


			if (!switcher.frames || !switcher.frames.length) {
				throw new Error('Switcher: couldn\'t find any frames, please provide direct DOM elements for frames, parent element or ID of it.');
			}
		}
		switcher.frameCount = switcher.frames.length;
		switcher.cpointer = 0;

		switcher.framesPerAnimation = Math.round((switcher.fps * switcher.duration) / 1000);
		switcher.frameAnimationPause = Math.round(switcher.duration / switcher.framesPerAnimation);
		switcher.frameHeight = switcher.frames[0].clientHeight;
		switcher.frameWidth = switcher.frames[0].clientWidth;
	}


	function autostep() {
		var switcher = this;
		setTimeout(function () {
			if (!switcher.skipNextAutoStep) {
				switcher.next();
			}
			switcher.skipNextAutoStep = false;
			switcher.go();
		}, switcher.duration + switcher.pause);
	}


	function next() {
		if (this.blocked) {
			return;
		}
		this.blocked = true;
		var previous = this.cpointer;
		var rounding = this.rounding;
		var next = parseInt(previous) + parseInt(this.direction);

		if (rounding === 'cyclic') {
			if (this.direction === 1 && next === this.frameCount) {
				next = 0;
			}
			else if (this.direction === -1 && next < 0) {
				next = this.frameCount - 1;
			}
		}
		else {
			// bounce
			if (this.direction === 1 && next === this.frameCount) {
				this.direction = -1;
				next -= 2;
			}
			else if (this.direction === -1 && next < 0) {
				this.direction = 1;
				next = 1;
			}
		}

		this.cpointer = next;
		this.pre_callback && this.pre_callback.call(this, previous, next);
		this.animate(previous, next);
	}


	function prev() {
		if (this.blocked) {
			return;
		}
		this.blocked = true;
		var previous = this.cpointer;
		var rounding = this.rounding;
		var next = parseInt(previous) - parseInt(this.direction);

		if (rounding === 'cyclic') {
			if (this.direction === 1 && next < 0) {
				next = this.frameCount - 1;
			}
			else if (this.direction === -1 && next === this.frameCount) {
				next = 0;
			}
		}
		else {
			// bounce
			if (this.direction === 1 && next < 0) {
				this.direction = -1;
				next = 1;
			}
			else if (this.direction === -1 && next === this.frameCount) {
				this.direction = 1;
				next -= 2;
			}
		}

		this.cpointer = next;
		this.pre_callback && this.pre_callback.call(this, previous, next);
		this.animate(previous, next);
	}


	function to(which) {
		if (this.blocked) {
			return;
		}
		this.blocked = true;

		var prev = this.cpointer;
		this.cpointer = which;
		console.log(which);
		this.pre_callback && this.pre_callback.call(this, prev, which);
		this.animate(prev, which);
	}


	function animate(from, to) {

		var previous = switcher.frames[from];
		var next = switcher.frames[to];

		previous.className = previous.className.replace(' active', '');
		next.className += (' active');
		this.blocked = false;
	}



	var switcher = {
		// state
		box : null,
		cpointer : -1,
		blocked : false,

		auto : false,
		direction : 1, // 1 (right/up) or -1 (left/down)
		fps : 40,
		pause : 3000,
		duration : 500,
		rounding : 'cycle',  // bounce

		frames : [],
		frameCount : 0,
		frameWidth : null,
		frameHeight : null,

		// behaviour
		go : autostep,
		next : function () {
			this.skipNextAutoStep = true;
			next.call(this);
		},
		prev : function () {
			this.skipNextAutoStep = true;
			prev.call(this);
		},
		to : function (which) {
			this.skipNextAutoStep = true;
			to.call(this, which);
		},

		animate : animate,
		pre_callback : null,
		post_callback : null,

		toString : function () {return 'object Switcher';},

		// uhm...
		dummy : undefined // just for convenience
	};

	init(switcher);
	return switcher;
}

