Carousel = $.klass({
	initialize: function(options) {

		var defaults = {
			width: 940,
			inactiveOpacity: 0.2,
			adjustWrapper: true
		};

		this.options = $.extend({}, defaults, options);

		this.element.carousel_instance = this;

		if (this.options.feed_url) {
			this.feed_url = this.options.feed_url;
		} else {
			var link = this.element.find('a[rel=feed-url]');
			link.remove();
			this.feed_url = link.attr('href');
		}
		this.items = [];

		var self = this;
		this.readFeed(function() {
			self.addWrapper();
			self.addControls();
			self.jumpTo(0, true);
		});
	},

	readFeed: function(callback) {
		var self = this;
		if (this.feed_url) {
			$.getJSON(this.feed_url, function(data) {
				$.each(data, function(i, item) {
					self.addItem(item.type, item.url, item.width, item.height, item.data)
				});
				self.current_item_index = 0;
				self.renderItems(callback);
			});
		} else {
			var data = $.parseJSON(unescape(this.element.data('feed')).replace(/\+/g, ' '));
			$.each(data, function(i, item) {
				self.addItem(item.type, item.url, item.width, item.height, item.data)
			});
			self.current_item_index = 0;
			self.renderItems(callback);
		}
	},

	addWrapper: function() {
		var self = this;

		var $wrapper = $('<div />', { id: 'carousel-wrapper' });
		this.element.wrapAll($wrapper);
		this.viewWrapper = $('#carousel-wrapper');

		if (this.options.adjustWrapper) {
			if (!($.browser.msie && $.browser.version == "6.0")) {
				$(window).resize(function() { self.adjustWrapper(); });
				this.adjustWrapper();
			}
		}
	},

	adjustWrapper: function() {
			var $wrapper = this.viewWrapper;
			var width = $('body').width();

			if (width > 940) {
				$wrapper.width(width);
				$('body').css('overflow-x', 'hidden');
			} else {
				$wrapper.width(936);
				$('body').css('overflow-x', 'scroll');
			}
	},

	viewContainer: function() {
		if (this.view_container) {
			return this.view_container;
		} else {
			var $container = $('<ul />', {
				'class': 'clearfix'
			});
			this.view_container = $container;
			this.element.append($container);

			return $container;
		}
	},

	addControls: function() {
		if (this.items.length > 1) {
			var self = this;
			var prev = $('<a />', {
				'class': 'previous',
				html: '<span>Previous</span>'
			});
			var next = $('<a />', {
				'class': 'next',
				html: '<span>Next</span>'
			});
			prev.bind('click', function() {
				self.previousItem();
			});
			next.bind('click', function() {
				self.nextItem();
			});
			var $wrapper = this.options.controlsElement ? this.options.controlsElement : this.viewWrapper;
			$wrapper.append(prev);
			$wrapper.append(next);
		}
	},

	addItem: function(type, url, width, height, data) {
		switch (type) {
			case 'image':
				var i = new ImageCarouselItem(this, url, width, height, data);
				break;
			case 'video':
				var i = new VimeoCarouselItem(this, url, width, height, data);
				break;
		}

		this.items.push(i);
	},

	renderItems: function(callback) {
		var self = this;
		var position = 0;
		$.each(this.items, function(i, item) {
			self.viewContainer().append(item.render().css('left', position));
			position += parseInt(item.width) + 20;
		});
		this.viewContainer().height(this.items[0].height);
		this.shuffleItems();

		callback && callback.call();
	},

	scrollToCurrent: function(callback) {
		var item = this.getCurrentItem();

		var position = item.viewContainer().position().left - (this.options.width - item.width) / 2;

		this.viewContainer().stop().animate({
			left: sprintf('%dpx', -position)
		}, 500, callback);

		this.updateTitle();
		this.updateMoreLink();
		this.updateSwitcher();
	},

	populateSwitcher: function() {
		var self = this;
		if (this.items.length > 1 && this.options.itemList && !this.switcher_populated) {
			$.each(this.items, function(index, item) {
				$li = $('<li />', {
					text: item.data.title,
					click: function() { self.jumpTo(index) }
				});
				$li.data('item-index', index);
				self.options.itemList.append($li);
			});
			this.switcher_populated = true;
		}
	},

	updateSwitcher: function() {
		var self = this;
		this.populateSwitcher();
		this.options.itemList.find('li').each(function() {
			if ($(this).data('item-index') == self.current_item_index) {
				$(this).addClass('active');
			} else {
				$(this).removeClass('active');
			}
		});
	},

	updateTitle: function() {
		var self = this;
		if (this.options.titleElement) {
			this.options.titleElement.fadeOut(250, function() {
				$(this).text(self.getCurrentItem().data.title);
				$(this).fadeIn();
			});
		}
	},

	updateMoreLink: function() {
		var self = this;
		var $link = this.options.moreLink;
		if ($link) {
			if (this.more_link_visible == true) {
				$link.fadeOut(250, function() {
					self.more_link_visible = false;
					if (url = self.getCurrentItem().data.details_url) {
						$link.attr('href', url);
						$link.fadeIn();
						self.more_link_visible = true;
					}
				});
			} else {
				setTimeout(function() {
					if (url = self.getCurrentItem().data.details_url) {
						$link.attr('href', url);
						$link.fadeIn();
						self.more_link_visible = true;
					}
				}, 250);
			}
		}
	},

	getCurrentItem: function() {
		return this.items[this.current_item_index];
	},

	getPreviousItem: function() {
		return this.items[this.previous_item_index];
	},

	shuffleItems: function() {
		if (this.items.length > 5) {
			var current = $(_.sortBy(this.viewContainer().find('li'), function(li) { return $(li).position().left; })).index(this.getCurrentItem().viewContainer()) + 1;
			var count = this.items.length;
			var before = current - 1;
			var after = count - current;
			if (before < after) {
				this.shuffleLast((after - before) / 2);
			} else {
				this.shuffleFirst((before - after) / 2);
			}
		}
	},

	shuffleFirst: function(count) {
		var count = Math.ceil(count);
		var items = _.sortBy(this.viewContainer().find('li'), function(li) { return $(li).position().left; });
		var first = $(items.slice(0, count));
		var last = $(_.last(items));
		var last_position = last.position().left + last[0].item_instance.width + 20;
		first.each(function() {
			$(this).css('left', last_position);
			last_position += this.item_instance.width + 20
		});
	},

	shuffleLast: function(count) {
		var count = Math.ceil(count);
		var items = _.sortBy(this.viewContainer().find('li'), function(li) { return $(li).position().left; });
		var first = $(_.first(items));
		var last = $(items.slice(-count));
		var first_position = first.position().left;
		$($.makeArray(last).reverse()).each(function() {
			$(this).css('left', first_position - this.item_instance.width - 20);
			first_position = $(this).position().left;
		});
	},

	nextItem: function() {
		this.previous_item_index = this.current_item_index;
		if (this.current_item_index == this.items.length - 1) {
			this.current_item_index = 0;
		} else {
			this.current_item_index += 1;
		}
		this.shuffleItems();
		this.getPreviousItem().makeInactive();
		this.getCurrentItem().makeActive();
		this.scrollToCurrent()
	},

	previousItem: function() {
		this.previous_item_index = this.current_item_index;
		if (this.current_item_index == 0) {
			this.current_item_index = this.items.length - 1;
		} else {
			this.current_item_index -= 1;
		}
		this.shuffleItems();
		this.getPreviousItem().makeInactive();
		this.getCurrentItem().makeActive();
		this.scrollToCurrent()
	},

	jumpTo: function(index, force) {
		var self = this;
		if (force || index != this.current_item_index) {
			this.previous_item_index = this.current_item_index;
			this.current_item_index = index;
			this.shuffleItems();
			this.getPreviousItem().makeInactive();
			this.getCurrentItem().makeActive();
			this.scrollToCurrent();
		}
	}
});

