(function($) {
    $.fn.slider = function(settings) {
        settings = $.extend({
            addClass:        null,
            fnBeforeCreate:  null,
            fnAfterCreate:   null,
            fnBeforeSlide:   null,
            fnAfterHide:     null,
            fnBeforeShow:    null,
            fnAfterSlide:    null,
            fadeDuration:    400,
            slideDuration:   10000,
            btnPrev:         false,
            btnNext:         false,
            btnPrevText:     'Previous',
            btnNextText:     'Next',
            pagination:      true,
            autoSlide:       true,
            startSlide:      0,
            sequentialSlide: false,
            circular:        true
        }, settings);

        return this.each(function() {
            var objSlider = {
                $this:      null,
                $slideList: null,
                isAnimated: false,
                countSlide: null,
                curSlide:   settings.startSlide,
                timeoutId:  0,
                objNavigation: {
                    $this:       null,
                    $btnPrev:    null,
                    $pagination: null,
                    $btnNext:    null
                },

                objCallbackArg: {
                    $slider:     null,
                    $slideList:  null,
                    countSlide:  null,
                    curSlide:    settings.startSlide,
                    newSlide:    null,
                    $btnPrev:    null,
                    $pagination: null,
                    $btnNext:    null
                },

                fnCreate: function($slider) {
                    this.$this = this.objCallbackArg.$slider = $slider;
                    this.$slideList = this.objCallbackArg.$slideList = $slider.children('ul').children('li');
                    this.countSlide = this.objCallbackArg.countSlide = this.$slideList.length;

                    if (settings.fnBeforeCreate && settings.fnBeforeCreate(this.objCallbackArg) === false) {
                        return;
                    }

                    var objSlider = this;

                    this.$this.addClass('slider');
                    this.$slideList.parent()/*.css({
                        position: 'relative'
                    })*/.addClass('slide-list');
                    if (settings.addClass) {
                        this.$this.addClass(settings.addClass);
                    }

                    var p = settings.padding;
                    this.$slideList.css({
                        display:  'none',
                        //position: 'absolute',
                        top:      p + 'px',
                        right:    p + 'px',
                        bottom:   p + 'px',
                        left:     p + 'px'
                    });

                    /* Navigation */
                    if (settings.btnPrev || settings.btnNext || settings.pagination) {
                        var $navigation = $('<div class="navigation"></div>');
                        $slider.append($navigation);
                    }

                    /* Button Previous */
                    if (settings.btnPrev !== false) {
                        var $btnPrev;

                        if (settings.btnPrev === true) {
                            $btnPrev = $('<a class="prev" href="#">' + settings.btnPrevText + '</a>');
                            $navigation.append($btnPrev);
                        } else {
                            $btnPrev = settings.btnPrev;
                        }

                        this.objNavigation.$btnPrev = this.objCallbackArg.$btnPrev = $btnPrev;

                        $btnPrev.click(function(event) {
                            if (!objSlider.isAnimated) {
                                objSlider.isAnimated = true;
                                objSlider.fnStop();
                                objSlider.fnPrevSlide(function() {
                                    objSlider.isAnimated = false;
                                });
                                objSlider.fnStart();
                            }

                            event.preventDefault();
                        });
                    } else {
                        this.objNavigation.$btnPrev = this.objCallbackArg.$btnPrev = $([]);
                    }

                    /* Pagination */
                    if (settings.pagination !== false) {
                        var $paginationUl;

                        if (settings.pagination === true) {
                            $paginationUl = $('<ul class="pagination"></ul>');
                            $navigation.append($paginationUl);

                            objSlider.$slideList.each(function(index) {
                                var $pLi = $('<li></li>');
                                $paginationUl.append($pLi);
                                var $pA = $('<a href="#">' + (index + 1) + '</a>');
                                $pLi.append($pA);
                            });
                        } else {
                            $paginationUl = settings.pagination;
                        }

                        this.objNavigation.$pagination = this.objCallbackArg.$pagination = $paginationUl.children('li');

                        $paginationUl.find('> li > a').each(function(index) {
                            $(this).click(function(event) {
                                if (!$(this).closest('li').hasClass('active') && !objSlider.isAnimated) {
                                    objSlider.isAnimated = true;
                                    objSlider.fnStop();
                                    objSlider.fnGotoSlide(index, function() {
                                        objSlider.isAnimated = false;
                                    });
                                }
                                objSlider.fnStart();

                                event.preventDefault();
                            });
                        });
                    } else {
                        this.objNavigation.$pagination = this.objCallbackArg.$pagination = $([]);
                    }

                    /* Button Next */
                    if (settings.btnNext !== false) {
                        var $btnNext;

                        if (settings.btnNext === true) {
                            $btnNext = $('<a class="next" href="#">' + settings.btnNextText + '</a>');
                            $navigation.append($btnNext);
                        } else {
                            $btnNext = settings.btnNext;
                        }

                        this.objNavigation.$btnNext = this.objCallbackArg.$btnNext = $btnNext;

                        $btnNext.click(function(event) {
                            if (!objSlider.isAnimated) {
                                objSlider.isAnimated = true;
                                objSlider.fnStop();
                                objSlider.fnNextSlide(function() {
                                    objSlider.isAnimated = false;
                                });
                            }
                            objSlider.fnStart();

                            event.preventDefault();
                        });
                    } else {
                        this.objNavigation.$btnNext = this.objCallbackArg.$btnNext = $([]);
                    }

                    /* Start */
                    this.$slideList.eq(this.curSlide).css({display: 'block'}).addClass('active');
                    this.objNavigation.$pagination.eq(this.curSlide).addClass('active');
                    this.updateNavigation(0);

                    if (!(settings.fnAfterCreate && settings.fnAfterCreate(this.objCallbackArg) === false)) {
                        this.fnStart();
                    }
                },

                fnGotoSlide: function(newSlide, fnCallback) {
                    if (newSlide >= this.countSlide || newSlide < 0 || newSlide === this.curSlide) {
                        return;
                    }

                    var objSlider = this;
                    this.objCallbackArg.newSlide = newSlide;

                    var $curPaginationLi = this.objNavigation.$pagination.eq(this.curSlide);
                    var $newPaginationLi = this.objNavigation.$pagination.eq(newSlide);
                    var $curSlideLi = this.$slideList.eq(this.curSlide);
                    var $newSlideLi = this.$slideList.eq(newSlide);

                    if (settings.fnBeforeSlide && settings.fnBeforeSlide(this.objCallbackArg) === false) {
                        this.objCallbackArg.newSlide = null;
                        return;
                    }

                    $curSlideLi.add($curPaginationLi).removeClass('active');
                    $newSlideLi.add($newPaginationLi).addClass('active');

                    if (settings.sequentialSlide) {
                        $curSlideLi.fadeOut(settings.fadeDuration, function() {
                            if (settings.fnAfterHide) {
                                settings.fnAfterHide(objSlider.objCallbackArg);
                            }

                            if (settings.fnBeforeShow) {
                                settings.fnBeforeShow(objSlider.objCallbackArg);
                            }

                            $newSlideLi.fadeIn(settings.fadeDuration, afterSlide);
                        });
                    } else {
                        if (settings.fnBeforeShow) {
                            settings.fnBeforeShow(objSlider.objCallbackArg);
                        }

                        if (newSlide > this.curSlide) {
                            $newSlideLi.fadeIn(settings.fadeDuration, function() {
                                $curSlideLi.css({display: 'none'});
                                if (settings.fnAfterHide) {
                                    settings.fnAfterHide(objSlider.objCallbackArg);
                                }

                                afterSlide();
                            });
                        } else {
                            $newSlideLi.css({display: 'block'});
                            $curSlideLi.fadeOut(settings.fadeDuration, function() {
                                if (settings.fnAfterHide) {
                                    settings.fnAfterHide(objSlider.objCallbackArg);
                                }

                                afterSlide();
                            });
                        }
                    }

                    function afterSlide() {
                        if (settings.fnAfterSlide) {
                            settings.fnAfterSlide(this.objCallbackArg);
                        }

                        objSlider.curSlide = objSlider.objCallbackArg.curSlide = newSlide;
                        objSlider.objCallbackArg.newSlide = null;

                        fnCallback();
                    }
                },

                fnPrevSlide: function(fnCallback) {
                    if (settings.circular) {
                        if (this.curSlide == 0) {
                            this.fnGotoSlide(this.countSlide - 1, fnCallback);
                        } else {
                            this.fnGotoSlide(this.curSlide - 1, fnCallback);
                        }
                    } else {
                        if (this.curSlide == 0) {
                            fnCallback();
                            return;
                        } else {
                            this.updateNavigation(-1);
                            this.fnGotoSlide(this.curSlide - 1, fnCallback);
                        }
                    }
                },

                fnNextSlide: function(fnCallback) {
                    if (settings.circular) {
                        if (this.curSlide == this.countSlide - 1) {
                            this.fnGotoSlide(0, fnCallback);
                        }  else {
                            this.fnGotoSlide(this.curSlide + 1, fnCallback);
                        }
                    } else {
                        if (this.curSlide == this.countSlide - 1) {
                            fnCallback();
                            return;
                        } else {
                            this.updateNavigation(1);
                            this.fnGotoSlide(this.curSlide + 1, fnCallback);
                        }
                    }
                },

                fnAutoSlide: function() {
                    if (!objSlider.isAnimated) {
                        objSlider.isAnimated = true;
                        objSlider.fnNextSlide(function() {
                            objSlider.isAnimated = false;
                        });
                    }
                    objSlider.fnStart();
                },

                updateNavigation: function(newSlide) {
                    if (this.curSlide + newSlide === 0) {
                        this.objNavigation.$btnPrev.addClass('disabled');
                    }
                    if (this.curSlide + newSlide > 0) {
                        this.objNavigation.$btnPrev.removeClass('disabled');
                    }
                    if (this.curSlide + newSlide < this.countSlide - 1) {
                        this.objNavigation.$btnNext.removeClass('disabled');
                    }
                    if (this.curSlide + newSlide === this.countSlide - 1) {
                        this.objNavigation.$btnNext.addClass('disabled');
                    }
                },

                fnStart: function() {
                    if (settings.autoSlide) {
                        this.timeoutId = setTimeout($.proxy(this.fnAutoSlide, this), settings.slideDuration);
                    }
                },

                fnStop: function() {
                    if (this.timeoutId) {
                        clearTimeout(this.timeoutId);
                    }
                }
            };

            objSlider.fnCreate($(this));
        });
    };
})(jQuery);
