var FZScroller = new Class({
	Implements: [Options, Events],
	
	options: {
		createElements: true,
		className: '',
		mode: 'vertical',
		height: null,
		maxHeight: null,
		wheel: true,
		closable: true,
		closed: false,
		overlay: true,
		scrollerBarGap: 20,
		contentURL: null
	},
	
	elements: {
		scrollerContainer: null,
		scroller: null,
		mask: null,
		content: null,
		scrollbar: null,
		handle: null,
		overlay: null,
		buttonCollapse: null,
		buttonExpand: null
	},
	
	slider: null,
	scrollable: true,
	
	contentLoaded: false,
	
	initialize: function(el, options) {
		this.setOptions(options);
		
		var self = this;
		
		this.addEvents({
			'contentLoaded': function() { self.onContentLoaded(); }
		});
		
		if ( this.options.createElements ) {
			this.createScroller($(el));
		}
		
		// load the scroller content if needed
		if ( this.options.contentURL != null ) {
			this.loadContent();
		} else {
			this.replaceContent();
		}
	},
	
	createScroller: function(parent) {
		var self = this;
		
		this.elements.scrollerContainer = parent;
		this.elements.scrollerContainer.addClass('fz_scroller_container');
		
		var className = 'fz_scroller';
		if ( this.options.overlay ) className += ' fz_scroller_hasoverlay';
		if ( this.options.closable ) className += ' fz_scroller_closable';
		this.elements.scroller = new Element('div', {
			'class': className
		}).inject(this.elements.scrollerContainer);
		
		this.elements.mask = new Element('div', {
			'class': 'fz_scroller_mask'
		}).inject(this.elements.scroller);
		
		this.elements.content = new Element('div', {
			'class': 'fz_scroller_content'
		}).inject(this.elements.mask);
		
		this.elements.scrollbar = new Element('div', {
			'class': 'fz_scroller_scrollbar ' + (this.options.mode == 'vertical' ? 'fz_scroller_scrollbar_v' : 'fz_scroller_scrollbar_h')
		}).inject(this.elements.mask);
		
		this.elements.handle = new Element('div', {
			'class': 'fz_scroller_handle ' + ((this.options.mode == 'vertical') ? 'fz_scroller_handle_v' : 'fz_scroller_handle_h')
		}).inject(this.elements.scrollbar);
		
		if ( this.options.overlay ) {
			this.elements.overlay = new Element('div', {
				'class': 'fz_scroller_overlay png'
			}).inject(this.elements.scroller);
		}
		
		if ( this.options.closable ) {
			this.elements.buttonExpand = new Element('a', {
				'href': '#',
				'class': 'fz_scroller_trigger fz_scroller_expand',
				events: {
					click: function(e) {
						e.preventDefault();
						self.expand();
					}
				}
			}).inject(this.elements.scrollerContainer, 'top');
			
			this.elements.buttonCollapse = new Element('a', {
				'href': '#',
				'class': 'fz_scroller_trigger fz_scroller_collapse',
				events: {
					click: function(e) {
						e.preventDefault();
						self.collapse();
					}
				}
			}).inject(this.elements.scrollerContainer);
		}
	},
	
	initScroller: function() {
		var self = this;
		
		// the set correct height
		this.setDimensions(this.options.height);
			
		// create the slider if needed
		if ( this.scrollable ) {
			if ( Tools.isMobile() ) {
				this.createTouchSlider();
			} else {
				this.createSlider();
			}
		}
		
		// set initial state of scroller
		if ( this.options.closable ) {
			if ( this.options.closed ) {
				this.collapse();
			} else {
				this.expand();
			}
		}
		
		this.fireEvent('ready');
	},
	
	setDimensions: function(height) {
		// get the height of the scrollable content
		var coords = this.elements.content.measure(function() {
			this.setStyle('height', 'auto');
			return this.getCoordinates();
		});
		
		// if content height is greater than the specified scroller height we have a scroller
		if ( height < coords.height ) {
			this.scrollable = true;
			this.elements.scrollerContainer.addClass('fz_scroller_scrollable');
			this.elements.scroller.setStyle('height', height);
			this.elements.mask.setStyle('height', height);
			this.elements.scrollbar.setStyle('height', height);
		}
		else
		{
			// the content fits within the default display area - don't show the scroller
			this.scrollable = false;
			height = coords.height;
			this.elements.scrollerContainer.removeClass('fz_scroller_scrollable');
			this.elements.scroller.setStyle('height', height);
			this.elements.mask.setStyle('height', height);
			this.elements.content.setStyle('height', height);
		}
		
		// for closable scrollers, the scroller bar must be shorter to leave room for the close button
		if ( this.options.closable ) {
			this.elements.scrollbar.setStyle('height', height - this.options.scrollerBarGap);
		}
		
		this.fireEvent('scrollerCreated', this);
		
		return this;
	},
	
	loadContent: function() {
		var self = this,
		
		request = new Request.HTML({
			url: this.options.contentURL,
			method: 'get',
			evalScripts: true,
			useSpinner: true,
			update: this.elements.content,
			onSuccess: function() {
				self.fireEvent('contentLoaded');
			},
			onFailure: function(xhr) { console.log('FZScroller::loadContent() - failure'); },
			onException: function(xhr) { console.log('FZScroller::loadContent() - exception'); }
		}).send();
	},
	
	replaceContent: function() {
		var placeholder = this.elements.scrollerContainer.getChildren('.fz_scroller_placeholder')[0];
		placeholder.inject(this.elements.content);
		this.fireEvent('contentLoaded');
	},
	
	createSlider: function() {
		var self = this,
			mask, steps;
		
		mask = this.elements.mask.measure(function() {
			return [this.getSize(), this.getScrollSize()];
		});
		steps =  (this.options.mode == 'horizontal') ? (mask[1].x - mask[0].x) : (mask[1].y - mask[0].y);
		
		this.slider = new Slider(this.elements.scrollbar, this.elements.handle, {
			steps: steps,
			mode: this.options.mode,
			wheel: this.options.wheel,
			onChange: function(step) {
				// scrolls the content of the mask in x or y direction.
				if ( this.options.mode == 'horizontal' ) {
					this.elements.mask.scrollTo(step, 0);
				} else {
					this.elements.mask.scrollTo(0, step);
				}
			}.bind(this)
		}).set(0);
		
		// scroll the content element when the mousewheel is used within the content or the scrollbar element
		if ( this.options.wheel ) {
			this.elements.mask.addEvent('mousewheel', function(e) {
				e = new Event(e).stop();
				var step = self.slider.step - e.wheel * 30;
				self.slider.set(step);
			});
		}
		
		// stop the handle dragging process when the mouse leaves the document body
		$(document.body).addEvent('mouseleave', function() { self.slider.drag.stop(); });
	},
	
	createTouchSlider: function() {
		var self = this;
		$script.ready('mobile', function() {
			self.elements.scrollbar.destroy();
			self.elements.mask.addEvent('touchmove', function (e) { e.preventDefault(); });

			// initialize the slider within measure() so that we can do it even while the scroller is collapsed
			self.elements.mask.measure(function() {
				this.slider = new iScroll(this.elements.mask, {
					desktopCompatibility: true,
					fadeScrollbar: false,
					hideScrollbar: false,
					scrollbarClass: 'fz_scroller_scrollbar fz_scroller_scrollbar_'
				});
				this.slider.scrollTo(0,0,0);
			}.bind(self));
		});
	},
	
	removeSlider: function() {
		if ( Tools.isMobile() ) {
			this.slider.destroy();
		} else {
			this.slider.detach();
			this.slider = null;
			this.elements.mask.removeEvents('mousewheel');
			$(document.body).removeEvents('mouseleave');
		}
	},
	
	resetSlider: function() {
		// this is ugly.  MooToolsMore 1.3.1 has an autosize() method that lets us resize the scroller on the fly.  for now we have to recreate it
		if ( this.slider ) {
			this.removeSlider();
		}
		
		if ( !this.scrollable ) return;
		
		if ( Tools.isMobile() ) {
			this.createTouchSlider();
		} else {
			this.createSlider();
		}
	},
	
	expand: function() {
		this.elements.scrollerContainer.removeClass('fz_scroller_closed');
		this.fireEvent('expand');
	},
	
	collapse: function() {
		this.elements.scrollerContainer.addClass('fz_scroller_closed');
		this.fireEvent('collapse');
	},
	
	resetScroller: function() {
		if ( this.slider ) this.slider.detach();
		this.initialize(this.scroller);
	},
	
	onContentLoaded: function() {
		this.contentLoaded = true;
		this.initScroller();
	}
});

