contract ERC721PresetMinterPauserAutoId is
    ContextUtils,
    VRFConsumerBase,
    AccessControlEnumerable,
    ERC721Enumerable,
    ERC721Burnable,
    ERC721Pausable,
    ERC721URIStorage
{
    using Counters for Counters.Counter;

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

    Counters.Counter private _tokenIdTracker;

    string private _baseTokenURI;

    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public randomResult;
    address public VRFCoordinator;
    address public LinkToken;

    mapping(bytes32 => uint256) requestToTokenId;
    mapping(bytes32 => uint256) requestToType; // 0 - simple, 1 - exclusive
    mapping(uint256 => uint256) public tokenPower;

    modifier onlyLink() {
        require(msg.sender == LinkToken, "ERC677: must be Link token");
        _;
    }

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
     * See {ERC721-tokenURI}.
     */

    constructor(
        string memory name,
        string memory symbol,
        string memory baseTokenURI,
        address _VRFCoordinator,
        address _LinkToken,
        bytes32 _keyhash,
        uint256 _fee,
        uint256 _decimals
    ) VRFConsumerBase(_VRFCoordinator, _LinkToken) ERC721(name, symbol) {
        _baseTokenURI = baseTokenURI;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());

        VRFCoordinator = _VRFCoordinator;
        LinkToken = _LinkToken;
        keyHash = _keyhash;
        fee = _fee * 10**_decimals; // 2 LINK
    }

    function requestNewRandom(address _sender, uint256 _type)
        public
        returns (bytes32)
    {
        require(
            IERC20(LinkToken).balanceOf(address(this)) >= fee,
            "Not enough LINK - fill contract"
        );
        bytes32 requestId = requestRandomness(keyHash, fee);
        requestToTokenId[requestId] = _tokenIdTracker.current();
        requestToType[requestId] = _type;
        return requestId;
    }

    function fulfillRandomness(bytes32 requestId, uint256 randomness)
        internal
        override
    {
        if (requestToType[requestId] > 0) {
            tokenPower[requestToTokenId[requestId]] = (randomness == 0 ||
                randomness % 5 == 0)
                ? 6
                : (randomness % 8 < 6 ? 7 : randomness % 8);
        } else {
            tokenPower[requestToTokenId[requestId]] = (randomness == 0 ||
                randomness % 5 == 0)
                ? 1
                : randomness % 5;
        }
    }

    function onTokenTransfer(
        address _sender,
        uint256 _value,
        bytes memory _data
    ) public onlyLink {
        requestNewRandom(_sender, 0);

        _mint(_sender, _tokenIdTracker.current());
        _setTokenURI(_tokenIdTracker.current(), string(_data));
        _updateTokenCreator(_tokenIdTracker.current(), payable(_sender));
        _tokenIdTracker.increment();
    }

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

    /**
     * @dev Creates a new token for `to`. Its token ID will be automatically
     * assigned (and available on the emitted {IERC721-Transfer} event), and the token
     * URI autogenerated based on the base URI passed at construction.
     *
     * See {ERC721-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, string memory _tokenURI) public virtual {
        require(
            hasRole(MINTER_ROLE, _msgSender()),
            "ERC721PresetMinterPauserAutoId: must have minter role to mint"
        );

        requestNewRandom(to, 0);

        _mint(to, _tokenIdTracker.current());
        _setTokenURI(_tokenIdTracker.current(), _tokenURI);
        _updateTokenCreator(_tokenIdTracker.current(), payable(to));
        _tokenIdTracker.increment();
    }

    function mintExclusive(address to, string memory _tokenURI) public virtual {
        require(
            hasRole(MINTER_ROLE, _msgSender()),
            "ERC721PresetMinterPauserAutoId: must have minter role to mint"
        );

        requestNewRandom(to, 1);

        _mint(to, _tokenIdTracker.current());
        _setTokenURI(_tokenIdTracker.current(), _tokenURI);
        _updateTokenCreator(_tokenIdTracker.current(), payable(to));
        _tokenIdTracker.increment();
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721Burnable: caller is not owner nor approved"
        );
        _setTokenURI(tokenId, _tokenURI);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(
            hasRole(PAUSER_ROLE, _msgSender()),
            "ERC721PresetMinterPauserAutoId: must have pauser role to pause"
        );
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(
            hasRole(PAUSER_ROLE, _msgSender()),
            "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"
        );
        _unpause();
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721URIStorage: URI query for nonexistent token"
        );

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }

        return super.tokenURI(tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId)
        internal
        virtual
        override(NFT721Creator, ERC721URIStorage)
    {
        super._burn(tokenId);

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }
    }

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

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(AccessControlEnumerable, ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function adminWithdrawChainlink(address _destination)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(
            IERC20(LinkToken).balanceOf(address(this)) > 0,
            "No excess LINK"
        );
        IERC20(LinkToken).transfer(
            _destination,
            IERC20(LinkToken).balanceOf(address(this))
        );
    }

    function adminSetLinkToken(address _newLinkToken)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        LinkToken = _newLinkToken;
    }

    function adminSetVRFCoordinator(address _newVRFCoordinator)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        VRFCoordinator = _newVRFCoordinator;
    }

    function adminSetKeyHash(bytes32 _newKeyHash)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        keyHash = _newKeyHash;
    }
}
/**
 * @dev {ERC721} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *  - token ID and URI autogeneration
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 */
function unpause() public virtual {
    require(
        hasRole(PAUSER_ROLE, _msgSender()),
        "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"
    );
    _unpause();
}

/**
 * @dev Unpauses all token transfers.
 *
 * See {ERC721Pausable} and {Pausable-_unpause}.
 *
 * Requirements:
 *
 * - the caller must have the `PAUSER_ROLE`.
 */