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

  Modules.TableOfContents = function () {
    var $html = $('html');

    var $toc;
    var $tocList;

    var $openLink;
    var $closeLink;

    this.start = function ($element) {
      $toc = $element;
      $tocList = $toc.find('.js-toc-list');

      // Open link is not inside the module
      $openLink = $html.find('.js-toc-show');
      $closeLink = $toc.find('.js-toc-close');

      fixRubberBandingInIOS();
      updateAriaAttributes();

      // Need delegated handler for show link as sticky polyfill recreates element
      $openLink.on('click.toc', preventingScrolling(openNavigation));
      $closeLink.on('click.toc', preventingScrolling(closeNavigation));
      $tocList.on('click.toc', 'a', closeNavigation);

      // Allow aria hidden to be updated when resizing from mobile to desktop or
      // vice versa
      $(window).on('resize.toc', updateAriaAttributes)

      $(document).on('keydown.toc', function (event) {
        var ESC_KEY = 27;

        if (event.keyCode == ESC_KEY) {
          closeNavigation();
        }
      });
    };

    function fixRubberBandingInIOS() {
      // By default when the table of contents is at the top or bottom,
      // scrolling in that direction will scroll the body 'behind' the table of
      // contents. Fix this by preventing ever reaching the top or bottom of the
      // table of contents (by 1 pixel).
      // 
      // http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/
      $toc.on("touchstart.toc", function () {
        var $this = $(this),
          top = $this.scrollTop(),
          totalScroll = $this.prop('scrollHeight'),
          currentScroll = top + $this.prop('offsetHeight');

        if (top === 0) {
          $this.scrollTop(1);
        } else if (currentScroll === totalScroll) {
          $this.scrollTop(top - 1);
        }
      });
    }

    function openNavigation() {
      $html.addClass('toc-open');
      
      toggleBackgroundVisiblity(false);
      updateAriaAttributes();

      focusFirstLinkInToc();
    }

    function closeNavigation() {
      $html.removeClass('toc-open');

      toggleBackgroundVisiblity(true);
      updateAriaAttributes();
    }

    function focusFirstLinkInToc() {
      $('a', $tocList).first().focus();
    }

    function toggleBackgroundVisiblity(visibility) {
      $('.toc-open-disabled').attr('aria-hidden', visibility ? '' : 'true');
    }

    function updateAriaAttributes() {
      var tocIsVisible = $toc.is(':visible');

      $($openLink).add($closeLink)
        .attr('aria-expanded', tocIsVisible ? 'true' : 'false');

      $toc.attr('aria-hidden', tocIsVisible ? 'false' : 'true');
    }

    function preventingScrolling(callback) {
      return function (event) {
        event.preventDefault();
        callback();
      }
    }
  };
})(jQuery, window.GOVUK.Modules);