GlobalNavScroller = new Class({
	Extends: FZScroller,
	
	initialize: function(el, options) {
		var self = this;
		this.addEvents({
			scrollerCreated: function() { self.customizeScroller(); },
			expand: function() { self.onExpand(); },
			collapse: function() { self.onCollapse(); }
		});
		this.parent(el, options);
	},
	
	customizeScroller: function() {
		// set the height of the scroller to be equal to that of the other columns in the menu
		this.elements.submenu = this.elements.scrollerContainer.getParent();
		
		var self = this;
		var height = this.elements.scrollerContainer.measure(function() {
			var height = self.elements.submenu.getStyle('height').toInt() - self.elements.scrollerContainer.getStyle('top').toInt();
			self.elements.scroller.setStyle('height', height);
			self.elements.mask.setStyle('height', height);
			
			// shorten the track by its vertical offset
			var scrollbarHeight = height - self.elements.scrollbar.getStyle('top').toInt() - self.options.scrollerBarGap;
			self.elements.scrollbar.setStyle('height', scrollbarHeight);
			
			return height;
		});
		this.elements.scrollerContainer.setStyle('height', height);
		
		// the expand button for this scroller is external to the scroller - destroy the existing button
		if ( this.elements.buttonExpand ) this.elements.buttonExpand = this.elements.buttonExpand.destroy();
	},
	
	onExpand: function() {
		// we can't just hide the regular submenu because we need the title to remain visible from under the scroller.
		// however, the submenu is longer than the scroller so it needs to be shortened so that it doesn't poke out from the bottom of the scroller.
		// the view_all_products link is absolutely positioned so it needs to be hidden.
		var height = this.elements.submenu.measure(function() {
			return this.getStyle('height').toInt();
		});
		var column = this.elements.submenu.getChildren('ul')[0];
		column.setStyle('height', height - 100);
		column.getChildren('.view_all_products').hide();
		this.elements.scrollerContainer.show();
	},
	
	onCollapse: function() {
		this.elements.scrollerContainer.hide();
		var height = this.elements.submenu.measure(function() {
			return this.getStyle('height').toInt();
		});
		var column = this.elements.submenu.getChildren('ul')[0];
		column.setStyle('height', height);
		column.getChildren('.view_all_products').show();
	}
});



