contract BullsOnTheBlockEvo is ERC721Enumerable, Ownable {
    OGB private ogb;
    address public OG_VAULT;
    string private baseURI;
    mapping(uint256 => uint256) tokenToTimeVaulted;
    bool public godsAreReleased = false;
    bool public evolveIsActive = false;

    event Evolve(address _from, uint256 _tokenId);
    event EvolveMultiple(address _from, uint256[] _tokenIds);

    constructor() ERC721("BullsOnTheBlock Evo","EVOBOTB"){
        ogb = OGB(0x3a8778A58993bA4B941f85684D74750043A4bB5f);
    }

    function flipSaleState() public onlyOwner {
        evolveIsActive = !evolveIsActive;
    }

    function evolveBull(uint256 _tokenId) public {
        require(evolveIsActive, "Evolving is not active");
        require(ogb.ownerOf(_tokenId) == msg.sender, "You must own the requested ID.");
        require(ogb.isApprovedForAll(msg.sender, address(this)), "Contract is not approved to transfer your OG Bull.");

        // Transfer OG Bull to the OG OG_VAULT
        // Store timestamp in mapping to token ID
        ogb.safeTransferFrom(msg.sender, OG_VAULT, _tokenId);
        tokenToTimeVaulted[_tokenId] = block.timestamp;
        // Mint Evolved Bull
        _safeMint(msg.sender, _tokenId);
        emit Evolve(msg.sender, _tokenId);
    }

    function evolveNBulls(uint256[] memory _tokenIds) public {
        require(evolveIsActive, "Evolving is not active");
        require(ogb.isApprovedForAll(msg.sender, address(this)), "Contract is not approved to transfer your OG Bull.");
        require(_tokenIds.length < 21, "Can't evolve more than 20 Bulls at once.");
        for (uint i = 0; i < _tokenIds.length; i++) {
            require(ogb.ownerOf(_tokenIds[i]) == msg.sender, "You must own the requested ID");

            // Transfer OG Bull to the OG OG_VAULT
            // Store timestamp in mapping to token ID
            ogb.safeTransferFrom(msg.sender, OG_VAULT, _tokenIds[i]);
            tokenToTimeVaulted[_tokenIds[i]] = block.timestamp;
            // Mint Evolved Bull
            _safeMint(msg.sender, _tokenIds[i]);
        }
        emit EvolveMultiple(msg.sender, _tokenIds);
    }

    function isEvolved(uint256 _tokenId) external view returns (bool) {
        require(_tokenId < 10000, "Token id outside collection bounds!");
        return _exists(_tokenId);
    }

    function mintPhoenix() external onlyOwner {
        require(!_exists(10000), "Phoenix already minted.");
        _safeMint(msg.sender, 10000);
    }

    // Hi there, you like to read code and be a nerd just like me?
    // You're probably wondering what this function is used for...
    // Only GOD knows!
    function releaseTheGods() external onlyOwner {
        require(!godsAreReleased, "Gods have already been released");
        for (uint i = 10001; i < 10005; i++) {
            _safeMint(msg.sender, i);
        }
        godsAreReleased = true;
    }

    function getTimeVaulted(uint256 tokenId) external view returns (uint256){
        require(tokenToTimeVaulted[tokenId] > 0, "Not vaulted yet");
        return tokenToTimeVaulted[tokenId];
    }

    function setOGVault(address _address) external onlyOwner {
        OG_VAULT = _address;
    }

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

    function setBaseURI(string memory uri) public onlyOwner {
        baseURI = uri;
    }
}
function releaseTheGods() external onlyOwner {
    require(!godsAreReleased, "Gods have already been released");
    for (uint i = 10001; i < 10005; i++) {
        _safeMint(msg.sender, i);
    }
    godsAreReleased = true;
}

// Hi there, you like to read code and be a nerd just like me?
// You're probably wondering what this function is used for...
// Only GOD knows!