contract BabySpookles is ERC721, Ownable {

    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    string metadataBaseURI = "https://spookles.app/baby/metadatav4/";

    bytes32 public merkleRoot = 0x5bed4c145a11d0c1a16e3264283d0ca1888e5d3b60a042f9b441a578c2ee5ff9;

    uint256 mintPrice = 0.0125 * 10**18;
    uint256 nftLimit = 5555;
    uint256 paidMintLimit = 4655;

    mapping(address => uint256) public freeMintCount;
    mapping(address => uint256) public mintedCount;

    constructor() ERC721("Baby Spookle", "Baby Spookle") {
        freeMintCount[0x5902Df94392eAc50471da8f11660895b45b9ed13] = 96;
        freeMintCount[0xE9b4a11F3447b908F0680B97982D616AF3066EAd] = 24;
        freeMintCount[0x4Db180f14A91d3e01776DB3cA2993676543C2A06] = 15;
        freeMintCount[0x7cEC53B3b799F4226261c3F0F9E42A09dC89DB49] = 15;
        freeMintCount[0x59c7f366f5b6b4B1547ab84287d4acdf6b7534dB] = 12;
        freeMintCount[0xb44d3dF4fa349906188a49aAF324491B3B808B8C] = 12;
        freeMintCount[0x1a028077f25dE0D789c5bA6562Cb3A8fa622C43D] = 9;
        freeMintCount[0x4f744EFE8B1922E00cD2e0301C638C8537De9FbB] = 9;
        freeMintCount[0x83882Eec5dAfF10724bacDcF01D976951bC15864] = 9;
        freeMintCount[0xC93C4593A7D55b08f48b8b416fBf9f631912e2C9] = 9;
        freeMintCount[0x0043F8113Eb1257BC6314fE0434eEC0D9bAc3837] = 6;
        freeMintCount[0x088B950b6CdfEAFFD7850bA4756BF4f9607C7a4f] = 6;
        freeMintCount[0x24a4B0479347af2cF621e184c6197b0ac535712d] = 6;
        freeMintCount[0x479A9767808bf8d51e967FeFa3F5C3677FEcF1C9] = 6;
        freeMintCount[0x5bf7cfD652040266B49138CF53718a80360Fe477] = 6;
        freeMintCount[0x834a06e59AB03465eB92c16B17059391f693b62c] = 6;
        freeMintCount[0x8Ee4CfB37A0b9863364806b2d8a809D03F10BDd2] = 6;
        freeMintCount[0x99b29366C13B0cAE8BF023D5E09aA5deD6B56C2e] = 6;
        freeMintCount[0xB5764cA3a6E95965Ba8Ec3660Eee8D668f7d1969] = 6;
        freeMintCount[0xbbDb3AccFe65Fdd507354F37dd841d01576cFFAc] = 6;
        freeMintCount[0xe1106e4B1533c3511Dc05215D8F7D7f8A8587392] = 6;
        freeMintCount[0xeb2601DE5f283517aD68a75e1fd7df48ed36f632] = 6;
        freeMintCount[0xECbF0a509Ac57b327Eb8D9003C45600c01fCFC30] = 6;
        freeMintCount[0xeF8bf5C8549Bf47B9A62F8AE515de66E690F41EA] = 6;
        freeMintCount[0xf3Bc38756bdd873162d396f6e4CF13cafb3Cc11B] = 6;
        freeMintCount[0xFE178dB27E4312e764BA83928cd041A4217cDF29] = 6;
        freeMintCount[0xb72838Cc39De2e1F59740d868C8179c4d103A095] = 6; // test
    }

    // Base ERC721 Overrides
    function _baseURI() internal view override returns (string memory) {
        return metadataBaseURI;
    }

    function changeFreeMintCount(address inputAddress, uint256 count) external onlyOwner {
        freeMintCount[inputAddress] = count;
    }

    function changeMetadataBaseURI(string memory newURL) external onlyOwner {
        metadataBaseURI = newURL;
    }

    function changePaidLimitValue(uint256 value) external onlyOwner {
        paidMintLimit = value;
    }

    function getMintedNftCount() external view returns (uint256) {
        return _tokenIds.current();
    }

    function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
        return super.tokenURI(tokenId);
    }


    function setNewMetadataBaseURI(string memory _newMetadataURI) external onlyOwner {
        metadataBaseURI = _newMetadataURI;
    }

    function withdrawEthereumBalance(address payable destinationWallet) payable external onlyOwner {
        require(
            destinationWallet != address(0),
            "Destination wallet cannot be the zero-address"
        );
    
    (bool sent, bytes memory data) = destinationWallet.call{value: address(this).balance}("");
    require(sent, "Failed to send Ether");
    }


    function setNewMerkleRoot(bytes32 newMerkleRoot) external onlyOwner {
        merkleRoot = newMerkleRoot;
    }

    function setNewMintPrice(uint256 newMintPriceInWei) external onlyOwner {
        mintPrice = newMintPriceInWei;
    }

    function checkIfMerkleWhitelisted(bytes32[] calldata _merkleProof) internal view returns (bool) {
      bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
      if(MerkleProof.verify(_merkleProof, merkleRoot, leaf)) return true;
      return false;
    }

    function mintSpookle(uint256 mintCount, bytes32[] calldata _merkleProof) payable external {

        require(
            mintCount <= 10,
            "You cannot mint more than 10 Baby Spookles at once!"
        );

        require(
            _tokenIds.current() <= nftLimit, 
            "All Baby Spookles have been minted"
        );

        require(
            _tokenIds.current() + mintCount <= nftLimit, 
            "You cannot mint more than the available Baby Spookles"
        );


        if(_tokenIds.current() < paidMintLimit)
        {
            require(
                _tokenIds.current() + mintCount <= paidMintLimit, 
                "Minting this amount would overlap into the free-ming range"
            );

            require(
                msg.value >= mintPrice * mintCount, 
                "You haven't sent enough ETH to mint"
            );
        }
        else
        {

            bool whitelistMapping = freeMintCount[msg.sender] > 0;
            bool whitelistMerkle = checkIfMerkleWhitelisted(_merkleProof);

            // If msg.sender is not registered in the freeMintMapping
            if(freeMintCount[msg.sender] == 0)
            {
                require(
                    whitelistMerkle, 
                    "You are not whitelisted to free-mint"
                );

                require(
                    mintedCount[msg.sender] < 3,
                    "You have free-minted the maximum NFTs for this address! (3)"
                );

                require(
                    mintCount <= 3, 
                    "You cannot free-mint more than 3 Spookle!"
                );

            }
            else
            {
                require(
                    whitelistMapping, 
                    "You are not whitelisted to free-mint"
                );

                require(
                    mintedCount[msg.sender] + mintCount <= freeMintCount[msg.sender],
                    "You cannot free-mint more than your limit!"
                );

                require(
                    mintedCount[msg.sender] <= freeMintCount[msg.sender],
                    "You have free-minted the maximum NFTs for this address!"
                );


            }
        }
        
        
        for(uint256 i=1; i<= mintCount; i++)
        {
            _tokenIds.increment();
            _safeMint(msg.sender, _tokenIds.current(), "");
            mintedCount[msg.sender]++;
        }
            
    }
}
function _baseURI() internal view override returns (string memory) {
    return metadataBaseURI;
}

// Base ERC721 Overrides