contract KevinDoodles is ERC721, Ownable, ReentrancyGuard {
    using Counters for Counters.Counter;
    using Strings for uint256;

    Counters.Counter private tokenCounter;

    string public baseURI;
    string public baseExtension = ".json";
    string public provenanceHash;


    uint256 public constant MAX_KEVIN_DOODLES_PER_WALLET = 10;
    uint256 public immutable maxKevinDoodles;
    uint256 public freeMaxSupply = 1000;

    bool public isMintActive;
    uint256 public mintPrice = 0.0069 ether;

    mapping(address => uint256) public publicListMintCounts;


    constructor(
        string memory _name,
        string memory _symbol,
        string memory _initBaseURI,
        uint256 _maxKevinDoodles
    ) ERC721(_name, _symbol) {
        setBaseURI(_initBaseURI);
        maxKevinDoodles = _maxKevinDoodles;
    }

    // ============ ACCESS CONTROL MODIFIERS ============
    
    modifier mintActive() {
        require(isMintActive, "Public sale not active");
        _;
    }

    modifier totalNotExceeded(uint256 numberOfTokens) {
        require(
            tokenCounter.current() + numberOfTokens <= maxKevinDoodles,
            "Not enough azuki mfers remaining to mint"
        );
        _;
    }

    modifier isCorrectPayment(uint256 price, uint256 numberOfTokens) {
        if (tokenCounter.current() + numberOfTokens > freeMaxSupply) {
            require(
                price * numberOfTokens == msg.value,
                "Incorrect ETH value sent"
            ); 
        }
        _;
    }

    // ============ OWNER ONLY FUNCTION FOR MINTING ============

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


    function mintToTeam(uint256 numberOfTokens, address recipient)
        external
        onlyOwner
        totalNotExceeded(numberOfTokens)
    {
        for (uint256 i = 0; i < numberOfTokens; i++) {
            _safeMint(recipient, nextTokenId());
        }
    }
    
    // ============ PUBLIC FUNCTIONS FOR MINTING ============

    function mint(uint256 numberOfTokens)
        external
        payable
        nonReentrant
        mintActive
        isCorrectPayment(mintPrice, numberOfTokens)
        totalNotExceeded(numberOfTokens)
    {
        uint256 numAlreadyMinted = publicListMintCounts[msg.sender];
        require(numAlreadyMinted + numberOfTokens <= MAX_KEVIN_DOODLES_PER_WALLET, "PM: 10 KEVIN DOODLES max per wallet");
        publicListMintCounts[msg.sender] = numAlreadyMinted + numberOfTokens;


        for (uint256 i = 0; i < numberOfTokens; i++) {
            _safeMint(msg.sender, nextTokenId());
        }
    }

    // ============ PUBLIC READ-ONLY FUNCTIONS ============

    function totalSupply() external view returns (uint256) {
        return tokenCounter.current();
    }

    // ============ OWNER-ONLY ADMIN FUNCTIONS ============


    function setProvenanceHash(string memory _hash) external onlyOwner {
        provenanceHash = _hash;
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    function setBaseExtension(string memory _newBaseExtension)
        public
        onlyOwner
    {
        baseExtension = _newBaseExtension;
    }

    // Set prices 

    function setMintPrice(uint256 _price) external onlyOwner {
        mintPrice = _price;
    }

    // Toggle Sales Active / Inactive 

    function setMintActive(bool _isMintActive) external onlyOwner {
        isMintActive = _isMintActive;
    }

    // Withdrawal 

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    function withdrawTokens(IERC20 token) public onlyOwner {
        uint256 balance = token.balanceOf(address(this));
        token.transfer(msg.sender, balance);
    }

    // ============ SUPPORTING FUNCTIONS ============

    function nextTokenId() private returns (uint256) {
        tokenCounter.increment();
        return tokenCounter.current();
    }

    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,
                        tokenId.toString(),
                        baseExtension
                    )
                )
                : "";
    }
         
}
function nextTokenId() private returns (uint256) {
    tokenCounter.increment();
    return tokenCounter.current();
}

// ============ SUPPORTING FUNCTIONS ============