contract Token is ERC721, ERC721Enumerable, Pausable, AccessControl {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    string public baseURI;

    constructor(string memory name_, string memory symbol_, string memory baseURI_) ERC721(name_, symbol_) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);

        baseURI = baseURI_;
    }

    function setBaseURI(string memory baseURI_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        baseURI = baseURI_;
    }

    function _baseURI() internal view override returns (string memory) {
        return baseURI;
    }

    function pause() external onlyRole(PAUSER_ROLE) {
        _pause();
    }

    function unpause() external onlyRole(PAUSER_ROLE) {
        _unpause();
    }

    function safeMint(address to, uint256 tile) external onlyRole(MINTER_ROLE) {
        _safeMint(to, tile);
    }

    function safeMintRange(address to, uint256 minX, uint256 minY, uint256 maxX, uint256 maxY) external onlyRole(MINTER_ROLE) {
        require((minX <= maxX) && (minY <= maxY), "Token: invalid parameters");
        require((minX != maxX) || (minY != maxY), "Token: please use non range version");

        for (uint256 x = minX; x <= maxX; x += 1) {
            for (uint256 y = minY; y <= maxY; y += 1) {
                _safeMint(to, makeTile(x, y));
            }
        }
    }

    function makeTile(uint256 x, uint256 y) internal pure returns(uint256) {
        return (x << 128) | y;
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        whenNotPaused
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    // The following functions are overrides required by Solidity.

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable, AccessControl)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
function supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721, ERC721Enumerable, AccessControl)
    returns (bool)
{
    return super.supportsInterface(interfaceId);
}

// The following functions are overrides required by Solidity.