text: 
  import {
    _IDLE_,
    _JOINED_,
    _CALL_,
    _LEFT_,
    MEETING_END_REASON
  } from '../constants';
  import ParameterError from '../common/errors/parameter';
  
  const SelfUtils = {};
  
  /**
   * parses the relevant values for self: muted, guest, moderator, mediaStatus, state, joinedWith, creator, id
   * @param {Object} self
   * @param {String} deviceId
   * @returns {undefined}
   */
  SelfUtils.parse = (self, deviceId) => {
    if (self) {
      return {
        muted: SelfUtils.getMuted(self),
        lastModified: SelfUtils.getLastModified(self),
        modifiedBy: SelfUtils.getModifiedBy(self),
        guest: self.guest,
        moderator: self.moderator,
        mediaStatus: SelfUtils.getStatus(self.status),
        state: self.state,
        // TODO: give a proper name . With same device as login or different login`
        // Some times we might have joined with both mobile and web
        joinedWith: self.devices.find((device) => deviceId === device.url),
        creator: self.isCreator, // check if its used,
        selfId: self.id,
        selfIdentity: SelfUtils.getSelfIdentity(self),
        selfUrl: self.url,
        removed: self.removed
      };
    }
  
    return null;
  };
  
  SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
    const previous = oldSelf && SelfUtils.parse(oldSelf, deviceId);
    const current = newSelf && SelfUtils.parse(newSelf, deviceId);
    const updates = {};
  
    updates.isUnadmittedGuest = SelfUtils.isUnadmittedGuest(current);
    updates.isAdmittedGuest = SelfUtils.isAdmittedGuest(previous, current);
    updates.isMutedByOthers = SelfUtils.mutedByOthers(previous, current);
    updates.moderatorChanged = SelfUtils.moderatorChanged(previous, current);
    updates.isMediaInactiveOrReleased = SelfUtils.wasMediaInactiveOrReleased(previous, current);
  
    return {
      previous,
      current,
      updates
    };
  };
  
  SelfUtils.getLastModified = (self) => {
    if (!self || !self.controls || !self.controls.audio || !self.controls.audio.meta || !self.controls.audio.meta.lastModified) {
      return null;
    }
  
    return self.controls.audio.meta.lastModified;
  };
  
  SelfUtils.getModifiedBy = (self) => {
    if (!self || !self.controls || !self.controls.audio || !self.controls.audio.meta || !self.controls.audio.meta.modifiedBy) {
      return null;
    }
  
    return self.controls.audio.meta.modifiedBy;
  };
  
  /**
   * get the id from the self object
   * @param {Object} self
   * @returns {String}
   */
  SelfUtils.getSelfIdentity = (self) => {
    if (!self && !self.person) {
      return null;
    }
  
    return self.person.id;
  };
  
  /**
   * get the muted property from the self object
   * @param {Object} self
   * @returns {Boolean}
   */
  SelfUtils.getMuted = (self) => {
    if (!self || !self.controls || !self.controls.audio) {
      return null;
    }
  
    return self.controls.audio.muted;
  };
  
  SelfUtils.getStatus = (status) => ({
    audio: status.audioStatus,
    video: status.videoStatus,
    slides: status.videoSlidesStatus
  });
  
  
  /**
   * @param {Object} oldSelf
   * @param {Object} changedSelf
   * @returns {Boolean}
   */
  SelfUtils.wasMediaInactiveOrReleased = (oldSelf = {}, changedSelf) => oldSelf.joinedWith && oldSelf.joinedWith.state === _JOINED_ && changedSelf.joinedWith.state === _LEFT_ &&
   (changedSelf.joinedWith.reason === MEETING_END_REASON.INACTIVE || changedSelf.joinedWith.reason === MEETING_END_REASON.MEDIA_RELEASED);
  
  
  /**
   * @param {Object} check
   * @returns {Boolean}
   */
  SelfUtils.isLocusGuestUnadmitted = (check) => check && check.guest && check.state === _IDLE_;
  
  /**
   * @param {Object} check
   * @returns {Boolean}
   */
  SelfUtils.isLocusGuestAdmitted = (check) => check && check.guest && check.state === _JOINED_;
  
  /**
   * @param {Object} self
   * @returns {Boolean}
   * @throws {Error} when self is undefined
   */
  SelfUtils.isUnadmittedGuest = (self) => {
    if (!self) {
      throw new ParameterError('self must be defined to determine if self is unadmitted as guest.');
    }
  
    return SelfUtils.isLocusGuestUnadmitted(self);
  };
  
  SelfUtils.moderatorChanged = (oldSelf, changedSelf) => {
    if (!oldSelf) {
      return true;
    }
    if (!changedSelf) {
      throw new ParameterError('New self must be defined to determine if self transitioned moderator status.');
    }
  
    return oldSelf.moderator !== changedSelf.moderator;
  };
  
  /**
   * @param {Object} oldSelf
   * @param {Object} changedSelf
   * @returns {Boolean}
   * @throws {Error} if changed self was undefined
   */
  SelfUtils.isAdmittedGuest = (oldSelf, changedSelf) => {
    if (!oldSelf) {
      // if there was no previous locus, it couldn't have been admitted yet
      return false;
    }
    if (!changedSelf) {
      throw new ParameterError('New self must be defined to determine if self transitioned to admitted as guest.');
    }
  
    return SelfUtils.isLocusGuestUnadmitted(oldSelf) && SelfUtils.isLocusGuestAdmitted(changedSelf);
  };
  
  SelfUtils.mutedByOthers = (oldSelf = {}, changedSelf) => {
    if (!changedSelf) {
      throw new ParameterError('New self must be defined to determine if self was muted by others.');
    }
  
    return changedSelf.muted && (oldSelf.lastModified !== changedSelf.lastModified || oldSelf.modifiedBy !== changedSelf.modifiedBy);
  };
  
  /**
   * extract the sipUrl from the partner
   * @param {Object} partner
   * @param {Object} info
   * @returns {Object}
   */
  
  SelfUtils.getSipUrl = (partner, type, sipUri) => {
    // For webex meeting the sipUrl gets updated in info parser
    if (partner && type === _CALL_) {
      return {sipUri: partner.person.sipUrl || partner.person.emailAddress || partner.person.email};
    }
  
    return {sipUri};
  };
  
  
  export default SelfUtils;
  
  