CarouselItem = $.klass({
	initialize: function(carousel, url, width, height, data) {
		this.carousel = carousel;
		this.url = url;
		this.width = parseInt(width);
		this.height = parseInt(height);
		this.data = data ? data : {};
	},

	viewContainer: function() {
		if (this.view_container) {
			return this.view_container;
		} else {
			var $container = $('<li />', {
				css: {
					width: this.width,
					height: this.height,
					opacity: this.carousel.options.inactiveOpacity
				}
			});
			$container[0].item_instance = this;
			this.view_container = $container;

			return $container;
		}

	},

	render: function() {
		var self = this;
		$container = this.viewContainer();

		var $image = $('<img />', {
			src: this.url,
			width: this.width,
			height: this.height,
			alt: this.data.title
		});

		if (this.carousel.options.linkImages) {
			var $wrapper = $('<a />', {
				'href': self.data.details_url,
				'class': 'inner'
			});
			$wrapper.hover(function() {
				self.carousel.options.moreLink.addClass('hover');
			}, function() {
				self.carousel.options.moreLink.removeClass('hover');
			});
		} else {
			var $wrapper = $('<div />', {
				'class': 'inner'
			});
		}

		$wrapper.append($image);
		$container.empty().append($wrapper);

		this.image = $image;

		return $container;
	},

	makeActive: function(callback) {
		this.viewContainer().animate({ opacity: 1 }, 500, callback);
		this.viewContainer().addClass('active');
	},

	makeInactive: function(callback) {
		this.viewContainer().animate({ opacity: this.carousel.options.inactiveOpacity }, 500, callback);
		this.viewContainer().removeClass('active');
	}
});

ImageCarouselItem = $.klass(CarouselItem, {

});

VimeoCarouselItem = $.klass(CarouselItem, {
	initialize: function($super, carousel, url, width, height, data) {
		this.video_id = data.vimeo_id;
		$super(carousel, url, width, height, data);
	},

	render: function($super) {
		var self = this;
		$container = $super();
		$container.addClass('video');
		$play = $('<a />', {
			'class': 'play',
			text: 'Play'
		});
		$play.bind('click', function() { self.embedVimeo(); });
		$container.find('.inner').append($play);

		return $container;
	},

	makeInactive: function($super, callback) {
		if (this.vimeo_embedded) this.video_object.api("api_pause");
		$super(callback);
	},

	embedVimeo: function() {
		this.viewContainer().unbind('click');

		var video_id = this.video_id;
		var $wrapper = $('<div />');

		var iframe_html = sprintf('<iframe src="http://player.vimeo.com/video/%s?portrait=0&autoplay=1&js_api=1&js_swf_id=vimeo_%s" width="%s" height="%s" frameborder="0" id="vimeo_%s"></iframe>', video_id, video_id, this.width, this.height, video_id);

		$wrapper.append(iframe_html);
		this.viewContainer().empty().append($wrapper);

		this.video_object = $wrapper.find('iframe')[0];
		this.vimeo_embedded = true;
		Froogaloop.init([this.video_object]);
	}
});

