(function($){
    $.fn.mediascroll = function(options) {
        // support mutltiple elements
        if (this.length > 1){
            this.each(function() { $(this).mediascroll(options) });
            return this;
        }
		
        /** DEFAULT OPTIONS */
        var d_o = {
			showNextPrevButtons: false,
			nextPrevButtonsUsingClass: false,
			/** AN ARRAY OF ELEMENTS TO USE AS INDEXING BUTTONS */
			externalNav: null,
			/** IF BUTTONS ARRAY ( externalNav ) WE CAN SET STATE ON BUTTONS */
			externalNavState: { show : false, child : false },
			/** SET STATE ON BUTTONS WITH THESE CLASSES */
			mouseWheel: false,
			auto: null,
			speed: 200,
			easing: null,
			vertical: false,
			circular: true,
			showProgress: false,
			/** CAROUSEL WILL GENERATE THE DIV ID LIKE THIS id="o.id_prefix+'_progressbar'" AND id="o.id_prefix+'_progress'" */
			id_prefix: null,
			pauseable: false,
			/** SHOW FOCUS IS AN ALPHA .05 BLACK DIV THAT IS SET TO DISPLAY NONE ON MOUSE ENTER */
			showFocus: false,/** CAROUSEL WILL GENERATE THE DIV ID LIKE THIS '#'+o.id_prefix+'_focus' */
			visible: 1,
			start: 0,
			readURL: false,/** get the URL parameter carousel= and set parmeter as start index*/
			scroll: 1,
			beforeStart: null,
			afterEnd: null
        };
		
		/** SET OPTIONS PASSED IN */
		var o = $.extend({}, d_o, options);

        /** SETUP PRIVATE VARS */
        var mediascroll = this;
        var animating = false;
        var animCss = o.vertical?"top":"left";
        var sizeCss = o.vertical?"height":"width";
        var div = $(this);
        var ul = $("ul", div);
        var tLi = $("li", ul);
        var tl = tLi.size();
        var v = o.visible;
		var bar = '';
		var progressWidth;
		var run = null;
		var rem;
		var progress = null;
		var elapsed = 0;
		var carouselregex = new RegExp( o.id_prefix + "=[0-9]" );
		var carouselURL = document.URL;
		/** CONSTANTS FOR STATE */
		var RUNNING = "running";
		var STOPPED = "stopped";
		/** STATE */
		var state = RUNNING;
		var newIndex = null;
		if( o.auto != null || o.auto != undefined ){
			div.data('auto', o.auto+o.speed);
			div.data('time', (new Date).getTime() + div.data('auto'));
		}

		/** STOP CAROUSEL ROTATION IF PAUSE ON MOUSE OVER SET */
		if(o.pauseable || o.showFocus){
			ul.mouseenter(function(){
				if(o.pauseable && o.auto){
					if( state == RUNNING ){
						clearInterval(run);
						/** STOP PROGRESS BAR ANIMATION */
						if( o.showProgress == true ){
							pauseProgress();
						}
						/** CALCULATE REMAINING TIME FOR SLIDE AT FOR RESUME */
						var time_rem = div.data('time') - (new Date).getTime();
						if( time_rem > 0 ){
							rem =  time_rem;
						}else{
							rem = 10;
						}
					}
				}
			});
			ul.mouseleave(function(){
				if(o.pauseable && o.auto){
					if( state == RUNNING ){
						/** PASS REMAINING TIME FOR SLIDE TO RESUME AND TO RUN */
						if( o.showProgress == true ){
							resumeProgress(rem)
						}
						run = setInterval(function() { go(curr+o.scroll); }, rem);
					}
				}
				if(o.showFocus == true){
					$( '#'+o.id_prefix+'_focus' ).css('display', 'block');
				}
			});
		}
		/** BE CAREFULL WHAT YOU CLONE! JQUERY CAN'T CLONE HTML THAT IS NOT VALID */
        if(o.circular) {
            ul.prepend(tLi.slice(tl-v-1+1).clone())
              .append(tLi.slice(0,v).clone());
            o.start += v;
        }
		/**	SOME VARIABLES CAN ONLY BE SET AFTER THE CLONE */
        var li = $("li", ul);
        var itemLength = li.size();
		/** IF READ URL SET MEDIA TO START AT THAT INDEX */
		if(o.readURL){
			if( carouselregex.test( carouselURL ) ){
				newIndex = parseInt( carouselURL.substring( carouselURL.indexOf( 'carousel=' ) + 9, carouselURL.indexOf( 'carousel=' ) + 10 ));
				/** ADJUST FOR CLONED LI IF CIRCULAR CLONES. CAN ONLY BE SET AFTER SIZE OF LI SET */
                if(o.circular){
                	if( newIndex == 0 ){
                		/** DO NOTHING START DEFAULTS TO 1 */	
                	}else if(newIndex == 1){
                		o.start = 2;
                	}else if( newIndex >= (itemLength-2) ){
                		/** NOT VALID - IGNORE */
                		newIndex = null;
                	}else{
                		o.start = newIndex + 1;
                	}
                }else{
                	if( newIndex >= (itemLength) ){
                		/** NOT VALID - IGNORE */
                		newIndex = null;
                	}else{
                		o.start = newIndex;
                	}
                }
            }
		}
        var curr = o.start;
        if(!o.circular) {
        	setPrevNextState();
        }
        div.css("visibility", "visible");
        li.css({overflow: "hidden", float: o.vertical ? "none" : "left"});
		ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
		div.css({overflow: "hidden", position: "relative", "z-index": "2", left: "0px"});

        var liSize = o.vertical ? height(li) : width(li);   // Full li size(incl margin)-Used for animation
        var ulSize = liSize * itemLength;                   // size of full ul(total length, not just for the visible items)
        var divSize = liSize * v;                           // size of entire div(total length for just the visible items)

        li.css({width: li.width(), height: li.height()});
        ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));

        div.css(sizeCss, divSize+"px");                     // Width of the DIV. length of visible images

		if( o.showNextPrevButtons == true ){
			if(o.nextPrevButtonsUsingClass == true){
	            $( '.'+o.id_prefix+'_prev' ).click(function() {
	                return go(curr-o.scroll);
	            });
	            $( '.'+o.id_prefix+'_next' ).click(function() {
	                return go(curr+o.scroll);
	            });					
			}else{
	            $( '#'+o.id_prefix+'_prev' ).click(function() {
	                return go(curr-o.scroll);
	            });
	            $( '#'+o.id_prefix+'_next' ).click(function() {
	                return go(curr+o.scroll);
	            });	
            }	
		}

        if(o.externalNav){
            $.each(o.externalNav, function(i, val) {
                $(val).click(function(event) {
                    return go(o.circular ? o.visible+i : i);
                });
            });
		}
        if(o.mouseWheel && div.mousewheel){
            div.mousewheel(function(e, d) {
                return d>0 ? go(curr-o.scroll) : go(curr+o.scroll);
            });
		}
        if(o.auto){
            run = setInterval(function() { go(curr+o.scroll); }, o.auto+o.speed);
			if( o.showProgress == true ){
				bar = "<div id='"+o.id_prefix+"_progressbar'><div id='"+o.id_prefix+"_progress'></div></div>";
				$( '#'+o.id_prefix ).append( bar );
				progressWidth = $( '#'+o.id_prefix ).width() - 8;
				startProgress();
			}
		}
		/** ADD THE ACTIVE STATE TO FIRST NAV IMAGE */
		if( o.externalNavState.show == true ){
			if(o.readURL && newIndex){
				if( o.externalNavState.child == true ){
					$( o.externalNav[newIndex].children[0] ).addClass( o.id_prefix+'_active' );
				}else{
					$( o.externalNav[newIndex] ).addClass( o.id_prefix+'_active' );
				}
			}else{
				if( o.externalNavState.child == true ){
					$( o.externalNav[0].children[0] ).addClass( o.id_prefix+'_active' );
				}else{
					$( o.externalNav[0] ).addClass( o.id_prefix+'_active' );
				}
			}
		}
		
		function setPrevNextState(){
			if(!o.circular) {
				if( o.showNextPrevButtons == true ){
					if( o.nextPrevButtonsUsingClass == true ){
						$( '.'+o.id_prefix+'_prev' + ',' + '.'+o.id_prefix+'_next' ).removeClass("disabled");
					}else{
						$( '#'+o.id_prefix+'_prev' + ',' + '#'+o.id_prefix+'_next' ).removeClass("disabled");
					}
					if(curr-o.scroll<0){
						if( o.nextPrevButtonsUsingClass == true ){
							$('.'+o.id_prefix+'_prev').addClass("disabled");
						}else{
							$('#'+o.id_prefix+'_prev').addClass("disabled");
						}								
					}else if(curr+o.scroll > itemLength-v){			
						if( o.nextPrevButtonsUsingClass == true ){
							$('.'+o.id_prefix+'_next').addClass("disabled");
						}else{
							$('#'+o.id_prefix+'_next').addClass("disabled");
						}					
					}					
				}
			}
		}
		
        function vis() {
            return li.slice(curr).slice(0,v);
        };
		function startProgress(){
			$('#'+o.id_prefix+'_progress').animate({'width':progressWidth+'px'}, o.auto, 'linear');
		}
		function resetProgress(){
			$('#'+o.id_prefix+'_progress').stop( true, true );
			$('#'+o.id_prefix+'_progress').css('width','1px');
		}
		function pauseProgress(){
			$('#'+o.id_prefix+'_progress').stop();
		}
		function resumeProgress(val){
			$('#'+o.id_prefix+'_progress').animate({'width':progressWidth+'px'}, val, 'linear');
		}
        function go(to) {
        	state = RUNNING;
            if(!animating) {

                if(o.beforeStart){
                    o.beforeStart.call(this, vis());
				}
                if(o.circular) {            // If circular we are in first or last, then goto the other end
                    if(to<=o.start-v-1) {           // If first, then goto last
                        ul.css(animCss, -((itemLength-(v*2))*liSize)+"px");
                        // If "scroll" > 1, then the "to" might not be equal to the condition; it can be lesser depending on the number of elements.
                        curr = to==o.start-v-1 ? itemLength-(v*2)-1 : itemLength-(v*2)-o.scroll;
                    } else if(to>=itemLength-v+1) { // If last, then goto first
                        ul.css(animCss, -( (v) * liSize ) + "px" );
                        // If "scroll" > 1, then the "to" might not be equal to the condition; it can be greater depending on the number of elements.
                        curr = to==itemLength-v+1 ? v+1 : v+o.scroll;
                    } else curr = to;
                } else {                    // If non-circular and to points to first or last, we just return.
                    if(to<0 || to>itemLength-v) return;
                    else curr = to;
                }                           // If neither overrides it, the curr will still be "to" and we can proceed.

                animating = true;

                ul.animate(
                    animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
                    function() {
                        if(o.afterEnd){
                            o.afterEnd.call(this, vis());
						}
                        animating = false;
                    }
                );
				if(o.externalNavState.show == true){	// Set the active nav element to visited state
					if(o.circular) {
						if( o.externalNavState.child == true ){
							o.externalNav.each(function(i){
								if($(this).children().hasClass( o.id_prefix+'_active' )){
									$(this).children().removeClass(  o.id_prefix+'_active' ).addClass( o.id_prefix+'_visited' );
								}
								if( curr > 0 && curr <= o.externalNav.length ){
									if(i == curr - 1){
										$(this).children().removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
									}
								}else if( curr == 0 && i == o.externalNav.length - 1 ){
									$(this).children().removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}else if( curr == o.externalNav.length + 1 && i == 0 ){
									$(this).children().removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}
							});
						}else{
							o.externalNav.each(function(i){
								if($(this).hasClass( o.id_prefix+'_active' )){
									$(this).removeClass(  o.id_prefix+'_active' ).addClass( o.id_prefix+'_visited' );
								}
								if( curr > 0 && curr <= o.externalNav.length ){
									if(i == curr - 1){
										$(this).removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
									}
								}else if( curr == 0 && i == o.externalNav.length - 1 ){
									$(this).removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}else if( curr == o.externalNav.length + 1 && i == 0 ){
									$(this).removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}
							});
						}
					}else{
						/** NOT CIRCULAR SO NO CLONES */
						if( o.externalNavState.child == true ){
							o.externalNav.each(function(i){
								if($(this).children().hasClass( o.id_prefix+'_active' )){
									$(this).children().removeClass(  o.id_prefix+'_active' ).addClass( o.id_prefix+'_visited' );
								}
								if(i == curr ){
									$(this).children().removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}

							});
						}else{
							o.externalNav.each(function(i){
								if($(this).hasClass( o.id_prefix+'_active' )){
									$(this).removeClass(  o.id_prefix+'_active' ).addClass( o.id_prefix+'_visited' );
								}
								if(i == curr ){
									$(this).removeClass(  o.id_prefix+'_visited' ).addClass(  o.id_prefix+'_active' );
								}

							});
						}						
					}
				}
				/** RE-SET STORED VALUES OF TIME FOR PAUSABLE AND INTERVAL*/
				if( o.auto != null || o.auto != undefined ){
					div.data('time', (new Date).getTime() + div.data('auto'));
					clearInterval(run);
					run = setInterval(function() { go(curr+o.scroll); }, o.auto+o.speed);
					if( o.showProgress == true ){
						resetProgress();
						startProgress();
					}
				}
                /** TODO - Disable buttons when the carousel reaches the last/first, and enable when not */
				setPrevNextState()
            }else{
				ul.stop(true, true);
				if( o.showProgress == true ){
					$('#'+o.id_prefix+'_progress').stop(true, true);
				}
				animating = false;
			}
            return false;
        };

        /** PUBLIC METHODS */
        this.stopScroll = function() {
        	state = STOPPED;
			clearInterval(run);
			resetProgress();
			ul.stop(true, true);
			if( o.showProgress == true ){
				$('#'+o.id_prefix+'_progress').stop(true, true);
			}
			animating = false;
        };
		
        this.startScroll = function() {
            state = RUNNING;
			go(curr);
        };
        
        this.setIndex = function( val ) {
			curr = val;
        };
        
        this.getIndex = function() {
            return curr;
        };

        this.getOptions = function() {
            return o;
        };

        return this;
    }
	
	function css(el, prop) {
		if (el && el.length) {
			return parseInt($.css(el[0], prop)) || 0;
		}
	};
	function width(el) {
		if (el && el.length) {
		   return  el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
		}
	};
	function height(el) {
		if (el && el.length) {
			return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
		}
	};
	
})(jQuery);
