contract CatshitCrazyNFT is ERC721, ERC721Enumerable,ERC721URIStorage, Ownable {
   using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    
    uint16 public constant maxSupply = 10069;
    string private _baseTokenURI;
    
    uint256 public _freemintStartDate = 1630008000;
    uint256 public _freemintStopDate = 1630051200;
    
    uint256 public _mintForAllStartDate = 1630080000;
    
    address private _frensAddress;
    
    mapping(uint256 => bool) private _freeMinted;

    constructor() ERC721("Catshit Crazy NFT", "CSC") {
    }
    
    function setMintForAllStartDate(uint256 startDate) public onlyOwner {
        _mintForAllStartDate = startDate;
    }
    
    function setFreemintStartDate(uint256 startDate) public onlyOwner {
        _freemintStartDate = startDate;
    }
    
    function setFreemintStopDate(uint256 stopDate) public onlyOwner {
        _freemintStopDate = stopDate;
    }
    
    function setFrensAddress(address frensAddress) public onlyOwner {
        _frensAddress = frensAddress;
    }
    
    function getFreeMintableCount(address userAddress) public view returns(uint256) {
        uint256 _frensMinted = IFrens(_frensAddress).balanceOf(userAddress);
        uint8 _freeMints;
        
        for(uint8 i = 0;i<_frensMinted;i++) {
            uint256 _tokenIndex = IFrens(_frensAddress).tokenOfOwnerByIndex(userAddress, i);
            if(!_freeMinted[_tokenIndex]) {
                _freeMints = _freeMints + 1;
            }
        }
        
        return _freeMints;
    }
    
    function getFirstAvailableFreeMintableFren() private view returns(uint256) {
        uint256 _frensMinted = IFrens(_frensAddress).balanceOf(msg.sender);
        
        for(uint8 i = 0;i<_frensMinted;i++) {
            uint256 _tokenIndex = IFrens(_frensAddress).tokenOfOwnerByIndex(msg.sender, i);
            if(!_freeMinted[_tokenIndex]) {
                return _tokenIndex;
            }
        }
        
        return 0;
    }


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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }


    function setBaseURI(string memory _newbaseTokenURI) public onlyOwner {
        _baseTokenURI = _newbaseTokenURI;
    }

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

    // Get minting limit (for a single transaction) based on current token supply
    function getCurrentMintLimit() public pure returns (uint8) {
        return 20;
    }

    // Get ether price based on current token supply
    function getCurrentPrice() public pure returns (uint64) {
        return 42_000_000_000_000_000;
    }
    
    function mintForPreOwners(uint8 _quantityToMint) public{
        require(_quantityToMint >= 1, "Must mint at least 1");
        require(
            _quantityToMint <= getCurrentMintLimit(),
            "Maximum current buy limit for individual transaction exceeded"
        );
        require(
            (_quantityToMint + totalSupply()) <= maxSupply,
            "Exceeds maximum supply"
        );
        
        require(_freemintStartDate <= block.timestamp, "Free mint is not open yet");
        require(block.timestamp <= _freemintStopDate, "Free mint has finished.");
        
        require(getFreeMintableCount(msg.sender) >= _quantityToMint, "Not enough frens.");
        

        for (uint8 i = 0; i < _quantityToMint; i++) {
            _freeMinted[getFirstAvailableFreeMintableFren()] = true;
            _tokenIds.increment();
            

            uint256 newItemId = _tokenIds.current();
            _mint(msg.sender, newItemId);
        }
    }

    // Mint new token(s)
    function mint(uint8 _quantityToMint) public payable {
        require(_mintForAllStartDate <= block.timestamp, "Sale is not open");
        require(_quantityToMint >= 1, "Must mint at least 1");
        require(
            _quantityToMint <= getCurrentMintLimit(),
            "Maximum current buy limit for individual transaction exceeded"
        );
        require((_quantityToMint + balanceOf(msg.sender)) <= 69, "Max mint per account is 69");
        require(
            (_quantityToMint + totalSupply()) <= maxSupply,
            "Exceeds maximum supply"
        );
        require(
            msg.value == (getCurrentPrice() * _quantityToMint),
            "Ether submitted does not match current price"
        );

        for (uint8 i = 0; i < _quantityToMint; i++) {
            _tokenIds.increment();

            uint256 newItemId = _tokenIds.current();
            _mint(msg.sender, newItemId);
        }
    }
    
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    // Withdraw ether from contract
    function withdraw() public onlyOwner {
        require(address(this).balance > 0, "Balance must be positive");
        (bool success, ) = msg.sender.call{value: address(this).balance}("");
        require(success == true, "Failed to withdraw ether");
    }
}
function withdraw() public onlyOwner {
    require(address(this).balance > 0, "Balance must be positive");
    (bool success, ) = msg.sender.call{value: address(this).balance}("");
    require(success == true, "Failed to withdraw ether");
}

// Withdraw ether from contract