contract ButtonNFT is ERC721Burnable, AccessControl {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdTracker;
    string private _baseTokenURI;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    uint256 private constant FL_WINNER = 1;

    uint256 private seed;

    bool private paused;

    mapping(uint256 => uint256) private cosmeticData;

    constructor(string memory baseTokenURI) public ERC721("Button", "BUTTON") {
        _baseTokenURI = baseTokenURI;

        // Allow upgrade of the game while allowing everyone to keep their collectibles
        _setupRole(DEFAULT_ADMIN_ROLE, tx.origin);
        _setupRole(MINTER_ROLE, msg.sender);
    }

    function baseURI() public view override returns (string memory) {
        return _baseTokenURI;
    }

    // Allow root to change the base URI (in case of off-chain problems like the domain dying)
    function setBaseURI(string memory _baseURI) public {
        require (hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
        _setBaseURI(_baseURI);
    }

    // Allow root to pause minting (effectively curtailing the supply)
    function pause(bool _paused) public {
        require (hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
        paused = _paused;
    }

    function mint(address to, uint256 pressTime) public returns (uint256) {
        require (hasRole(MINTER_ROLE, msg.sender));
        require (!paused, "Minting paused");
        uint256 currentId = _tokenIdTracker.current();
        uint256 float = uint256(keccak256(abi.encodePacked(seed, currentId, to, pressTime, block.coinbase, now)));
        seed = float;
        // store some metadata in lower bits
        float = (float & uint256(~0xffffffffffffffffff)) | ((block.number & 0xffffffff) << 8) | ((pressTime & 0xffffffff) << 40);
        _mint(to, currentId);
        cosmeticData[currentId] = float;
        _tokenIdTracker.increment();
        return currentId;
    }

    function setWinner(uint256 tokenId) public {
        require (hasRole(MINTER_ROLE, msg.sender));

        cosmeticData[tokenId] |= FL_WINNER;
    }

    function exists(uint256 tokenId) public view returns (bool) {
        return _exists(tokenId);
    }

    function tokenInfoOf(uint256 tokenId) public view returns (uint256) {
        uint256 float = cosmeticData[tokenId];
        require (tx.origin == msg.sender || block.number != ((float >> 8) & 0xffffffff));
        return float;
    }
}
function pause(bool _paused) public {
    require (hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
    paused = _paused;
}

// Allow root to pause minting (effectively curtailing the supply)