contract PermissionControll {
    // This facet controls access to contract that implements it. There are four roles managed here:
    //
    // - The Admin: The Admin can reassign admin and issuer roles and change the addresses of our dependent smart
    // contracts. It is also the only role that can unpause the smart contract. It is initially
    // set to the address that created the smart contract in the CryptoWarriorCore constructor.
    //
    // - The Bank: The Bank can withdraw funds from CryptoWarriorCore and its auction and battle contracts, and change admin role.
    //
    // - The Issuer: The Issuer can release gen0 warriors to auction.
    //
    // / @dev Emited when contract is upgraded
    event ContractUpgrade(address newContract);
    
    // Set in case the core contract is broken and an upgrade is required
    address public newContractAddress;

    // The addresses of the accounts (or contracts) that can execute actions within each roles.
    address public adminAddress;
    address public bankAddress;
    address public issuerAddress; 
    // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
    bool public paused = false;
    

    // / @dev Access modifier for Admin-only functionality
    modifier onlyAdmin(){
        require(msg.sender == adminAddress);
        _;
    }

    // / @dev Access modifier for Bank-only functionality
    modifier onlyBank(){
        require(msg.sender == bankAddress);
        _;
    }
    
    /// @dev Access modifier for Issuer-only functionality
    modifier onlyIssuer(){
    		require(msg.sender == issuerAddress);
        _;
    }
    
    modifier onlyAuthorized(){
        require(msg.sender == issuerAddress ||
            msg.sender == adminAddress ||
            msg.sender == bankAddress);
        _;
    }


    // / @dev Assigns a new address to act as the Bank. Only available to the current Bank.
    // / @param _newBank The address of the new Bank
    function setBank(address _newBank) external onlyBank {
        require(_newBank != address(0));
        bankAddress = _newBank;
    }

    // / @dev Assigns a new address to act as the Admin. Only available to the current Admin.
    // / @param _newAdmin The address of the new Admin
    function setAdmin(address _newAdmin) external {
        require(msg.sender == adminAddress || msg.sender == bankAddress);
        require(_newAdmin != address(0));
        adminAddress = _newAdmin;
    }
    
    // / @dev Assigns a new address to act as the Issuer. Only available to the current Issuer.
    // / @param _newIssuer The address of the new Issuer
    function setIssuer(address _newIssuer) external onlyAdmin{
        require(_newIssuer != address(0));
        issuerAddress = _newIssuer;
    }

    /*** Pausable functionality adapted from OpenZeppelin ***/
    // / @dev Modifier to allow actions only when the contract IS NOT paused
    modifier whenNotPaused(){
        require(!paused);
        _;
    }

    // / @dev Modifier to allow actions only when the contract IS paused
    modifier whenPaused{
        require(paused);
        _;
    }

    // / @dev Called by any "Authorized" role to pause the contract. Used only when
    // /  a bug or exploit is detected and we need to limit damage.
    function pause() external onlyAuthorized whenNotPaused{
        paused = true;
    }

    // / @dev Unpauses the smart contract. Can only be called by the Admin.
    // / @notice This is public rather than external so it can be called by
    // /  derived contracts.
    function unpause() public onlyAdmin whenPaused{
        // can't unpause if contract was upgraded
        paused = false;
    }
    
    
    /// @dev Used to mark the smart contract as upgraded, in case there is a serious
    ///  breaking bug. This method does nothing but keep track of the new contract and
    ///  emit a message indicating that the new address is set. It's up to clients of this
    ///  contract to update to the new contract address in that case. (This contract will
    ///  be paused indefinitely if such an upgrade takes place.)
    /// @param _v2Address new address
    function setNewAddress(address _v2Address) external onlyAdmin whenPaused {
        newContractAddress = _v2Address;
        ContractUpgrade(_v2Address);
    }
}
function setAdmin(address _newAdmin) external {
    require(msg.sender == adminAddress || msg.sender == bankAddress);
    require(_newAdmin != address(0));
    adminAddress = _newAdmin;
}

// / @dev Assigns a new address to act as the Admin. Only available to the current Admin.
// / @param _newAdmin The address of the new Admin