;(function($, window, document, undefined) {
  "use strict";
  var pluginName = "permissions",
    defaults = {
      lang: {
        selectAll: "Select All",
        deselectAll: "Deselect All"
      }
    };

  // The actual plugin constructor
  function Plugin (element, options) {
    this.element = element;
    this.settings = $.extend({}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

  // Avoid Plugin.prototype conflicts
  $.extend(Plugin.prototype, {
    init: function() {
      $('.permissions-group-checkbox').on('change', $.proxy(this.onGroupToggle, this));
      $('.permissions-all-toggle').on('change', $.proxy(this.onAllToggle, this));
      $('.permission-checkbox').on('change', $.proxy(this.onPermissionToggle, this));

      var $this = $(this.element);
      var group = $this.data('id');
      var total = this.getTotalPermissionCountForGroup(group);
      var checked = this.getCheckedPermissionsCountForGroup(group);

      this.setCheckedPermissionsCountForGroup(group);

      if (total === checked) {
        $this.find('.permissions-group-checkbox').prop('checked', true).trigger('change');
      }
    },
    onGroupToggle: function (evt) {
      var $this = $(evt.currentTarget);
      var group = $this.data('toggle-group');
      var $label = $this.siblings('.toggle-label:first');
      var $permissions = $('input[type=checkbox][data-group=' + group + ']');
      var checkbox = $this.get(0);

      $permissions.prop('checked', checkbox.checked);
      $label.text(checkbox.checked ? this.settings.lang.deselectAll : this.settings.lang.selectAll);
      this.setCheckedPermissionsCountForGroup(group);

      // Update the global state.
      this.updateGlobalToggle();
    },
    onAllToggle: function (evt) {
      var $this = $(evt.currentTarget);
      var $toggles = $('.permissions-group-toggle').find('input[type=checkbox]');
      var $label = $this.find('.toggle-label:first');
      var checkbox = $this.find('input[type=checkbox]').get(0);

      if (checkbox.checked) {
        $toggles.prop('checked', true).trigger('change');
        $label.text('Deselect All');
      } else if (checkbox.indeterminate) {
        $toggles.prop('checked', false).trigger('change');
        $label.text('Select All');
      } else {
        $toggles.prop('checked', false).trigger('change');
        $label.text('Select All');
      }
    },
    onPermissionToggle: function (evt) {
      this.setCheckedPermissionsCountForGroup($(evt.currentTarget).data('group'));
    },
    setCheckedPermissionsCountForGroup: function (group) {
      var checked = this.getCheckedPermissionsCountForGroup(group);
      var total = this.getTotalPermissionCountForGroup(group);
      $('#count-' + group).text(checked + '/' + total);
    },
    getCheckedPermissionsCountForGroup: function (group) {
      return $('.permission-group[data-id=' + group + '] input[type=checkbox][data-group=' + group + ']:checked').length;
    },
    getTotalPermissionCountForGroup: function (group) {
      return $('.permission-group[data-id=' + group + '] input[type=checkbox][data-group=' + group + ']').length;
    },
    updateGlobalToggle: function () {
      var $global = $('.permissions-all-toggle');
      var global = $global.find('input[type=checkbox]').get(0);
      var $toggles = $('.permissions-group-toggle').find('input[type=checkbox]');
      var toggledOn = $toggles.get().filter(function (toggle) {
        return toggle.checked === true;
      });

      // Determine whether all toggles are on or none of the toggles are on.
      if (toggledOn.length > 0 && toggledOn.length < $toggles.length) {
        global.indeterminate = true;
        return;
      }

      // Turn off the indeterminate state since we know the state can now be determined.
      global.indeterminate = false;

      // Determine whether all of the toggles are selected.
      if (toggledOn.length === $toggles.length) {
        global.checked = true;
        $global.find('.toggle-label:first').text(this.settings.lang.deselectAll);
      }

      // Determine whether none of the toggles are selected.
      if (toggledOn.length === 0) {
        global.checked = false;
        $global.find('.toggle-label:first').text(this.settings.lang.selectAll);
      }
    }
  });

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (! $.data(this, "plugin_" + pluginName)) {
        $.data(this, "plugin_" + pluginName, new Plugin(this, options));
      }
    });
  };

})(jQuery, window, document);
