contract PermissionManager is IPermissionManager, Ownable {
  struct UserData {
    uint256 permissions;
    address permissionAdmin;
  }

  mapping(address => UserData) _users;
  mapping(address => uint256) _permissionsAdmins;

  uint256 public constant MAX_NUM_OF_ROLES = 256;

  modifier onlyPermissionAdmins(address user) {
    require(_permissionsAdmins[user] > 0, 'CALLER_NOT_PERMISSIONS_ADMIN');
    _;
  }

  ///@inheritdoc IPermissionManager
  function addPermissionAdmins(address[] calldata admins) external override onlyOwner {
    for (uint256 i = 0; i < admins.length; i++) {
      _permissionsAdmins[admins[i]] = 1;

      emit PermissionsAdminSet(admins[i], true);
    }
  }

  ///@inheritdoc IPermissionManager
  function removePermissionAdmins(address[] calldata admins) external override onlyOwner {
    for (uint256 i = 0; i < admins.length; i++) {
      _permissionsAdmins[admins[i]] = 0;

      emit PermissionsAdminSet(admins[i], false);
    }
  }

  ///@inheritdoc IPermissionManager
  function addPermissions(uint256[] calldata roles, address[] calldata users)
    external
    override
    onlyPermissionAdmins(msg.sender)
  {
    require(roles.length == users.length, 'INCONSISTENT_ARRAYS_LENGTH');

    for (uint256 i = 0; i < users.length; i++) {
      uint256 role = roles[i];
      address user = users[i];

      require(role < MAX_NUM_OF_ROLES, 'INVALID_ROLE');

      uint256 permissions = _users[user].permissions;
      address permissionAdmin = _users[user].permissionAdmin;

      require(
        (permissions != 0 && permissionAdmin == msg.sender) ||
          _users[user].permissionAdmin == address(0),
        'INVALID_PERMISSIONADMIN'
      );

      if (permissions == 0) {
        _users[user].permissionAdmin = msg.sender;
      }

      _users[user].permissions = permissions | (1 << role);

      emit RoleSet(user, role, msg.sender, true);
    }
  }

  ///@inheritdoc IPermissionManager
  function removePermissions(uint256[] calldata roles, address[] calldata users)
    external
    override
    onlyPermissionAdmins(msg.sender)
  {
    require(roles.length == users.length, 'INCONSISTENT_ARRAYS_LENGTH');

    for (uint256 i = 0; i < users.length; i++) {
      uint256 role = roles[i];
      address user = users[i];

      require(role < MAX_NUM_OF_ROLES, 'INVALID_ROLE');

      uint256 permissions = _users[user].permissions;
      address permissionAdmin = _users[user].permissionAdmin;

      require(
        (permissions != 0 && permissionAdmin == msg.sender) ||
          _users[user].permissionAdmin == address(0),
        'INVALID_PERMISSIONADMIN'
      );

      _users[user].permissions = permissions & ~(1 << role);

      if (_users[user].permissions == 0) {
        //all permission have been removed
        _users[user].permissionAdmin = address(0);
      }

      emit RoleSet(user, role, msg.sender, false);
    }
  }

  ///@inheritdoc IPermissionManager
  function getUserPermissions(address user)
    external
    view
    override
    returns (uint256[] memory, uint256)
  {
    uint256[] memory roles = new uint256[](256);
    uint256 rolesCount = 0;
    uint256 userPermissions = _users[user].permissions;

    for (uint256 i = 0; i < 256; i++) {
      if ((userPermissions >> i) & 1 > 0) {
        roles[rolesCount] = i;
        rolesCount++;
      }
    }

    return (roles, rolesCount);
  }

  ///@inheritdoc IPermissionManager
  function isInRole(address user, uint256 role) external view override returns (bool) {
    return (_users[user].permissions >> role) & 1 > 0;
  }

  ///@inheritdoc IPermissionManager
  function isInAllRoles(address user, uint256[] calldata roles)
    external
    view
    override
    returns (bool)
  {
    for (uint256 i = 0; i < roles.length; i++) {
      if ((_users[user].permissions >> roles[i]) & 1 == 0) {
        return false;
      }
    }
    return true;
  }

  ///@inheritdoc IPermissionManager
  function isInAnyRole(address user, uint256[] calldata roles)
    external
    view
    override
    returns (bool)
  {
    for (uint256 i = 0; i < roles.length; i++) {
      if ((_users[user].permissions >> roles[i]) & 1 > 0) {
        return true;
      }
    }
    return false;
  }

  ///@inheritdoc IPermissionManager
  function isPermissionsAdmin(address admin) public view override returns (bool) {
    return _permissionsAdmins[admin] > 0;
  }

  ///@inheritdoc IPermissionManager
  function getUserPermissionAdmin(address user) external view override returns (address) {
    return _users[user].permissionAdmin;
  }

  ///@inheritdoc IPermissionManager
  function isUserPermissionAdminValid(address user) external view override returns (bool) {
    return _permissionsAdmins[_users[user].permissionAdmin] > 0;
  }
}
/**
 * @title PermissionManager contract
 * @notice Implements basic whitelisting functions for different actors of the permissioned protocol
 
 * @author Aave
 **/
function getUserPermissions(address user)
  external
  view
  override
  returns (uint256[] memory, uint256)
{
  uint256[] memory roles = new uint256[](256);
  uint256 rolesCount = 0;
  uint256 userPermissions = _users[user].permissions;

  for (uint256 i = 0; i < 256; i++) {
    if ((userPermissions >> i) & 1 > 0) {
      roles[rolesCount] = i;
      rolesCount++;
    }
  }

  return (roles, rolesCount);
}

///@inheritdoc IPermissionManager