contract BloodShedBearsTreeHouse is ERC721Enum, VRFConsumerBase, Ownable, Pausable {
    using Strings for uint256;

    //sale settings
    uint256 constant private maxSupply = 7000;
    uint256 private teamReserveAvailableMints = 100;
    uint256 private cost = 50000 ether;
    address public bloodTokenContract = 0x0000000000000000000000000000000000000000;
    address public gameStatsContract = 0x0000000000000000000000000000000000000000;

    string private baseURI;

    uint256 private vrfFee;
    bytes32 private vrfKeyHash;

    uint256 private seed;

    event SeedFulfilled();

    constructor(
        string memory initBaseURI_,
        address vrfCoordinatorAddr_,
        address linkTokenAddr_,
        bytes32 vrfKeyHash_,
        uint256 fee_
    )
    VRFConsumerBase(vrfCoordinatorAddr_, linkTokenAddr_)
    ERC721P("Bloodshed Bears TreeHouse", "BSBTH") {
        setBaseURI(initBaseURI_);
        vrfKeyHash = vrfKeyHash_;
        vrfFee = fee_;
       pause();
    }

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

    function setBloodTokenContract(address contractAddress_) public onlyOwner {
        bloodTokenContract = contractAddress_;
    }

    function setGameStatsContract(address contractAddress_) public onlyOwner {
        gameStatsContract = contractAddress_;
    }

    function setCost(uint256 _newCost) external onlyOwner {
        cost = _newCost;
    }

    function mint(uint256 mintAmount_) external payable whenNotPaused {
        require(seed != 0, "Seed not set");

        uint256 totalSupply = totalSupply();

        require(totalSupply + mintAmount_ <= maxSupply, "TOO MANY");
        require(IBloodTokenContract(bloodTokenContract).walletsBalances(msg.sender) >= mintAmount_ * cost, "NOT ENOUGH");

        IBloodTokenContract(bloodTokenContract).spend(msg.sender, mintAmount_ * cost);

        uint256[] memory tokens = new uint256[](mintAmount_);
        uint256[] memory levels = new uint256[](mintAmount_);

        for (uint256 i = 0; i < mintAmount_; i++) {
            tokens[i] = totalSupply + i;
            levels[i] = generateHouseLevel(totalSupply + i) + 1;
            _safeMint(msg.sender, totalSupply + i);
        }

        IGameStats(gameStatsContract).setHousesLevels(tokens, levels);

        delete totalSupply;
        delete tokens;
        delete levels;
    }

    function tokenURI(uint256 _tokenId) external view virtual override returns (string memory) {
        require(_exists(_tokenId), "ERC721Metadata: Nonexistent token");
        string memory currentBaseURI = _baseURI();

        return bytes(currentBaseURI).length > 0	? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), ".json")) : "";
    }

    function reserve(address _to, uint256 _reserveAmount) public onlyOwner {
        require(seed != 0, "Seed not set");
        uint256 supply = totalSupply();
        require(
            _reserveAmount > 0 && _reserveAmount <= teamReserveAvailableMints,
            "Not enough reserve left for team"
        );

        uint256[] memory tokens = new uint256[](_reserveAmount);
        uint256[] memory levels = new uint256[](_reserveAmount);
        for (uint256 i = 0; i < _reserveAmount; i++) {
            tokens[i] = supply + i;
            levels[i] = generateHouseLevel(supply + i) + 1;
            _safeMint(_to, supply + i);
        }

        IGameStats(gameStatsContract).setHousesLevels(tokens, levels);

        teamReserveAvailableMints -= _reserveAmount;

        delete supply;
        delete tokens;
        delete levels;
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    function isOwnerOfBatch(uint256[] calldata tokenIds_, address address_) external view returns (bool) {
        bool ownership = true;

        for (uint256 i = 0; i < tokenIds_.length; ++i) {
            ownership = ownership && (ownerOf(tokenIds_[i]) == address_);
        }

        return ownership;
    }

    function generateHouseLevel(uint256 tokenId) public view returns (uint256) {
        return uint256(keccak256(abi.encodePacked(seed, tokenId, tx.origin, blockhash(block.number - 1), block.timestamp))) % 3;
    }

    function initSeedGeneration() public onlyOwner returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) >= vrfFee, "Not enough LINK");
        return requestRandomness(vrfKeyHash, vrfFee);
    }

    function fulfillRandomness(bytes32, uint256 randomness) internal override {
        seed = randomness;
        emit SeedFulfilled();
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

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

// internal