contract AdminRole {
    using Role for Role.RoleContainer;
    using Votings for Votings.Voting;

    //admin storage
    Role.RoleContainer private _admins;

    //voting storage
    Votings.Voting private _addVoting;
    Votings.Voting private _expelVoting;

    /**
     * @dev events for "add admin" action logging
     * @param account New admin
     */
    event AdminAdded(address indexed account);

    /**
     * @dev events for "expel admin" action logging
     * @param account Expelled admin
     */
    event AdminRemoved(address indexed account);

    modifier AdminOnly() {
        require(isAdmin(msg.sender));
        _;
    }

    modifier WhileSetup() {
        require(isAdmin(msg.sender));
        require(countAdmins() == 1);
        _;
    }

    constructor () internal {
        _add(msg.sender);
    }

    /**
     * @param account to check
     * @return is this account contains in admin list
     */
    function isAdmin(address account) public view returns (bool) {
        return _admins.has(account);
    }

    /**
     * @return list of admins
     */
    function listAdmins() public view returns (address[] memory) {
        return _admins.bearer;
    }

    /**
     * @return amount of admins
     */
    function countAdmins() public view returns (uint) {
        return _admins.total();
    }

    /**
     * @dev initialize admin list while setup-stage of sale
     * @param defaultAdmins list of default admins
     */
    function initAdmins(address[] memory defaultAdmins) WhileSetup internal {
        for (uint256 index = 0; index < defaultAdmins.length; index++) {
            _add(defaultAdmins[index]);
        }
    }

    /**
     * @dev Vote and append given account to the admin list after consensus
     * @param account Account to be appended
     */
    function addAdmin(address account) AdminOnly public {
        if (_addAdminVoting(account)) {
            _add(account);
        }
    }

    /**
     * @dev Vote and remove given account from admin list after consensus
     * @param account Account to be removed
     */
    function expelAdmin(address account) AdminOnly public {
        if (_expelAdminVoting(account)) {
            _expel(account);
        }
    }


    /**
     * @dev require (N/2)+1 admins to be agreed with the add proposal
     * @param account Account to be appended
     * @return do (N/2)+1  admins agreed with the proposal?
     */
    function _addAdminVoting(address account) private returns (bool) {
        return _addVoting.voteAndCheck(
            Helpers.idFromAddress(account),
            msg.sender,
            Helpers.majority(countAdmins())
        );
    }

    /**
     * @dev require (N/2)+1 admins to be agreed with the removal proposal
     * @param account Account to be removed
     * @return do (N/2)+1  admins agreed with the proposal?
     */
    function _expelAdminVoting(address account) private returns (bool) {
        require(msg.sender != account);
        return _expelVoting.voteAndCheck(
            Helpers.idFromAddress(account),
            msg.sender,
            Helpers.majority(countAdmins())
        );
    }


    /**
     * @dev appends given account to admin list
     * @param account Account to be appended
     */
    function _add(address account) private {
        _admins.add(account);
        emit AdminAdded(account);
    }

    /**
     * @dev removes given account to admin list
     * @param account Account to be excluded
     */
    function _expel(address account) private {
        _admins.remove(account);
        emit AdminRemoved(account);
    }


}
function listAdmins() public view returns (address[] memory) {
    return _admins.bearer;
}

/**
 * @return list of admins
 */