(function($){
    //namespace
    var AccordianSlideshow = window.AccordianSlideshow = {};
    
    AccordianSlideshow.Abstract = {};
	/**
	 * For managing options
	 * @abstract
	 */
	AccordianSlideshow.Abstract.Options =  {
		/**
		 * @var Object
		 */
		options: {},

		/**
		 * @param Object options
		 * @return void
		 */
		setOptions: function(options) {
			$.extend(this.options, options);
		},

		/**
		 * @param String key
		 * @param Mixed value
		 * @return void
		 */
		setOption: function(key, value) {
			this.options[key] = value;
		},

		/**
		 * @param void
		 * @return Object
		 */
		getOptions: function() {
			return this.options;
		},

		/**
		 * @param String key
		 * @param Mixed default
		 * @return Mixed
		 */
		getOption: function(key, defaultValue) {
			if (!this.objHasAttribute(this.options, key) && !defaultValue) {
				return null;
			} else if (!this.objHasAttribute(this.options, key) && defaultValue && this.options[key]) {
				return defaultValue;
			}
			return this.options[key];
		},
        
        objHasAttribute: function(obj, attr) {
            for(key in obj) {
                if(attr == key) {
                    return true;
                }
            }
            return false;
        }
	};
})(jQuery);

(function($){
    
    var o = AccordianSlideshow.slider = function(el, options) 
    {
        this.$el = $(el);
        
        var defaultOptions = {
            initialOffset: 10,
            openOffset: 200,
            bumpOffset: 5,
            zIndexFrom: 0,
            slideClass: 'slide',
            slideLinkClass: 'slide-link',
            stackTop: 'left',
            startSlide: 2,
            openAnimateTime: 300,
            closeAnimateTime: 300,
            bumpAnimateTime: 20,
            openEasing: 'easeInSine',
            closeEasing: 'easeOutSine',
            bumpEasing: null,
            animateSlideBackground: true,
            slideBackgroundPositions: { 0 : [0, 100], 1 : [0, 100], 2 : [0, 100], 3 : [0, 100]},
            slideBackgroundOpenAnimateTime: 300,
            slideBackgroundcloseAnimateTime: 300,
            slideBackgroundOpenEasing: 'easeInSine',
            slideBackgroundCloseEasing: 'easeOutSine',
            donePrepCallback: null,
            openOnClick: true,
            openOnMouseEnter: false,
            openOnMouseLeave: false,
            closeOnMouseEnter: false,
            closeOnMouseLeave: false,
            closeOnClick: true,
            slideOpenCallback: null,
            slideCloseCallback: null,
            slideClickCallback: null,
            slideMouseEnterCallback: null,
            slideMouseLeaveCallback: null
        }
        
        this.setOptions(defaultOptions);
        this.setOptions(options);
        
        this.slides = $('.' + this.getOption('slideClass'), this.$el);
        this.slidesLen = this.slides.length;
        this.slideCache = {};
        this.counter = 0;
        this.slideLock = false;
        
        //prepare strip
        _prepShow.call(this);

        //attach events
        _initEvents.call(this);
        
        //make it visible
        this.$el.css({visibility:'visible'});
        
        //setup listeners
       
    }, p = o.prototype;
    
    //add initial styles
    var _prepShow= function() 
    {
        offset = this.getOption('initialOffset');
        initZ = this.getOption('zIndexFrom');
        endOffset = (this.slidesLen-1) * offset;
        endZ = initZ + this.slidesLen;
        topOrder = this.getOption('stackTop');
        if(topOrder == 'left') {
            myZ = initZ;
        } else if(topOrder == 'right'){
            myZ = endZ;
        }
        for(var i=this.slidesLen; i--;) {
            
            $(this.slides[i]).addClass('slide-' + i);
            $(this.slides[i]).data('slideData', { pos: i });
            
            $(this.slides[i]).css({
                zIndex: myZ,
                left: endOffset
            });
            
            endOffset = endOffset - offset;
            
            //decrement the two
            if(topOrder == 'left') {
                myZ = myZ + 1;
            } else if(topOrder == 'right'){
                myZ = myZ - 1;   
            }
            //mark all as open
        }
        
        //close the one after start slide
        _updateSlideCache.call(this, true)
        start = this.getOption('startSlide');
        if(start < this.slidesLen - 1) {
            _toggleSlide.call(this, start, false);
        }
        
        //call the prep done callback
        var done = this.getOption('donePrepCallback');
        var slidesCopy = this.slides.slice();
        var startSlide = slidesCopy.splice(start, 1);
        if(done != null) {
            done(startSlide, slidesCopy, this.slides);
        }
        
    }

    var _initEvents = function() {
        var _this = this;
        //prevent the slide links from bubbling
        $("." + this.getOption('slideLinkClass')).bind('click',{}, function(){
                window.location = $(this).attr("href");
                return false;
        });
        
        if(this.getOption('openOnClick') ==  true || this.getOption('closeOnClick') == true || this.getOption('slideClickCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'click', function(e){ _preOpenSlide.call(_this, e, this); });
        }
        
        if(this.getOption('openOnMouseEnter') == true || this.getOption('closeOnMouseEnter') == true || this.getOption('slideMouseEnterCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'mouseenter', function(e){ _preOpenSlide.call(_this, e, this); });
        }
        
        if(this.getOption('openOnMouseLeave') == true || this.getOption('closeOnMouseLeave') == true || this.getOption('slideMouseLeaveCallback') != null) {
            this.$el.delegate("." + this.getOption('slideClass'), 'mouseleave', function(e){ _preOpenSlide.call(_this, e, this); });
        }
    }
    
    var _updateSlideCache = function(start, update) {
        if(update != undefined) {
            var iter = update.length;
            var store = update;
        } else {
            var iter = this.slides.length;
            var store = this.slides;
        }
        for(var i = iter; i--;) {
            $curr = $(store[i]); 
            slidePos = $curr.data('slideData').pos;
            var newValues = { 
                zIndex:$curr.css('zIndex'), 
                left: $curr.css('left'), 
                pos: $curr.position(), 
                off: $curr.offset() 
            }
            
            this.slideCache[slidePos] = $.extend(this.slideCache[slidePos], newValues);
            if(start == true) {
                this.slideCache[slidePos] = $.extend(this.slideCache[slidePos], {
                    startPos : $curr.position(),  
                    startOffset : $curr.offset(),
                    open : true
                });
            }
        }
        this.counter++;
    }
    
    var _preOpenSlide = function(event, element) {
        $elm = $(element);
        //only allow non-visible elements to trigger events
        if(!$elm.hasClass('visible')) {
            slidePos = $elm.data('slideData').pos;
            $meData = this.slideCache[slidePos];
            var slidesCopy = this.slides.slice();
            var target = slidesCopy.splice(slidePos, 1);
            
            callbacks = {
                click: (this.getOption('clickCallback') != null) ? this.getOption('clickCallback') : false,
                mouseenter: (this.getOption('slideMouseEnterCallback') != null) ? this.getOption('slideMouseEnterCallback') : false,
                mouseleave: (this.getOption('slideMouseLeaveCallback') != null) ? this.getOption('slideMouseLeaveCallback') : false,
                open: (this.getOption('slideOpenCallback') != null) ? this.getOption('slideOpenCallback') : false,
                close: (this.getOption('slideCloseCallback') != null) ? this.getOption('slideCloseCallback') : false
            }

            if ($meData.open == true) {
                var dir = 'close';
            } else {
                var dir = 'open';
            }
            if (event.type == 'mouseenter') {
                if (this.getOption(dir + 'OnMouseEnter')) {
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.mouseenter])
                } else if(callbacks.mouseenter != false) {
                    callbacks.mouseenter(slidePos, target, slidesCopy, this.slides)
                }
            } else if (event.type == 'mouseleave') {
                if (this.getOption(dir + 'OnMouseLeave')) {
                    console.log(event.type, dir + 'OnMouseLeave', this.getOption(dir + 'OnMouseLeave'));
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.mouseleave], slidePos)
                } else if(callbacks.mouseleave != false) {
                    callbacks.mouseleave(slidePos, target, slidesCopy, this.slides)
                }
            } else if (event.type == 'click') {
                if (this.getOption(dir + 'OnClick')) {
                    _toggleSlide.call(this, slidePos, true, [callbacks[dir], callbacks.click])
                } else if(callbacks.mouseenter != false) {
                    callbacks.click(slidePos, target, slidesCopy, this.slides)
                }
            }
        }
    }
    
    var _toggleSlide = function(slide, animoot, fns){
        if(!this.slideLock) {
            this.slideLock = true;
            $me = $(this.slides[slide]);
            $meData = this.slideCache[slide];
            var move = null;
            
            //am i open or closed?
            var imOpen = ($meData.open == true) ? true : false;
            
            var offsetDiff = this.getOption('openOffset') - this.getOption('initialOffset')
            

            //behavior is different if this is the first slide
            //var visSlide;  
            if(imOpen == true) {
                move = this.slides.slice(slide+1);
                var moveTo = $meData.startPos.left + offsetDiff;
                var direction = 'close';
                //mark slide - 1 as visible
                $(this.slides).removeClass('visible');
                $(this.slides[slide]).addClass('visible');
                visSlide = slide;
            } else {
                move = this.slides.slice(1, slide);
                addOffset = 0;
                var moveTo = $meData.startPos.left;
                var direction = 'open';
                //mark slide as visible
                $(this.slides).removeClass('visible');
                $me.addClass('visible');
                visSlide = slide
            }
            

            moveLen = move.length
            
            //move the slide background
            if(this.getOption('animateSlideBackground') == true) {
                _moveSlideBackground.call(this, visSlide, true);
            }
            
            //move me only if i am closed
            if(!imOpen) {
                if($meData.startPos.left != 0 && direction) {
                    _moveSlide.call(this, slide, $me, moveTo, animoot, direction)
                }
            }

            //move my minions
            if(moveLen > 0) {
                for(var i = moveLen; i--;) {
                    $unit = $(move[i]);
                    $unitData = this.slideCache[$unit.data('slideData').pos]
                    var slidePos = $unit.data('slideData').pos;
                    var newPos = (direction == 'open') ? $unitData.startPos.left : $unitData.startPos.left + offsetDiff
                    _moveSlide.call(this, slidePos, $unit, newPos, animoot, direction);
                }
            }
            


                 
            _updateSlideCache.call(this, false, move);

            
            //issue the callback
            if(fns != undefined) {
                var R = fns.length;
                var slidesCopy = this.slides.slice();
                var opened = slidesCopy.splice($me.data('slideData').pos, 1);
                for(var i = R; i--;)  {
                    if (fns[i] != false) {
                        (fns[i])($me.data('slideData').pos, opened, slidesCopy, this.slides);
                    }
                }
            }
        }
    }
    
    var _moveSlide = function(slidePos, slide, pos, animoot, direction) {
        var easing = this.getOption(direction + 'Easing');
        this.slideCache[slidePos].open = (direction == 'open') ? true : false;
        var time = this.getOption(direction + 'AnimateTime');
        var _this = this;
        if(animoot ==  true) {
            slide.animate({left:pos}, time, easing, function(){ _this.slideLock = false; });
        } else {
            slide.css({left:pos});
            this.slideLock = false;
        }
    }
    
    var _moveSlideBackground = function(visibleSlidePos, animoot) {

        for(var i = this.slidesLen; i--;) {
            var $slideImg = $(".slide-background > img", this.slides[i]);
            var bkgdPosition = this.getOption('slideBackgroundPositions')[i];
            var time, easing, newPos;
            if ( i == visibleSlidePos ) {
                //move to open pos
                time = this.getOption('slideBackgroundOpenAnimateTime');
                easing = this.getOption('slideBackgroundOpenEasing');
                newPos = bkgdPosition[0];
            } else {
                //move to close
                time = this.getOption('slideBackgroundCloseAnimateTime');
                easing = this.getOption('slideBackgroundCloseEasing');
                newPos = bkgdPosition[1];
            }
            
            if (animoot == true) {
                $slideImg.animate({left:newPos}, time, easing);
            } else {
                $slideImg.css({left:newPos});
            }
        }
    }
    
    
    //extend the object to include the abstract classes
    $.extend(p, AccordianSlideshow.Abstract.Options);
    
})(jQuery);


    




