(function($, Modules) {
  'use strict';

  Modules.InPageNavigation = function InPageNavigation() {
    var $tocPane;
    var $contentPane;
    var $tocItems;
    var $targets;

    this.start = function start($element) {
      $tocPane = $element.find('.app-pane__toc');
      $contentPane = $element.find('.app-pane__content');
      $tocItems = $('.js-toc-list').find('a');
      $targets = $contentPane.find('[id]');

      $contentPane.on('scroll', _.debounce(handleScrollEvent, 100, { maxWait: 100 }));

      if (Modernizr.history) {
        // Popstate is triggered when using the back button to navigate 'within'
        // the page, i.e. changing the anchor part of the URL.
        $(window).on('popstate', function (event) {
          restoreScrollPosition(event.originalEvent.state);
        });

        if (history.state && history.state.scrollTop) {
          // Restore existing state when e.g. using the back button to return to
          // this page
          restoreScrollPosition(history.state);
        } else {
          // Store the initial position so that we can restore it even if we
          // never scroll.
          handleInitialLoadEvent();
        }
      }
    };

    function restoreScrollPosition(state) {
      if (state && typeof state.scrollTop !== 'undefined') {
        $contentPane.scrollTop(state.scrollTop);
      }
    }

    function handleInitialLoadEvent() {
      var fragment = fragmentForTargetElement();

      if (!fragment) {
        fragment = fragmentForFirstElementInView();
      }

      handleChangeInActiveItem(fragment);
    }

    function handleScrollEvent() {
      handleChangeInActiveItem(fragmentForFirstElementInView());
    }

    function handleChangeInActiveItem(fragment) {
      storeCurrentPositionInHistoryApi(fragment);
      highlightActiveItemInToc(fragment);
    }

    function storeCurrentPositionInHistoryApi(fragment) {
      if (Modernizr.history && fragment) {
        history.replaceState(
          { scrollTop: $contentPane.scrollTop() },
          "",
          fragment
        );
      }
    }

    function highlightActiveItemInToc(fragment) {
      var $activeTocItem = $tocItems.filter('[href="' + fragment + '"]');

      if ($activeTocItem.get(0)) {
        $tocItems.removeClass('toc-link--in-view');
        $activeTocItem.addClass('toc-link--in-view');
        scrollTocToActiveItem($activeTocItem);
      }
    }

    function scrollTocToActiveItem($activeTocItem) {
      var paneHeight = $tocPane.height();
      var linkTop = $activeTocItem.position().top;
      var linkBottom = linkTop + $activeTocItem.outerHeight();

      var offset = null;

      if (linkTop < 0) {
        offset = linkTop;
      } else if (linkBottom >= paneHeight) {
        offset = -(paneHeight - linkBottom);
      } else {
        return;
      }

      var newScrollTop = $tocPane.scrollTop() + offset;

      $tocPane.scrollTop(newScrollTop);
    }

    function fragmentForTargetElement() {
      return window.location.hash;
    }

    function fragmentForFirstElementInView() {
      var result = null;

      $($targets.get().reverse()).each(function checkIfInView(index) {
        if (result) {
          return;
        }

        var $this = $(this);

        if (Math.floor($this.position().top) <= 0) {
          result = $this;
        }
      });

      return result ? '#' + result.attr('id') : false;
    }
  };
})(jQuery, window.GOVUK.Modules);