(function($){
	var options;
	
	$.fn.slideThis = function( options ){
		options = $.extend({
			slideTime : 1500,					// how long for each slide
			slideFXTime : 500,					// how long transition should be
			classSlideBox : "slideContainer",
			classSlide : "slideClass",
			classIndex : "slideIndex",			// 1-n controls
			classIndexOn : "on",				// a control is "on"
			classButtons : "slideButtons",		// next, prev, play/pause buttons list wrapper
			classNext : "slideNext",
			classPrev : "slidePrev",
			classPlay : "slidePlay",
			classPlayOn : "slidePlayOn",
			classPause : "slidePause",
			makePlayPause : false,				// does the play button become the pause button?
			autoPlay : false,					// automatically play (may be a number also?)
			transitionType : 'fade',			// fade; slide
			grouping : 1,						// for transitionType "slide" = how many to slide over at a time; groups of 2, 3, ?
			oneWay : false,						// only play one direction, do not loop TODO: GET THIS WORKING
			showing : false,					// how many are actually showing through slide portal
												// 			ONLY WORKS WITH GROUPING === 1 right now!!
			// event methods, executed in this order!
			onBeforeHide : function() {	},		// event fired prior to existing slide moving
			onAfterHide : function() { },		// event fired after existing slide moves (only available for transitionType = 'fade)
			onBeforeNew : function() { },		// event fired after existing slide moves before new slide comes in (only available for transitionType = 'fade)
			onAfterNew : function() { }			// event fired after new slide comes in
		}, options || {});

		function putLI(liClass, liText){
			var xClass = ( $.trim(liClass) != '' ) ? " class='" + liClass + "'" : "";
			return '<li' + xClass + '>' + liText + '</li>';
		}

		function buildNav(slideshow, idx, slides){ // TODO: slides and idx aren't needed?
			// build buttons / index
			$(slideshow)
				.append('<ul class="' + options.classIndex + '" />')
				.append('<ul class="' + options.classButtons + '"/>');
			$("." + options.classSlide + " > li", slideshow).each(
				function(i) {
					if ( options.grouping === 1 ) {
						// @TODO: options.showing needs more robust support than this
						if ( options.showing !== false || typeof options.showing === "number" ) {
			// console.log("here 1")
							if ( i < options.showing) {
			// console.log("here 2")
								$("." + options.classIndex, slideshow)
									.append( putLI('', '<a href="#">x' + (i+1) + '</a>') );
							} 
						} else {
			// console.log("here 3")
							$("." + options.classIndex, slideshow)
								.append( putLI('', '<a href="#">' + (i+1) + '</a>') );
						}
					}
					if ( i != slideshow.index && options.transitionType != 'slide' ) {
						if ( options.grouping !== 1 ) {
							if ( i > options.grouping) $(this).hide();
						} else {
							$(this).hide();
						}
					}
				}
			);
			// build groupings indexes differently
			if ( options.grouping !== 1 && options.transitionType === 'slide' ) {
				// var howMany = (options.showing !== false) ? options.showing : parseInt($("." + options.classSlide + " li", slideshow).size() / options.grouping);
				var howMany = parseInt($("." + options.classSlide + " > li", slideshow).size() / options.grouping);
				var z = 0;
				var indexes = $("." + options.classIndex, slideshow);
				while ( z < howMany ) {
					indexes.append( putLI('', '<a href="#">' +  (z+1) + '</a>'));
					z++;
				}
			}
			// buttons
			$("." + options.classButtons, slideshow)
				.append( putLI(options.classPrev, '<a href="#">Previous</a>') )
				.append( putLI(options.classNext, '<a href="#">Next</a>') )
				.append( putLI(options.classPlay, '<a href="#">Play</a>') );
			// turn one on
			$("." + options.classIndex + " a", slideshow)
				.eq(slideshow.index)
					.addClass(options.classIndexOn);
			// add events
			$("." + options.classIndex + " a", slideshow).click(
				function(e){
					// slideshow.moving = true;
					var targ = $("." + options.classIndex + " a", slideshow).index(e.target);
					if ( targ == slideshow.index ) {
						checkPlay( slideshow );
						e.preventDefault();
						return true;
					} else {
						stop( slideshow );
						checkPlay( slideshow );
						move( slideshow, true, targ);
						e.preventDefault();
					}
					if ( noPlayButton( slideshow ) === true ) {
						makePausePlay( slideshow );
					}
					checkPlay( slideshow );
				}
			)
			$("." + options.classPrev + " a", slideshow).click( 
				function(e){ 
					stop( slideshow );
					checkPlay( slideshow );
					if ( noPlayButton( slideshow ) === true ) {
						makePausePlay( slideshow );
					}
					move( slideshow, false );
					e.preventDefault()
				} );
			$("." + options.classNext + " a", slideshow).click( 
				function(e, eType){  
					if ( eType !== 'auto') {
						checkPlay( slideshow )
						if ( noPlayButton( slideshow) === true ) {
							makePausePlay( slideshow );
						}
					}
					stop( slideshow );
					move( slideshow, true )
					e.preventDefault()
				}
			);
			// do we have a distinct play button?
			if ( options.makePlayPause !== true ) {
				$("." + options.classButtons, slideshow)
					.append( putLI(options.classPause, '<a href="#">Pause</a>') );
				bindPause( slideshow );
				bindPlay( slideshow );
			// if, make play into pause when clicked
			} else {
				bindPlay( slideshow );
			}
			if ( options.autoPlay ) {
				$("." + options.classPlay, slideshow).trigger('click').addClass(options.classPlayOn);
			}
		}
		
		function bindPlay(whichShow){
			$("." + options.classPlay + " a", whichShow).click(
				function boundPlay(e){
					$(this).addClass(options.classPlayOn);
					start( whichShow );
					if ( options.makePlayPause === true ) {
						makePlayPause( whichShow );
					}
					e.preventDefault();
			} );
		}
		
		function makePlayPause(whichShow){
			$("." + options.classPlay + " a", whichShow).html("Pause")
				.unbind('click')
				.removeClass(options.classPlayOn)
				.parent()
				.removeClass(options.classPlay)
				.addClass(options.classPause);
			bindPause( whichShow );
		}

		function makePausePlay(whichShow){
			$("." + options.classPause + " a", whichShow ).html("Play")
				.unbind('click')
				.parent()
				.removeClass(options.classPause)
				.addClass(options.classPlay);
			bindPlay( whichShow );
		}

		function noPlayButton(whichShow){
			var x = $("." + options.classPlay + " a", whichShow).size();
			if ( x > 0 ) {
				return false;
			} else {
				return true;
			}
		}
		
		function bindPause(whichShow){
			$("." + options.classPause + " a", whichShow).click(
				function boundPause(e){ 
					checkPlay( whichShow );
					stop( whichShow );
					if ( options.makePlayPause === true && noPlayButton( whichShow ) === true ) {
						makePausePlay( whichShow );
					}
					e.preventDefault();
				} );
		}
		
		function checkPlay(whichShow) {
			var button = $("." + options.classPlay + " a", whichShow);
			if ( button.hasClass(options.classPlayOn) === true ) {
				button.removeClass(options.classPlayOn);
			}
			return true;
		}
		
		function setNav(whichShow, idx){
			$(whichShow)
				.find("." + options.classIndex + " a")
					.removeClass( options.classIndexOn )
				.end()
				.find("." + options.classIndex + " a")
					.eq(idx)
					.addClass( options.classIndexOn );
		}
		
		function setIndex(whichShow, bit){ // which index to show
			if (typeof bit == "boolean") {
				if (bit) {
					whichShow.index++;
					if ( options.grouping > 1 ) {
						if ( parseInt(whichShow.index) >= parseInt(whichShow.items.length / options.grouping) ) {
							whichShow.index = 0;
						} else { // not needed?
							whichShow.index = whichShow.index;
						}
					} else if ( whichShow.index > whichShow.items.length-1 ) {
						whichShow.index = 0;
					}
				} else {
					whichShow.index--;
					if ( whichShow.index < 0 ) {
						if ( options.grouping > 1 ) {
							whichShow.index = (whichShow.items.length / options.grouping)-1
						} else {
							whichShow.index = whichShow.items.length-1;
						}
					}
				}
			} else {
				whichShow.index = bit;
			};
		}

		function nextIndex(whichShow, bit){
			var x = 0;
			if (bit) {
				x = whichShow.index + 1;
				if ( options.grouping > 1 ) {
					if ( x >= options.grouping ) {
						x = 0;
					}
				}
				if ( x > whichShow.items.length-1 ) {
					x = 0;
				}
			} else {
				x = whichShow.index-1;
				if ( options.grouping > 1 ) {
					if ( x < 0 ) {
						x = (whichShow.items.length / options.grouping)-1;
					}
				}
				if ( x < 0 ) {
					x = whichShow.items.length-1;
				}
			}
			return x;
		}
		
		function start(whichShow){
			if ( whichShow.handle != null ) {
				$("." + options.classNext + " a", whichShow).trigger('click','auto');
			}
			whichShow.handle = window.setTimeout(
				function(){
					start( whichShow );
				}, options.slideTime)
		}

		function stop(slideshow){
			window.clearTimeout( slideshow.handle )
		}
		
		function getMargin(directness, oldslide, curslide, wideness) {
			var slideThisFar = 0;
			var old = oldslide;
			var cur = curslide;
			var whichWay = "forward";
			if (directness === true) { // we need to go to a certain slide
				if (old > cur) {
					slideThisFar = ((old - cur)*wideness)*options.grouping;
					whichWay = "back";
				} else if (old < cur) {
					whichWay = "forward";
					slideThisFar = ((cur - old)*wideness)*options.grouping;
				} else {
					whichWay = "none"
					slideThisFar = wideness;
				}
			} else if ( directness === -1 ) {	// todo: fix this so it's more efficient; why is all this the same as Above?
				whichWay = "back";
				if (old < cur) {
					slideThisFar = ((cur - old)*wideness)*options.grouping;
				} else if (old > cur) {
					slideThisFar = ((old - cur)*wideness)*options.grouping;
				} else {
					slideThisFar = wideness;
				}
			} else {
				slideThisFar = wideness;
			}
			var xyz = ( ((directness === 1) ? "-=" : (whichWay === "forward") ? "-=" : "+=") + slideThisFar + "px");
			return xyz;
		}

		function move(whichShow, tf, go){
			// FIXME: try to bounce out and re-queue if an animation is in progress
			if ( whichShow.sliding != null ) {
				setTimeout(function(){
					move( whichShow, tf, go );
				}, options.slideTime)
				return;
			}
			// get next index or set to passed index
			var goTo = (typeof go != "undefined") ? go : nextIndex(whichShow, tf);
			
			if ( goTo == window.sliding ) { // FIXME: this looks like a bug!!
				return false;
			} // else if ( goTo === false ) {  // TODO: this is for options.oneWay (broken)
			// 				stop ( whichShow );
			// 				console.log("xxxxx")
			// 				return false;
			// 			}

			whichShow.sliding = goTo;
			setNav( whichShow, goTo );
			
			if ( options.transitionType === 'fade' ) {
				options.onBeforeHide(whichShow);
				$(whichShow.items).eq(whichShow.index).fadeOut(options.slideFXTime,function(){
					options.onAfterHide(whichShow);
					options.onBeforeNew(whichShow);
					$(whichShow.items).eq(goTo).fadeIn(options.slideFXTime,function(){
						if ( go != undefined ) {
							setIndex(whichShow, goTo);
						} else {
							setIndex(whichShow, tf);
						}
						whichShow.sliding = null;
						options.onAfterNew(whichShow);
						return;
					});
				});
			} else if ( options.transitionType === 'slide' ) {
				var setWidth = $("." + options.classSlide + " > li", whichShow).eq(0).outerWidth();
				var setMargin = 0;
				var direct = ( goTo > whichShow.index ) ? true : -1;
					direct = ( goTo == whichShow.index ) ? false : direct;
				
				setMargin = getMargin( direct, whichShow.index, goTo, setWidth );
				
				if ( go != undefined ) {
					setIndex( whichShow, goTo );
				} else {
					setIndex( whichShow, tf );
				}
				options.onBeforeHide(whichShow);
				$("." + options.classSlide, whichShow).eq(0).animate({
					marginLeft: setMargin
				},options.slideFXTime,function(){
					whichShow.sliding = null;
					options.onAfterNew(whichShow);
				});
			
			} else {
				// console.error("no valid transition type");
			}
		}

		return this.each(function(i){
			// $.fn.slideThis.count++; 							// this is global for all slideshows on the page; might be many
			// 	var count = $.fn.slideThis.count;				// use a convenience var
			// $.fn.slideThis.cache[count] = this; 				// set this item in the cache to the specific instance of the slide show
			// 	var cacheItem = $.fn.slideThis.cache[count];	// use a convenience var

			$.fn.slideThis.cache[options.classSlideBox] = this;
				var cacheItem = $.fn.slideThis.cache[options.classSlideBox];

			cacheItem.index = 0;								// set start point for this slideshow
			cacheItem.sliding = null;
			cacheItem.handle = null;
			cacheItem.items = $(this).find("." + options.classSlide + " > li");	// collect slides

			// @todo showing of x numbers does not work w/ groups (not sure about transitionType : "fade" either)
			if ( typeof options.showing === "number" ) {
				cacheItem.items = $(cacheItem.items).filter("li:lt(" + options.showing + ")");
			}
			
			buildNav(this, i);
		});
	}
	$.fn.slideThis.count   = -1; // start w/ negative index so it's incremented the first time to 0
	$.fn.slideThis.cache   = []; // create cache of instances
	
})(jQuery);


