(function( $ ){
    
    $.fn.edSlideshow = function(options) {
        
        // Default settings, overwritten by user
        var defaults = {
            slideshowPlay: true,
            preloading: true,
            slideDelay: 8000,
            buildControls: true,
            slideshow: true,
            thumbWidth: 50,
            slideshowClass: 'slideshow',
            wrapperClass: 'slideshow_wrapper',
            controlWrapper: 'control_wrapper',
            controlsClass: 'slideshow_controls',
            parentClass: 'slideshow',
            controlNudgeClass: 'controlNudge',
            controlClass: 'ctrl',
            slideClass: 'slide',
            speed: 400,
            visibleControls: 6,
            transition: 'fade',
            prevText: 'prev',
            nextText: 'next',
            orientation: 'horizontal'
        },
        settings,
        $slideshows,
        controls,
        control,
        nudger,
        wrapper;
        

        return this.each(function(){
                
                var
                $slideshow = $(this);
                
                $slideshow.settings = $.extend( defaults, options );
                $slideshow.controls = '<ul class="'+$slideshow.settings.controlsClass+'" />';
                $slideshow.wrapper = '<div class="'+$slideshow.settings.wrapperClass+'" />';
                $slideshow.control = '<li class="'+$slideshow.settings.controlClass+'" />';
                $slideshow.nudger = 
                    '<div class="'+$slideshow.settings.controlNudgeClass+' direction">'+
                        '<a href="#direction" class="direction">direction</a>'+
                    '</div>';
                $slideshow.addClass($slideshow.settings.slideshowClass);

                // Constants of a sort
                $slideshow.animating = false;
                $slideshow.slides = $slideshow.find('li');
                $slideshow.wrapper = null;
                $slideshow.currentSlide = $slideshow.slides.first();
                $slideshow.totalSlides = $slideshow.slides.length;
                $slideshow.animating = false;
                $slideshow.btnPrev = null;
                $slideshow.btnNext = null;
                $slideshow.positions = new Array();
                $slideshow.slideshowWidth = null;
                $slideshow.parentDiv = $slideshow.closest('div.'+$slideshow.settings.parentClass);
                $slideshow.visibleControls = 0;
                                
                // Hide other slides
                $slideshow.slides.filter(':gt(0)').hide();
                
                // Create a wrapper if one doesn't exist
                $wrapper = $slideshow.parentDiv.find('div.'+$slideshow.settings.wrapperClass);
                var madeWrapper = false;
                if ( $wrapper.length === 0) {
                    $wrapper = $(wrapper);
                    madeWrapper = true;
                }
                                
                // Use controls if there are any, otherwise build them
                $slideshow.controls = $slideshow.parentDiv.find('ul.'+$slideshow.settings.controlsClass);
                if ( $slideshow.controls.length === 0 && $slideshow.slides.length > 1) {
                    $slideshow.controls = buildControls($slideshow);
                }
                $slideshow.controlWrapper = $slideshow.controls.parent('div.control_wrapper');


                // How many controls can we show at one time?
                if ($slideshow.settings.orientation == 'horizontal') {
                    $slideshow.visibleControls = Math.floor( ($slideshow.controlWrapper.width() / $slideshow.controls.find('li').outerWidth(true)));
                } else {
                    $slideshow.visibleControls = Math.floor(($slideshow.controlWrapper.height() / $slideshow.controls.find('li:first').outerHeight(true)));
                }
                
                // Use nudgers if there are any otherwise build them
                $slideshow.controlNudge = $slideshow.parentDiv.find('div.'+$slideshow.settings.controlNudgeClass+' a');
                if ( 
                    $slideshow.visibleControls < $slideshow.slides.length 
                    && $slideshow.controlNudge.length < 2 
                ) {
                    buildNudgers($slideshow);
                    $slideshow.controlNudge = $slideshow.parentDiv.find('div.'+$slideshow.settings.controlNudgeClass+' a');
                }

                // If we generated the wrapper we need to add the controls & slideshow to it
                if ( madeWrapper === true ) {
                    var $whole = $slideshow.controls.add($slideshow);
                    $whole.wrapAll($wrapper);
                }
                                
                // Set widths etc.
                slideshowWidth = 0;
                // WRAPPER NOT A VALID OBJECT FOR WHATEVER REASON
                $wrapper.width($slideshow.currentSlide.width());
                $wrapper.height($slideshow.currentSlide.height());
                $slideshow.slides.each(function(){ 
                    $slideshow.positions[$slideshow.slides.index(this)] = slideshowWidth;
                    slideshowWidth += $(this).width();
                });
                $slideshow.width(slideshowWidth);
                
                // Bind functions
                $slideshow.controls.find('li a').bind('click', { slideshow: $slideshow }, controlClick );
                // Control nudges
                if ($slideshow.controlNudge.length > 0) {

                    $slideshow.controlNudge.bind('click', { slideshow: $slideshow }, animateControls);
                }
                
                // Pause the timer changes when hovering
                $slideshow
                    .mouseenter(function(){ $(this).addClass('pause'); })
                    .mouseleave(function(){ $(this).removeClass('pause'); });

                // Set intervals
                if ( $slideshow.settings.slideshow ) interval = setInterval('jogSlide()', $slideshow.settings.slideDelay);

            });
                        
        }
        
        controlClick = function (event) {
            
            
            
            var $slideshow = event.data.slideshow,
                currentPos = $slideshow.slides.index($slideshow.currentSlide);
                                                        
            event.preventDefault();
            if ( $slideshow.animating ) return;
                        
            var
            $clicked = $(this);
            
            // Ensure the timer doesn't keep firing
            $slideshow.addClass('stopped');
            
            // Specific slide requested
            if ( $clicked.hasClass('goto') ) {
                target = parseInt($clicked.attr('href').replace('#','')) - 1;
            }
            // Previous and not at the beginning, go back one
            else if ( $clicked.hasClass('prev') && currentPos > 0) {
                target = currentPos - 1;
            }
            // Previous and at the beginning, go to the end
            else if ( $clicked.hasClass('prev') && currentPos === 0) {
                target = $slideshow.slides.length-1;
            }
            // Next and not at the end
            else if ( $clicked.hasClass('next') && currentPos < ($slideshow.slides.length-1) ){
                target = currentPos + 1;
            } 
            // Default to slide one
            else {
                target = 0;
            }
            
            if ( currentPos == target ) return;
            
            animating = true;
            
            animateSlide(target,$slideshow);
            
        }
        
        // Moves one slide left of right
        // Usually in conjunction with a timer
        jogSlide = function (event,direction,$slideshow) {
            
            if ( $slideshows.animating ) return;
                                
            if ( direction == undefined && event == undefined ) {
                direction = 1;
            }
            
            /*
                TODO 
                ensure this will work for multiple slideshows on a page
                will need to sort out method scope and inheritance
            */
            var
            $a_slideshow = $('ul.slideshow');
            if ( $a_slideshow.hasClass('paused') || $a_slideshow.hasClass('stopped') ) return;
            $a_slideshow.parentWrapper = $a_slideshow.closest('div.slideshow');
            $a_slideshow.slides = $a_slideshow.find('> li');
            $a_slideshow.currentSlide = $a_slideshow.find('> li.cur');
            $a_slideshow.controls = $a_slideshow.parentWrapper.find('ul.slideshow_controls');
            $a_slideshow.controlWrapper = $a_slideshow.parentWrapper.find('div.control_wrapper');
            // How many controls can we show at one time?
            if ($slideshow.settings.orientation == 'horizontal') {
                $a_slideshow.visibleControls = Math.floor( ($a_slideshow.controlWrapper.width() / $a_slideshow.controls.find('li').outerWidth(true)))+1;
            } else {
                $a_slideshow.visibleControls = Math.floor( ($a_slideshow.controlWrapper.height() / $a_slideshow.controls.find('li').outerHeight(true)));
            }
            $a_slideshow.nudgeNext = $a_slideshow.parentWrapper.find('div.controlNudge a.next');
            
            if ( ($a_slideshow.currentSlide.index() + 1) % $a_slideshow.visibleControls == 0 || 
                $a_slideshow.slides.eq($a_slideshow.currentSlide.index() + 1).length == 0 ) {
                $a_slideshow.nudgeNext.trigger('click');
            }
                        
            target = parseInt($a_slideshow.controls.find('li.cur a').attr('href').replace('#','')) -1 + direction;
            
            if ( target >= $a_slideshow.slides.length ) {
                target = 0;
            }
            
            animateSlide(target,$a_slideshow);
            
        }
        
        animateSlide = function (target,$slideshow) {
                    
            var
            $target = $slideshow.slides.eq(target),
            image = new Image,
            $image = {},
            $image_link = $target.find('a.image_link');

            // 
            if ($target.has('img').length == 0) {
                image.src = $image_link.attr('href');
                $image = $(image);
                $image
                    .hide()
                    .css('zIndex', 0)
                    .load(function(){ $(this).fadeIn(350) });

                $image_link.replaceWith($image);
            }
            
            $slideshow.currentSlide.removeClass('cur');
            
            if ( $slideshow.settings.transition == 'fade' ) {
                $slideshow.currentSlide.fadeOut($slideshow.settings.speed);
                $target.fadeIn($slideshow.settings.speed, function(){ 
                    animating = false;
                    
                });
            }
            
            if ( $slideshow.settings.transition == 'slide' ) {
                $slideshow.animate({
                    'left': '-'+$slideshow.positions[target]+'px'
                }, $slideshow.settings.speed, function(){ 
                    animating = false;
                });
            }
            
            // Set current states etc.
            $slideshow.currentSlide = $target;
            $slideshow.slides.removeClass('cur');
            $slideshow.currentSlide.addClass('cur');
            $slideshow.controls
                .find('li').removeClass('cur')
                .eq(parseInt(target)).addClass('cur');
        }
        
        animateControls = function (event) {

            event.preventDefault();
            var 
            $clicked = $(this),
            $slideshow = event.data.slideshow,
            origin = 'horizontal' == $slideshow.settings.orientation ? 'left' : 'top',
            currentPos = parseInt($slideshow.controls.css(origin).replace('px','')),
            ctrlWrapperLimits = [$slideshow.controlWrapper.width(), $slideshow.controlWrapper.height()],
            currentSet = 'left' == origin ? Math.ceil(currentPos / ctrlWrapperLimits[0] * -1) : Math.ceil(currentPos / ctrlWrapperLimits[1] * -1),
            totalSlides = $slideshow.slides.length,
            totalSets = Math.ceil(totalSlides / $slideshow.visibleControls),
            finalPos = 'left' == origin ? (Math.ceil(($slideshow.controls.width() / ctrlWrapperLimits[0]) * -1) + 1) * ctrlWrapperLimits[0] : finalPos = (Math.ceil(($slideshow.controls.height() / ctrlWrapperLimits[1]) * -1) + 1) * ctrlWrapperLimits[1],
            moveBy = 0,            
            position = 0,
            href = $clicked.attr('href'),
            message = 
                'slide control width: '+$slideshow.controls.find('li').outerWidth(true)+'\n'+
                'total sets: '+totalSets+'\n'+
                'visible slides: '+$slideshow.visibleControls+'\n'+
                'from: '+currentSet+'\n'+
                'orientation: '+$slideshow.settings.orientation+'\n'+
                'direction: '+$clicked.attr('href');
                
            


            if ( href.indexOf("next") > -1 && (currentSet+2) <= totalSets ) {
                currentSet++;
            }
            else if ( href.indexOf("next") > -1 && (currentSet+2) > totalSets ) {
                currentSet = 0;
            }
            else if ( href.indexOf("prev") > -1 && (currentSet-1) >= 0 ) {
                currentSet--;
            } 
            else if ( href.indexOf("prev") > -1 && (currentSet-1) < 0 ) {
                currentSet = totalSets - 1;
            }
            message += '\nto: '+currentSet;

            position = 'left' == origin ? currentSet * ctrlWrapperLimits[0] * -1 : currentSet * ctrlWrapperLimits[1] * -1;
            message += '\nposition: '+position;
            
            //alert(message);
            // horizontal movement
            if ( origin == 'left' ) {
                $slideshow.controls.animate({ 'left': position+'px' }, $slideshow.settings.speed );
            }
            // vertical movement
            else {
                $slideshow.controls.animate({ 'top': position+'px' }, $slideshow.settings.speed );
            
            }
            //alert($slideshow.settings.orientation);
        
        }
        
        buildControls = function ($slideshow) {
            var 
            $controls = $(controls);            
            for (var i=1; i<=$slideshow.totalSlides; i++) {
                var $link = $('<a href="#'+(i-1)+'" class="goto">'+i+'</a>');
                $control = $(control);
                $link.appendTo($control);
                $controls.append($control);
                $slideshow.find('li:nth-child('+i+')').attr('id',$slideshow.settings.slideClass+'_'+i);
            }
            
            $slideshow.before($controls);
            $controls.find('li:nth-child(2)').addClass('cur');
            
            return $controls;

        }
        
        buildNudgers = function ($slideshow) {
            var
            $prev = $($slideshow.nudger.replace(/direction/g,'prev')),
            $next = $($slideshow.nudger.replace(/direction/g,'next')),
            $nudgers = $($prev,$next);
            
            $slideshow.controlWrapper.before($prev);
            $slideshow.controlWrapper.after($next);
            
            $slideshow.controlWrapper.addClass('with_nudge');
            
            return $nudgers;
            
        }
        
    

  
})( jQuery );

