(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) {
    // Navigation items for single page navigation don't necessarily include the path name, but
    // navigation items for multipage navigation items do include it. This checks for either case.
    var $activeTocItem = $tocItems.filter(
      '[href="' + window.location.pathname + fragment + '"],[href="' + fragment + '"]'
    );
    // Navigation items with children don't contain fragments in their url
    // Check to see if any nav items contain just the path name.
    if(!$activeTocItem.get(0)) {
      $activeTocItem = $tocItems.filter('[href="' + window.location.pathname + '"]');
    }
    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);