import debounce from '../utils/debounce.service';
import windowScrollTop from '../utils/window-scroll-top.service';

/**
 * @ngdoc module
 * @name spy.scrollContainer
 */
const mod = angular.module('spy.scrollContainer', [
    debounce.name,
    windowScrollTop.name
]);

mod.directive('spyScrollContainer', ['$window', '$timeout', 'spyDebounce', 'windowScrollTop',spyScrollContainerDirective ]);


/**
* @ngdoc directive
* @module spy.scrollContainer
* @name spyScrollContainer
*
* @restrict A
*
*
* @description
* The spyScrollContainer indicates which is the main scroller in the page.
*
*/
function spyScrollContainerDirective($window, $timeout, spyDebounce, windowScrollTop) {
    return {
        restrict: 'A',
        controller: ['$scope', '$element', function ($scope, $element) {
            this.spies = [];
            this.registerSpy = (spy) => {
                this.spies.push(spy);
            };

            this.getScrollContainer = () => {
                return $element[0];
            };
        }],
        link (scope, elem, attrs, selfCtrl) {
            const afTimeout = 400;
            let vpHeight,
                $aWindow = angular.element($window),
                $scroller = elem[0].tagName === 'BODY' ?
                    $aWindow : elem,
                animationFrame,
                lastScrollTimestamp = 0,
                scrollPrevTimestamp = 0,
                previousScrollTop = 0;

            function onScroll() {
                lastScrollTimestamp = $window.performance.now();
                if (!animationFrame) {
                    animationFrame = $window.requestAnimationFrame(onAnimationFrame);
                }
                scrollPrevTimestamp = $window.performance.now();
            }

            function onResize() {
                vpHeight = Math.max($window.document.documentElement.clientHeight, window.innerHeight || 0);
                selfCtrl.spies.forEach((spy)=> {
                    spy.updateClientRect();
                });
                onScroll();
            }

            function onAnimationFrame() {
                const viewportRect = getViewportRect(),
                        timestamp = $window.performance.now(),
                        delta = timestamp - scrollPrevTimestamp,
                        scrollDelta = viewportRect.top - previousScrollTop,
                        scrollDirection = scrollDelta === 0 ? 0 :
                            scrollDelta / Math.abs(scrollDelta);

                selfCtrl.spies.forEach((spy)=> {
                    spy.update(viewportRect, scrollDirection);
                });

                previousScrollTop = viewportRect.top;

                if (delta < afTimeout) {
                    queueAf();
                } else {
                    cancelAf();
                }
            }

            function getViewportRect() {
                const currentScroll = windowScrollTop();
                return {
                    top: currentScroll,
                    height: vpHeight
                };
            }

            function queueAf() {
                animationFrame = $window.requestAnimationFrame(onAnimationFrame);
            }

            function cancelAf() {
                $window.cancelAnimationFrame(animationFrame);
                animationFrame = null;
            }

            scope.$on('spyScrollContainer:updateSpies', onResize);

            if (angular.isDefined(attrs.triggerUpdate)) {
                scope.$watch(attrs.triggerUpdate, function (newVal, oldVal) {
                    if (newVal !== oldVal) {
                        $timeout(function () {
                            onResize();
                        }, 0);
                    }
                });
            }

            $aWindow.on('resize', spyDebounce(onResize, 300));
            $scroller.on('scroll', onScroll);
            $timeout(onResize);
        }
    };
}

export default mod;
