contract ImmutablesAdmin is Ownable, ReentrancyGuard {
  using Address for address payable;

  /// @dev Address of a third party curator.
  address public curator;
  /// @dev basis point (1/10,000th) share of third party curator on payout.
  uint16 public curatorPercent;

  /// @dev Address of a third party beneficiary.
  address public beneficiary;
  /// @dev basis point (1/10,000th) share of third party beneficiary on payout.
  uint16 public beneficiaryPercent;

  /// @dev whether or not users need to be pre-screened.
  bool public userScreeningEnabled = false;

  /// @dev Teammember administration mapping
  mapping(address => bool) public isTeammember;

  /// @dev Allowed user administration mapping
  mapping(address => bool) public isAllowedUser;

  /// @dev MODIFIERS

  modifier onlyTeammember() {
      require(isTeammember[msg.sender], "team");
      _;
  }

  modifier onlyAllowedUser() {
    if(userScreeningEnabled) {
      require(isAllowedUser[msg.sender] || isTeammember[msg.sender], "auth");
    }
    _;
  }
  /// @dev EVENTS

  event AdminModifiedTeammembers(
    address indexed user,
    bool isTeammember
  );

  event AdminModifiedAllowedUsers(
    address indexed user,
    bool isAllowedUser
  );

  /** @dev Allows the contract owner to add a teammember.
    * @param _address of teammember to add
    */
  function contractOwnerAddTeammember(address _address) external onlyOwner() {
      isTeammember[_address] = true;
      emit AdminModifiedTeammembers(_address, true);
  }

  /** @dev Allows the contract owner to remove a teammember.
    * @param _address of teammember to remove
    */
  function contractOwnerRemoveTeammember(address _address) external onlyOwner() {
      isTeammember[_address] = false;
      emit AdminModifiedTeammembers(_address, false);
  }

  /** @dev Allows the contract owner to add a user.
    * @param _address of user to add
    */
  function teamAddAllowedUser(address _address) external onlyTeammember() {
      isAllowedUser[_address] = true;
      emit AdminModifiedAllowedUsers(_address, true);
  }

  /** @dev Allows the contract owner to remove a user.
    * @param _address of a user to remove
    */
  function teamRemoveAllowedUser(address _address) external onlyTeammember() {
      isAllowedUser[_address] = false;
      emit AdminModifiedAllowedUsers(_address, false);
  }

  function teamToggleUserScreeningEnabled() external onlyTeammember() {
    userScreeningEnabled = !userScreeningEnabled;
  }

  // FINANCIAL

    /** @dev Allows the contract owner to set a curator address and percentage.
    * @dev Force payout of any curator that was previously set
    * @dev so that funds paid with a curator set are paid out as promised.
    * @param _newCurator address of a curator teammember.
    * @param _newPercent the basis point (1/10,000th) share of contract revenue for the curator.
    */
  function contractOwnerUpdateCuratorAddressAndPercent(address _newCurator, uint16 _newPercent) external onlyOwner() nonReentrant() {
    // checks
    require(_newPercent <= (10000-beneficiaryPercent));

    // effects
    uint256 _startingBalance = address(this).balance;
    uint256 _curatorValue = _startingBalance * curatorPercent / 10000;
    uint256 _beneficiaryValue = _startingBalance * beneficiaryPercent / 10000;
    uint256 _contractValue = _startingBalance - _curatorValue - _beneficiaryValue;

    address oldCurator = curator;
    isTeammember[curator] = false;
    emit AdminModifiedTeammembers(curator, false);
    curator = _newCurator;
    isTeammember[_newCurator] = true;
    emit AdminModifiedTeammembers(_newCurator, true);
    curatorPercent = _newPercent;

    // interactions
    payable(this.owner()).sendValue(_contractValue);
    payable(oldCurator).sendValue(_curatorValue);
    payable(beneficiary).sendValue(_beneficiaryValue);
  }

  /** @dev Allows the contract owner to set a beneficiary address and percentage.
    * @dev Force payout of any beneficiary that was previously set
    * @dev so that funds paid with a beneficiary set are paid out as promised.
    * @param _newBeneficiary address of a beneficiary.
    * @param _newPercent the basis point (1/10,000th) share of contract revenue for the beneficiary.
    */
  function contractOwnerUpdateBeneficiaryAddressAndPercent(address _newBeneficiary, uint16 _newPercent) external onlyOwner() nonReentrant() {
    // checks
    require(_newPercent <= (10000-curatorPercent));

    // effects
    uint256 _startingBalance = address(this).balance;
    uint256 _curatorValue = _startingBalance * curatorPercent / 10000;
    uint256 _beneficiaryValue = _startingBalance * beneficiaryPercent / 10000;
    uint256 _contractValue = _startingBalance - _curatorValue - _beneficiaryValue;
    address oldBeneficiary = beneficiary;
    beneficiary = _newBeneficiary;
    beneficiaryPercent = _newPercent;

    // interactions
    payable(this.owner()).sendValue(_contractValue);
    payable(curator).sendValue(_curatorValue);
    payable(oldBeneficiary).sendValue(_beneficiaryValue);
  }

  /** @dev Allows the withdraw of funds.
    * @dev Everyone is paid and the contract balance is zeroed out.
    */
  function withdraw() external nonReentrant() {
    // checks
    // effects
    uint256 _startingBalance = address(this).balance;
    uint256 _curatorValue = _startingBalance * curatorPercent / 10000;
    uint256 _beneficiaryValue = _startingBalance * beneficiaryPercent / 10000;
    uint256 _contractValue = _startingBalance - _curatorValue - _beneficiaryValue;

    // interactions
    payable(this.owner()).sendValue(_contractValue);
    payable(curator).sendValue(_curatorValue);
    payable(beneficiary).sendValue(_beneficiaryValue);
  }
}
/// @author Gutenblock.eth
/// @title ImmutablesAdmin
function contractOwnerUpdateCuratorAddressAndPercent(address _newCurator, uint16 _newPercent) external onlyOwner() nonReentrant() {
  // checks
  require(_newPercent <= (10000-beneficiaryPercent));

  // effects
  uint256 _startingBalance = address(this).balance;
  uint256 _curatorValue = _startingBalance * curatorPercent / 10000;
  uint256 _beneficiaryValue = _startingBalance * beneficiaryPercent / 10000;
  uint256 _contractValue = _startingBalance - _curatorValue - _beneficiaryValue;

  address oldCurator = curator;
  isTeammember[curator] = false;
  emit AdminModifiedTeammembers(curator, false);
  curator = _newCurator;
  isTeammember[_newCurator] = true;
  emit AdminModifiedTeammembers(_newCurator, true);
  curatorPercent = _newPercent;

  // interactions
  payable(this.owner()).sendValue(_contractValue);
  payable(oldCurator).sendValue(_curatorValue);
  payable(beneficiary).sendValue(_beneficiaryValue);
}

/** @dev Allows the contract owner to set a curator address and percentage.
 * @dev Force payout of any curator that was previously set
 * @dev so that funds paid with a curator set are paid out as promised.
 * @param _newCurator address of a curator teammember.
 * @param _newPercent the basis point (1/10,000th) share of contract revenue for the curator.
 */