var FZMultiScroller = new Class({
	Implements: [Options, Events],
	
	scrollers: [],
	
	initialize: function(scrollers) {
		var self = this;
		scrollers.each(function(scroller){
			self.initScroller(scroller);
		});
	},
	
	initScroller: function(scroller) {
		var self = this;
		
		scroller.elements.scrollerContainer.addClass('fz_multiscroller');
		if ( scroller.scrollable ) {
			scroller.elements.scrollerContainer.addClass('fz_multiscroller_scrollable');
		}
		
		// replace the click events on the expand and collapse buttons
		scroller.elements.buttonExpand.removeEvents('click').addEvent('click', function(e) {
			e.preventDefault();
			self.expand(scroller);
		});
		
		scroller.elements.buttonCollapse.removeEvents('click').addEvent('click', function(e) {
			e.preventDefault();
			self.collapse(scroller);
		});
		
		// add events to the scrollers and save them to an array
		scroller.addEvents({
			expand: function() {
				this.elements.scrollerContainer.removeClass('fz_multiscroller_closed');
			},
			collapse: function() {
				this.elements.scrollerContainer.addClass('fz_multiscroller_closed');
			},
			scrollerCreated: function() {
				if ( this.scrollable ) {
					this.elements.scrollerContainer.addClass('fz_multiscroller_scrollable');
				} else {
					this.elements.scrollerContainer.removeClass('fz_multiscroller_scrollable');
				}
			}
		});
		
		this.scrollers.push(scroller);
	},
	
	expand: function(scroller) {
		var self = this;
		
		// collapse all scrollers
		this.scrollers.each(function(scroller) {
			scroller.collapse();
		});
		
		// expand the chosen scroller
		scroller.elements.scrollerContainer.addClass('fz_multiscroller_max');
		scroller.setDimensions(scroller.options.maxHeight);
		scroller.resetSlider();
		scroller.expand();
	},
	
	collapse: function() {
		var self = this;
		
		// reset all scrollers to their initial state
		this.scrollers.each(function(scroller) {
			scroller.elements.scrollerContainer.removeClass('fz_multiscroller_max');
			scroller.setDimensions(scroller.options.height);
			scroller.resetSlider();
			scroller.expand();
		});
	}
});

