contract ZombieMice is ERC721Enumerable, Ownable {
    address public anonymiceAddress = 0xbad6186E92002E312078b5a1dAfd5ddf63d3f731;
    address public cheethAddress = 0x5f7BA84c7984Aa5ef329B66E313498F0aEd6d23A;
    uint public maxTokens = 6450;
    uint public mintingCost = 0.02 ether;
    string private baseTokenURI;

    uint public miceClaimed = 0;
    uint public maxMiceClaimed = 3550;

    uint public normalMinted = 0;
    uint public maxNormalMinted = 2900;
    uint public maxMintPerTx = 10;

    mapping(uint => uint) miceUsedToClaim;
    
    event Mint(address to, uint tokenId);
    event MintWithMice(address to, uint tokenId);

    constructor() ERC721("Zombiemice", "ZMICE") {}

    // modifiers
    bool claimIsEnabled;

    modifier claimEnabled {
        require(claimIsEnabled);
        _;
    }
    
    modifier onlySender() {
        require(msg.sender == tx.origin);
        _;
    }

    // owner functions
    function setClaimStatus(bool bool_) external onlyOwner {
        claimIsEnabled = bool_;
    }
    
    function setClaimedMiceToMintable() external onlyOwner {
        maxNormalMinted = maxNormalMinted + (maxMiceClaimed - miceClaimed);
    }
    
    function withdrawEther() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }
    
    function setBaseTokenURI(string memory uri_) external onlyOwner {
        baseTokenURI = uri_;
    }

    // functions
    function mintWithStakedMice(uint tokenId_) external onlySender claimEnabled {
        require(msg.sender == ICheeth(cheethAddress).getStaker(tokenId_), "You are not the staker!");
        require(miceUsedToClaim[tokenId_] == 0, "Mice already used to claim!");
        require(miceClaimed + 1 <= maxMiceClaimed, "No more claimable mice!");

        uint _mintId = totalSupply();
        miceUsedToClaim[tokenId_]++;
        miceClaimed++;

        _mint(msg.sender, _mintId);
        emit MintWithMice(msg.sender, _mintId);
    }
    
    function mintWithMice(uint tokenId_) external onlySender claimEnabled {
        require(msg.sender == IERC721(anonymiceAddress).ownerOf(tokenId_), "You are not the owner!");
        require(miceUsedToClaim[tokenId_] == 0, "Mice already used to claim!");
        require(miceClaimed + 1 <= maxMiceClaimed, "No more claimable mice!");

        uint _mintId = totalSupply();
        miceUsedToClaim[tokenId_]++;
        miceClaimed++;

        _mint(msg.sender, _mintId);
        emit MintWithMice(msg.sender, _mintId);
    }
    
    function mint(uint amount_) payable external onlySender {
        require(amount_ <= maxMintPerTx, "Over Maximum Mints per TX!");
        require(msg.value == (mintingCost * amount_), "Invalid value!");
        require(normalMinted + amount_ <= maxNormalMinted, "No more mintable mice!");

        uint _startMintId = totalSupply();
        normalMinted = normalMinted + amount_;

        // this mints the amount of tokens
        for (uint i = 0; i < amount_; i++) {
            uint _mintId = _startMintId + i;
            _mint(msg.sender, _mintId);
            emit Mint(msg.sender, _mintId);
        }
    }

    // view functions
    function tokenURI(uint tokenId_) public view override returns (string memory) {
        return string(abi.encodePacked(baseTokenURI, Strings.toString(tokenId_)));
    }
}
function mintWithStakedMice(uint tokenId_) external onlySender claimEnabled {
    require(msg.sender == ICheeth(cheethAddress).getStaker(tokenId_), "You are not the staker!");
    require(miceUsedToClaim[tokenId_] == 0, "Mice already used to claim!");
    require(miceClaimed + 1 <= maxMiceClaimed, "No more claimable mice!");

    uint _mintId = totalSupply();
    miceUsedToClaim[tokenId_]++;
    miceClaimed++;

    _mint(msg.sender, _mintId);
    emit MintWithMice(msg.sender, _mintId);
}

// functions