contract FUNGI is ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    using Strings for uint256;

    uint256 public constant FUNGI_PUBLIC = 1111;
    uint256 public maxTotal = 1111;
    uint256 public constant FUNGI_MAX = FUNGI_PUBLIC;
    uint256 public constant PURCHASE_LIMIT = 100;
    uint256 public allowListMaxMint = 5;
    uint256 public constant PRICE = 40_000_000_000_000_000; // 0.04 ETH
    string private _contractURI = "";
    string private _tokenBaseURI = "";
    bool private _isActive = false;
    bool public isAllowListActive = false;
    
    mapping(address => bool) private _allowList;
    mapping(address => uint256) private _allowListClaimed;
    
    Counters.Counter private _publicFUNGI;

    constructor() ERC721("Fungi Queendom", "FUNGI") {
        
    }

    function setActive(bool isActive) external onlyOwner {
        _isActive = isActive;
       
    }

    function setContractURI(string memory URI) external onlyOwner {
        _contractURI = URI;
    }

    function setBaseURI(string memory URI) external onlyOwner {
        _tokenBaseURI = URI;
    }

    // minting
    function minting(address to, uint256 numberOfTokens)
        external
        payable
        onlyOwner 
        {
            require(
                _publicFUNGI.current() < FUNGI_PUBLIC,
                "Purchase would exceed FUNGI_PUBLIC"
            );
            
            for (uint256 i = 0; i < numberOfTokens; i++) {
                uint256 tokenId = _publicFUNGI.current();
    
                if (_publicFUNGI.current() < FUNGI_PUBLIC) {
                    _publicFUNGI.increment();
                    _safeMint(to, tokenId);
                }
            }
    }
    
      function setIsAllowListActive(bool _isAllowListActive) external onlyOwner {
        isAllowListActive = _isAllowListActive;
      }
      
      function setTotal(uint256 total) external onlyOwner {
        maxTotal = total;
      }
    
      function setAllowListMaxMint(uint256 maxMint) external onlyOwner {
        allowListMaxMint = maxMint;
      }
      
      function addToAllowList(address[] calldata addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
          require(addresses[i] != address(0), "Can't add the null address");
            
          
          _allowList[addresses[i]] = true;
          
          /**
          * @dev We don't want to reset _allowListClaimed count
          * if we try to add someone more than once.
          */
          _allowListClaimed[addresses[i]] > 0 ? _allowListClaimed[addresses[i]] : 0;
        }
      }
     
      
      function allowListClaimedBy(address owner) external view returns (uint256){
        require(owner != address(0), 'Zero address not on Allow List');
    
        return _allowListClaimed[owner];
      }
    
      function onAllowList(address addr) external view returns (bool) {
        return _allowList[addr];
      }
    
      function removeFromAllowList(address[] calldata addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
          require(addresses[i] != address(0), "Can't add the null address");
    
          /// @dev We don't want to reset possible _allowListClaimed numbers.
          _allowList[addresses[i]] = false;
        }
      }
    
    function purchaseAllowList(uint256 numberOfTokens) external payable {
        require(
            numberOfTokens <= PURCHASE_LIMIT,
            "Can only mint up to 1 token"
        );
        
        require(isAllowListActive, 'Allow List is not active');
        require(_allowList[msg.sender], 'You are not on the Allow List');
        require(
            _publicFUNGI.current() < FUNGI_PUBLIC,
            "Purchase would exceed max"
        );
        require(numberOfTokens <= allowListMaxMint, 'Cannot purchase this many tokens');
        require(_allowListClaimed[msg.sender] + numberOfTokens <= allowListMaxMint, 'Purchase exceeds max allowed');
        require(PRICE * numberOfTokens <= msg.value, 'ETH amount is not sufficient');
        require(
            _publicFUNGI.current() < FUNGI_PUBLIC,
            "Purchase would exceed FUNGI_PUBLIC"
        );
        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 tokenId = _publicFUNGI.current();

            if (_publicFUNGI.current() < maxTotal) {
                _publicFUNGI.increment();
                _safeMint(msg.sender, tokenId);
            }
        }
      }

    function purchase(uint256 numberOfTokens) external payable {
        
        require(_isActive, "Contract is not active");
        
        require(
            numberOfTokens <= PURCHASE_LIMIT,
            "Can only mint up to 1 token"
        );
        require(
            _publicFUNGI.current() < FUNGI_PUBLIC,
            "Purchase would exceed FUNGI_PUBLIC"
        );
        require(
            PRICE * numberOfTokens <= msg.value,
            "ETH amount is not sufficient"
        );
        

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 tokenId = _publicFUNGI.current();

            if (_publicFUNGI.current() < maxTotal) {
                _publicFUNGI.increment();
                _safeMint(msg.sender, tokenId);
            }
        }
    }

    function contractURI() public view returns (string memory) {
        return _contractURI;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721)
        returns (string memory)
    {
        require(_exists(tokenId), "Token does not exist");

        return string(abi.encodePacked(_tokenBaseURI, tokenId.toString()));
    }

    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;

        payable(msg.sender).transfer(balance);
    }
}
function minting(address to, uint256 numberOfTokens)
        external
        payable
        onlyOwner 
        {
            require(
                _publicFUNGI.current() < FUNGI_PUBLIC,
                "Purchase would exceed FUNGI_PUBLIC"
            );
            
            for (uint256 i = 0; i < numberOfTokens; i++) {
                uint256 tokenId = _publicFUNGI.current();
    
                if (_publicFUNGI.current() < FUNGI_PUBLIC) {
                    _publicFUNGI.increment();
                    _safeMint(to, tokenId);
                }
            }
    }
// minting