text: /*!
   * Copyright 2014 Apereo Foundation (AF) Licensed under the
   * Educational Community License, Version 2.0 (the "License"); you may
   * not use this file except in compliance with the License. You may
   * obtain a copy of the License at
   *
   *     http://opensource.org/licenses/ECL-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an "AS IS"
   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
   * or implied. See the License for the specific language governing
   * permissions and limitations under the License.
   */
  
  import _ from 'underscore';
  
  /**
   * A Principal object that represents a principal in the roles system.
   *
   * @param  {String}   principalType  The type of this principal (e.g., 'u' for User, 'g' for Group)
   * @param  {String}   tenantAlias    The alias of the tenant to which this principal belongs
   * @param  {String}   principalId    The ID of this principal
   * @return {Object}                  The principal Object
   */
  const Principal = function(principalType, tenantAlias, principalId) {
    const that = {};
  
    that.tenantAlias = tenantAlias;
    that.principalType = principalType;
    that.principalId = principalId;
  
    return that;
  };
  
  /**
   * A Resource object that represents a resource to which a principal may be assigned a role in the system.
   *
   * @param  {String}   resourceType   The type of this principal (e.g., 'g' for Group, 'c' for Content)
   * @param  {String}   tenantAlias    The alias of the tenant to which this resource belongs
   * @param  {String}   resourceId     The ID of this resource
   * @return {Object}                  The resource Object
   */
  const Resource = function(resourceType, tenantAlias, resourceId) {
    const that = {};
  
    that.tenantAlias = tenantAlias;
    that.resourceType = resourceType;
    that.resourceId = resourceId;
  
    return that;
  };
  
  /**
   * An object that represents a target for a share / role change. There are 3 different permutations
   * that have slightly different meanings:
   *
   *  *   Only `principal` is set: The target for the share is some principal (user or group) and the
   *      appropriate interaction checks should be performed for the target
   *  *   Only `email` is set: The target is an email invitation
   *  *   Both `principal` and `email` are set: If the email matches the email of the principal
   *      profile, then interaction checks can be bypassed as the client has successfully
   *      looked up the user by email
   *
   * A `role` can optionally be present with the target, indicating a role change to apply to the
   * target
   *
   * @param  {Principal}  [principal]     The principal profile
   * @param  {String}     [email]         The email address
   * @param  {String}     [role]          The role change to apply, if applicable
   */
  const ShareTarget = function(principal, email, role) {
    return {
      principal,
      email,
      role
    };
  };
  
  /**
   * An object that represents a set of roles associated to a resource, or a set of role changes that
   * wish to be applied on a resource. The object is keyed by some string id of any generic format,
   * while the value is either a string role associated / applied to that id, or `false` to indicate
   * that the id should have its association removed from the target resource
   *
   * @param  {Object}     idRoles     The id roles as described in the summary
   */
  const IdRoles = function(idRoles) {
    return _.extend({}, idRoles);
  };
  
  /**
   * Identical to `IdRoles`, except that the ids in the key are expected to be constrained to resource
   * ids (e.g., u:cam:123abc`)
   *
   * @see IdRoles
   */
  const MemberRoles = function(memberRoles) {
    return _.extend({}, memberRoles);
  };
  
  /**
   * Identical to `IdRoles`, except that the ids in the key are expected to be constrained to email
   * addresses
   *
   * @see IdRoles
   */
  const EmailRoles = function(emailRoles) {
    return _.extend({}, emailRoles);
  };
  
  /**
   * An object that represents the delta state of some bulk id role change for a resource
   *
   * @param  {IdRoles}    changes         The canonical role changes that should be applied to a resource
   * @param  {Object}     roles           An info object that describes the resource roles before and after the `changes` object is applied
   * @param  {IdRoles}    roles.before    The state of the resource roles before the change is applied
   * @param  {IdRoles}    roles.after     The state of the resource roles after the change is applied
   * @param  {Object}     ids             Provides insight into how the state of the id association to the resource changes if the `changes` object is applied
   * @param  {String[]}   ids.added       The ids which are being added to the resource
   * @param  {String[]}   ids.updated     The ids which previously had a role association, and whose roles are changing on the resource
   * @param  {String[]}   ids.removed     The ids which previously had a role association, and who are being removed from the resource
   */
  const IdChangeInfo = function(changes, roles, ids) {
    return {
      changes,
      roles,
      ids
    };
  };
  
  /**
   * Create an empty id change info that represents a resource with no id associations that has no
   * changes applied to it
   *
   * return {IdChangeInfo}    An id change info with all its associations info empty
   */
  IdChangeInfo.empty = function() {
    const changes = {};
    const roles = {
      before: {},
      after: {}
    };
    const ids = {
      added: [],
      updated: [],
      removed: []
    };
  
    return new IdChangeInfo(changes, roles, ids);
  };
  
  /**
   * Similar to IdChangeInfo, however the role changes objects are expected to be constrainted to
   * resource ids, and the added/updated/removed members is expanded to full resources instead of just
   * their ids
   *
   * @param  {MemberRoles}    changes             The canonical member role changes that should be applied
   * @param  {Object}         roles               An info object that describes the resource member roles before and after the `changes` object is applied
   * @param  {MemberRoles}    roles.before        The state of the resource member roles before the change is applied
   * @param  {MemberRoles}    roles.after         The state of the resource member roles after the change is applied
   * @param  {Object}         members             Provides insight into how the state of the members association to the resource changes
   * @param  {Resource[]}     members.added       The members who are being added to the resource
   * @param  {Resource[]}     members.updated     The members who previously had a role, and whose roles are changing on the resource
   * @param  {Resource[]}     members.removed     The members who previously had a role, and who are being removed from the resource
   */
  const MemberChangeInfo = function(changes, roles, members) {
    return {
      changes,
      roles,
      members
    };
  };
  
  /**
   * Create an empty member change info that represents a resource with no members that has no
   * changes applied to it
   *
   * return {MemberChangeInfo}    A member change info with all its membership info empty
   */
  MemberChangeInfo.empty = function() {
    return MemberChangeInfo.fromIdChangeInfo(IdChangeInfo.empty(), {});
  };
  
  /**
   * Convert an id change info into a full member change info, given its full member objects indexed
   * by id
   *
   * @param  {IdChangeInfo}       idChangeInfo    The id change info object to transform
   * @param  {Object}             membersById     A collection of the members that are referenced in the id change info object, indexed by their resource id
   * @return {MemberChangeInfo}                   The member change info
   */
  MemberChangeInfo.fromIdChangeInfo = function(idChangeInfo, membersById) {
    const getMemberById = _.propertyOf(membersById);
    const members = {
      added: _.map(idChangeInfo.ids.added, getMemberById),
      updated: _.map(idChangeInfo.ids.updated, getMemberById),
      removed: _.map(idChangeInfo.ids.removed, getMemberById)
    };
  
    return new MemberChangeInfo(idChangeInfo.changes, idChangeInfo.roles, members);
  };
  
  /**
   * Similar to IdChangeInfo, however the role changes objects are expected to be constrained to
   * email addresses, and the added/updated/removed associations is keyed explicitly by an `emails`
   * key instead of `ids`
   *
   * @param  {EmailRoles}     changes             The canonical invitation role changes that should be applied
   * @param  {Object}         roles               An info object that describes the resource email roles before and after the `changes` object is applied
   * @param  {EmailRoles}     roles.before        The state of the resource email roles before the change is applied
   * @param  {EmailRoles}     roles.after         The state of the resource email roles after the change is applied
   * @param  {Object}         emails              Provides insight into how the state of the email association to the resource changes
   * @param  {String[]}       emails.added        The emails that are being added to the resource
   * @param  {String[]}       emails.updated      The emails that previously had an invitation role, and whose roles are changing on the resource
   * @param  {String[]}       emails.removed      The emails that previously had an invitation role, and are being removed from the resource
   */
  const EmailChangeInfo = function(changes, roles, emails) {
    return {
      changes,
      roles,
      emails
    };
  };
  
  /**
   * Create an empty email change info that represents a resource with no invitations that has no
   * changes applied to it
   *
   * return {IdChangeInfo}    An email change info with all its association info empty
   */
  EmailChangeInfo.empty = function() {
    return EmailChangeInfo.fromIdChangeInfo(IdChangeInfo.empty());
  };
  
  /**
   * Convert an id change info into an email change info
   *
   * @param  {IdChangeInfo}       idChangeInfo    The id change info object to transform
   * @return {EmailChangeInfo}                    The email change info
   */
  EmailChangeInfo.fromIdChangeInfo = function(idChangeInfo) {
    return new EmailChangeInfo(idChangeInfo.changes, idChangeInfo.roles, idChangeInfo.ids);
  };
  
  export {
    Principal,
    Resource,
    ShareTarget,
    IdRoles,
    MemberRoles,
    EmailRoles,
    IdChangeInfo,
    MemberChangeInfo,
    EmailChangeInfo
  };
  