/**
 * @ngdoc overview
 * @name ui.bootstrap.carousel
 *
 * @description
 * AngularJS version of an image carousel.
 *
 */
msos.provide("ng.bootstrap.ui.carousel");

ng.bootstrap.ui.carousel.version = new msos.set_version(15, 7, 7);


// Below is the standard plugin, except for templateUrl location and naming (MSOS style)
// ui.bootstrap.carousel -> ng.bootstrap.ui.carousel,
// template/carousel/carousel.html  -> msos.resource_url('ng', 'bootstrap/ui/tmpl/carousel.html')
// template/carousel/slide.html     -> msos.resource_url('ng', 'bootstrap/ui/tmpl/slide.html')
angular.module('ng.bootstrap.ui.carousel', [])
    .controller('CarouselController', ['$scope', '$interval', '$animate', function ($scope, $interval, $animate) {
        var self = this,
            slides = self.slides = $scope.slides = [],
            currentIndex = -1,
            currentInterval, isPlaying;
        self.currentSlide = null;

        var destroyed = false;
        /* direction: "prev" or "next" */
        self.select = $scope.select = function(nextSlide, direction) {
            var nextIndex = self.indexOfSlide(nextSlide);
            //Decide direction if it's not given
            if (direction === undefined) {
                direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
            }
            if (nextSlide && nextSlide !== self.currentSlide) {
                goNext();
            }

            function goNext() {
                // Scope has been destroyed, stop here.
                if (destroyed) {
                    return;
                }

                angular.extend(nextSlide, {
                    direction: direction,
                    active: true
                });
                angular.extend(self.currentSlide || {}, {
                    direction: direction,
                    active: false
                });
                if ($animate.enabled() && !$scope.noTransition && nextSlide.$element) {
                    $scope.$currentTransition = true;
                    nextSlide.$element.one('$animate:close', function closeFn() {
                        $scope.$currentTransition = null;
                    });
                }

                self.currentSlide = nextSlide;
                currentIndex = nextIndex;
                //every time you change slides, reset the timer
                restartTimer();
            }
        };
        $scope.$on('$destroy', function() {
            destroyed = true;
        });

        function getSlideByIndex(index) {
            if (angular.isUndefined(slides[index].index)) {
                return slides[index];
            }
            var i, len = slides.length;
            for (i = 0; i < slides.length; ++i) {
                if (slides[i].index == index) {
                    return slides[i];
                }
            }
        }

        self.getCurrentIndex = function() {
            if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
                return +self.currentSlide.index;
            }
            return currentIndex;
        };

        /* Allow outside people to call indexOf on slides array */
        self.indexOfSlide = function(slide) {
            return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
        };

        $scope.next = function() {
            var newIndex = (self.getCurrentIndex() + 1) % slides.length;

            //Prevent this user-triggered transition from occurring if there is already one in progress
            if (!$scope.$currentTransition) {
                return self.select(getSlideByIndex(newIndex), 'next');
            }
        };

        $scope.prev = function() {
            var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;

            //Prevent this user-triggered transition from occurring if there is already one in progress
            if (!$scope.$currentTransition) {
                return self.select(getSlideByIndex(newIndex), 'prev');
            }
        };

        $scope.isActive = function(slide) {
            return self.currentSlide === slide;
        };

        $scope.$watch('interval', restartTimer);
        $scope.$on('$destroy', resetTimer);

        function restartTimer() {
            resetTimer();
            var interval = +$scope.interval;
            if (!isNaN(interval) && interval > 0) {
                currentInterval = $interval(timerFn, interval);
            }
        }

        function resetTimer() {
            if (currentInterval) {
                $interval.cancel(currentInterval);
                currentInterval = null;
            }
        }

        function timerFn() {
            var interval = +$scope.interval;
            if (isPlaying && !isNaN(interval) && interval > 0) {
                $scope.next();
            } else {
                $scope.pause();
            }
        }

        $scope.play = function() {
            if (!isPlaying) {
                isPlaying = true;
                restartTimer();
            }
        };
        $scope.pause = function() {
            if (!$scope.noPause) {
                isPlaying = false;
                resetTimer();
            }
        };

        self.addSlide = function(slide, element) {
            slide.$element = element;
            slides.push(slide);
            //if this is the first slide or the slide is set to active, select it
            if (slides.length === 1 || slide.active) {
                self.select(slides[slides.length - 1]);
                if (slides.length == 1) {
                    $scope.play();
                }
            } else {
                slide.active = false;
            }
        };

        self.removeSlide = function(slide) {
            if (angular.isDefined(slide.index)) {
                slides.sort(function(a, b) {
                    return +a.index > +b.index;
                });
            }
            //get the index of the slide inside the carousel
            var index = slides.indexOf(slide);
            slides.splice(index, 1);
            if (slides.length > 0 && slide.active) {
                if (index >= slides.length) {
                    self.select(slides[index - 1]);
                } else {
                    self.select(slides[index]);
                }
            } else if (currentIndex > index) {
                currentIndex--;
            }
        };

    }])

.directive('carousel', [function() {
    return {
        restrict: 'EA',
        transclude: true,
        replace: true,
        controller: 'CarouselController',
        require: 'carousel',
        templateUrl: msos.resource_url('ng', 'bootstrap/ui/tmpl/carousel.html'),
        scope: {
            interval: '=',
            noTransition: '=',
            noPause: '='
        }
    };
}])

.directive('slide', function() {
    return {
        require: '^carousel',
        restrict: 'EA',
        transclude: true,
        replace: true,
        templateUrl: msos.resource_url('ng', 'bootstrap/ui/tmpl/slide.html'),
        scope: {
            active: '=?',
            index: '=?'
        },
        link: function(scope, element, attrs, carouselCtrl) {
            carouselCtrl.addSlide(scope, element);
            //when the scope is destroyed then remove the slide from the current slides array
            scope.$on('$destroy', function() {
                carouselCtrl.removeSlide(scope);
            });

            scope.$watch('active', function(active) {
                if (active) {
                    carouselCtrl.select(scope);
                }
            });
        }
    };
})

.animation('.item', [
    '$animate',
    function($animate) {
        return {
            beforeAddClass: function(element, className, done) {
                // Due to transclusion, noTransition property is on parent's scope
                if (className == 'active'
                 && element.parent()
                 && element.parent().scope()
                 && !element.parent().scope().noTransition) {
                    var stopped = false;
                    var direction = element.isolateScope().direction;
                    var directionClass = direction == 'next' ? 'left' : 'right';
                    element.addClass(direction);
                    $animate.addClass(element, directionClass).then(function() {
                        if (!stopped) {
                            element.removeClass(directionClass + ' ' + direction);
                        }
                        done();
                    });

                    return function() {
                        stopped = true;
                    };
                }
                done();
            },
            beforeRemoveClass: function(element, className, done) {
                // Due to transclusion, noTransition property is on parent's scope
                if (className == 'active'
                 && element.parent()
                 && element.parent().scope()
                 && !element.parent().scope().noTransition) {
                    var stopped = false;
                    var direction = element.isolateScope().direction;
                    var directionClass = direction == 'next' ? 'left' : 'right';
                    $animate.addClass(element, directionClass).then(function() {
                        if (!stopped) {
                            element.removeClass(directionClass);
                        }
                        done();
                    });
                    return function() {
                        stopped = true;
                    };
                }
                done();
            }
        };

    }
]);
