contract LykeIslandNFT is ERC721, Ownable {

    // util tokens and strings
    uint256 private currentTokenId;

    // URI
    string public baseURI;

    // prices and supply
    uint256 public price = 0.06 ether;
    uint256 public constant maxSupply = 120;
    uint256 public constant allowlistMintSupply = 112;
    uint256 public constant mintPerAllowlistAddress = 1;
    uint256 public constant mintsForDev = 1;
    uint256 public constant mintsForFounders = 2;
    // there are 5 plots missing from the math here, those are the minimum for the community wallet

    // sales constants
    bool public isAllowlistMintActive = false;
    bool public isReservedMintActive = true;

    // allowlist address + minted balance
    mapping(address => uint256) public allowlistMintAllowance;
    uint256 public allowlistSize = 0;

    // team and community wallets for reserve mint
    address public devWallet = 0x5056F17776CD4682B8961C8AC26C0b420009B6c7;
    address public foundersWallet = 0xBdD93FA3ff5AfF250650a1E3622B224bD74BD2E5;
    address public communityWallet = 0x9b8dC77FE4E9eC716C8B66a5e20AECE6E323bC5C;

    constructor() ERC721("LykeIsland", "LYKEISLAND") {

        currentTokenId = 0;

        baseURI = "ipfs://QmccJxVi9ZPYvunS44PsKmNimE7LysQZXnnowbEAP3tJ3m/";
    }

    function addToAllowlist(address[] memory _allowlistEntries) external onlyOwner {
        require(_allowlistEntries.length + allowlistSize
            <= allowlistMintSupply, "LYKE: can not add this many to allowlist");
        for (uint256 i = 0; i < _allowlistEntries.length; i++) {
            allowlistMintAllowance[_allowlistEntries[i]] = mintPerAllowlistAddress;
            allowlistSize++;
        }
    }

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

    function setBaseURI(string calldata uri) external onlyOwner {
        baseURI = uri;
    }

    function setCommunityWallet(address _newAddress) public {
        communityWallet = _newAddress;
    }

    // generic mint function, iterate over amount and safeMints to same target
    function _mintUnits(address _to, uint256 _amount) private {
        require(_amount + currentTokenId <= maxSupply, "LYKE: can not mint this many tokens");
        for (uint256 i = 0 ; i < _amount ; i++) {
            currentTokenId++;
            _safeMint( _to, currentTokenId );
        }
    }

    // minting for the founding team and community directly to wallets
    function reserveMint() external onlyOwner {
        require(isReservedMintActive, "LYKE: reserved mint is no longer active");
        // function can be used exactly 1 time, there is no toggle function for reserved mint
        isReservedMintActive = !isReservedMintActive;

        // founders mint
        _mintUnits(foundersWallet, mintsForFounders);

        // dev mint
        _mintUnits(devWallet, mintsForDev);
    }

    function communityMint(uint256 _amount) external onlyOwner {
        // community wallet mint
        require(_amount + totalSupply() <= maxSupply, "LYKE: can not mint that amount of plots");
        _mintUnits(communityWallet, _amount);
    }

    function toggleAllowlistMint() public onlyOwner {
        isAllowlistMintActive = !isAllowlistMintActive;
    }

    // allowlist mint
    // no amount parameter as it is a max of 1 per wallet
    function allowlistMint() external payable {
        require(isAllowlistMintActive, "LYKE: allowlist mint is not active");
        require(allowlistMintAllowance[msg.sender] >= mintPerAllowlistAddress, "LYKE: not in allowlist or max minted");
        require(msg.value >= price , "LYKE: insufficient amount paid to mint");

        // mints the allowed amount 
        allowlistMintAllowance[msg.sender] = 0;
        _mintUnits(msg.sender, 1);
    }

    function withdrawBalance() external onlyOwner {
        require(address(this).balance > 0, "LYKE: nothing to withdraw");
        (bool success, ) = communityWallet.call{value: address(this).balance}("");
        require(success, "LYKE: withdrawal failed");
    }

    function totalSupply() public view returns (uint256){
        return currentTokenId;
    }

}
function _mintUnits(address _to, uint256 _amount) private {
    require(_amount + currentTokenId <= maxSupply, "LYKE: can not mint this many tokens");
    for (uint256 i = 0 ; i < _amount ; i++) {
        currentTokenId++;
        _safeMint( _to, currentTokenId );
    }
}

// generic mint function, iterate over amount and safeMints to same target