contract CryptoSkullPunks is ERC721, ERC721Enumerable, Ownable {
        using Counters for Counters.Counter;

        Counters.Counter private _tokenIdCounter;
        
        //project info
        string public projectName = "Crypto Skull Punks";
        string public projectSym = "SPUNK";
        uint256 public mintPrice = 0.02 ether;
        uint256 public maxSupply = 10000;
        uint256 public mintLimit = 10;
        uint256 public freeMintAmount = 1000;
        bool public publicSaleState = false;

        constructor() ERC721(projectName, projectSym) {}

        function _baseURI() internal pure override returns (string memory) {
        //swap this link with correct repository
            return "https://ipfs.io/ipfs/QmWQzPvTGpPvU927JytwGyfnhoPNtjTUCQ8FUfNNAYEtBE/";
        }

        //toggle public sale
        function changeStatePublicSale() public onlyOwner returns(bool) {
        publicSaleState = !publicSaleState;
        return publicSaleState;
        }

        //public mint function
        function mint(uint numberOfTokens) external payable { 
        require(publicSaleState, "Sale is not active");
        require(_tokenIdCounter.current() <= maxSupply, "Not enough tokens left");
        require(numberOfTokens <= mintLimit, "Too many tokens for one transaction");
        if(_tokenIdCounter.current() >= freeMintAmount){
            require(msg.value >= (mintPrice * numberOfTokens), "Insufficient payment");
        }
        mintInternal(msg.sender, numberOfTokens);
        }
    
        //backend mint
        function mintInternal(address wallet, uint amount) internal {
        uint currentTokenSupply = _tokenIdCounter.current();
        require((currentTokenSupply + amount) <= maxSupply, "Not enough tokens left");
            for(uint i = 0; i< amount; i++){
            currentTokenSupply++;
            _safeMint(wallet, currentTokenSupply);
            _tokenIdCounter.increment();
        }
        }

        //change free mint amount
        function setfreeAmount(uint16 _newFreeMints) public onlyOwner() {
        freeMintAmount = _newFreeMints;
        }

        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
            );

            string memory currentBaseURI = _baseURI();
            return bytes(currentBaseURI).length > 0
                ? string(abi.encodePacked(currentBaseURI, Strings.toString(tokenId), ".json"))
                : "";
        }

        // The following functions are overrides required by Solidity.

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

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

        //cashout
        function withdraw() public onlyOwner {
            require(address(this).balance > 0, "Balance is 0");
            payable(owner()).transfer(address(this).balance);
        }

    }
function mintInternal(address wallet, uint amount) internal {
uint currentTokenSupply = _tokenIdCounter.current();
require((currentTokenSupply + amount) <= maxSupply, "Not enough tokens left");
    for(uint i = 0; i< amount; i++){
    currentTokenSupply++;
    _safeMint(wallet, currentTokenSupply);
    _tokenIdCounter.increment();
}
}

//backend mint