contract Femverse is ERC721Enumerable, Ownable {
    using Strings for uint256;
    using ECDSA for bytes32;
    uint256 public FEMVERSE_GIFT = 555; // first 555 NFTs free
    // uint256 public FEMVERSE_PRESALE = 0;
    uint256 public FEMVERSE_PUBLIC = 5000;
    // uint256 public FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
    uint256 public FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PUBLIC;
    uint256 public FEMVERSE_PRICE = .0555 ether;
    uint256 public FEMVERSE_FREE_PRICE = 0 ether;
    // uint256 public FEMVERSE_PRESALE_PRICE = .03 ether;
    uint256 public FEMVERSE_PER_MINT = 5;
    uint256 public constant FEMVERSE_PER_FREE_MINT = 1;
    // mapping(address => bool) public presalerList;
    // mapping(address => uint256) public presalerListPurchases;
    string private _tokenBaseURI = "https://mint.femverse.org/api/";
    uint256 public giftedAmount;
    uint256 public publicAmountMinted;
    // uint256 public privateAmountMinted;
    // uint256 public presalePurchaseLimit = 13;
    // bool public presaleLive;
    bool public saleLive;
    constructor() ERC721("FemVerse", "Fem") payable { }
    // function addToPresaleList(address[] calldata entries) external onlyOwner {
    //     for(uint256 i = 0; i < entries.length; i++) {
    //         address entry = entries[i];
    //         require(entry != address(0), "NULL_ADDRESS");
    //         require(!presalerList[entry], "DUPLICATE_ENTRY");
    //         presalerList[entry] = true;
    //     }
    // }
    // function removeFromPresaleList(address[] calldata entries) external onlyOwner {
    //     for(uint256 i = 0; i < entries.length; i++) {
    //         address entry = entries[i];
    //         require(entry != address(0), "NULL_ADDRESS");
    //         presalerList[entry] = false;
    //     }
    // }
    // function togglePresaleStatus() external onlyOwner {
    //     presaleLive = !presaleLive;
    // }
    function toggleSaleStatus() external onlyOwner {
        saleLive = !saleLive;
    }
    function buy(uint256 tokenQuantity) external payable {
        require(saleLive, "SALE_CLOSED");
        // require(!presaleLive, "ONLY_PRESALE");
        require((totalSupply() + 1) < FEMVERSE_MAX, "Out of stock.");
        require(publicAmountMinted + tokenQuantity <= FEMVERSE_PUBLIC, "EXCEED_PUBLIC");
        require(tokenQuantity <= FEMVERSE_PER_MINT, "EXCEED_FEMVERSE_PER_MINT");
        for(uint i = 0; i < tokenQuantity; i++) {
            uint _mintId = totalSupply() + 1; // iterate from 1
            // if (_mintId > 100 && _mintId < 301) {
            if (_mintId < 555) {
                require(tokenQuantity <= FEMVERSE_PER_FREE_MINT, "EXCEED_FEMVERSE_PER_FREE_MINT");
                _safeMint(msg.sender, _mintId);
                publicAmountMinted++;
            } else {
                require(FEMVERSE_PRICE * tokenQuantity <= msg.value, "Insufficient ETH.");
                _safeMint(msg.sender, _mintId);
                publicAmountMinted++;
            }
        }
    }
    // function presaleBuy(uint256 tokenQuantity) external payable {
    //     // require(!saleLive && presaleLive, "The presale has finished.");
    //     require(presalerList[msg.sender], "You are not qualified for the presale.");
    //     require((totalSupply() + 1) < FEMVERSE_MAX, "Out of stock.");
    //     require(privateAmountMinted + tokenQuantity <= FEMVERSE_PRESALE, "EXCEED_PRIVATE");
    //     require(presalerListPurchases[msg.sender] + tokenQuantity <= presalePurchaseLimit, "You have reached your maximum purchase amount.");
    //     require(FEMVERSE_PRESALE_PRICE * tokenQuantity <= msg.value, "Insufficient ETH.");
    //     for (uint256 i = 0; i < tokenQuantity; i++) {
    //         uint _mintId = totalSupply() + 1; // iterate from 1
    //         presalerListPurchases[msg.sender]++;
    //         _safeMint(msg.sender, _mintId);
    //         privateAmountMinted++;
    //     }
    // }
    function gift(address[] calldata receivers) external onlyOwner {
        require((totalSupply() + 1) + receivers.length <= FEMVERSE_MAX, "MAX_MINT");
        require(giftedAmount + receivers.length <= FEMVERSE_GIFT, "GIFTS_EMPTY");
        for (uint256 i = 0; i < receivers.length; i++) {
            uint _mintId = totalSupply() + 1; // iterate from 1
            _safeMint(receivers[i], _mintId);
            giftedAmount++;
        }
    }
    function withdrawTeam() external onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
    // function isPresaler(address addr) external view returns (bool) {
    //     return presalerList[addr];
    // }
    // function presalePurchasedCount(address addr) external view returns (uint256) {
    //     return presalerListPurchases[addr];
    // }
    function setBaseURI(string calldata URI) external onlyOwner {
        _tokenBaseURI = URI;
    }
    // function setPresaleAmount(uint256 number) external onlyOwner {
    //     FEMVERSE_PRESALE = number;
    //     FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
    // }
    function setPublicAmount(uint256 number) external onlyOwner {
        FEMVERSE_PUBLIC = number;
        // FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
        FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PUBLIC;
    }
    function setGiftAmount(uint256 number) external onlyOwner {
        FEMVERSE_GIFT = number;
        // FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
        FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PUBLIC;
    }
    function setFemVersePerMint(uint256 number) external onlyOwner {
        FEMVERSE_PER_MINT = number;
    }
    function setPrice(uint256 number) external onlyOwner {
        FEMVERSE_PRICE = number;
    }
    // function setPresalePrice(uint256 number) external onlyOwner {
    //     FEMVERSE_PRESALE_PRICE = number;
    // }
    function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
        require(_exists(tokenId), "Cannot query non-existent token");
        return string(abi.encodePacked(_tokenBaseURI, tokenId.toString()));
    }
}
function setPublicAmount(uint256 number) external onlyOwner {
    FEMVERSE_PUBLIC = number;
    // FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
    FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PUBLIC;
}

// function setPresaleAmount(uint256 number) external onlyOwner {
//     FEMVERSE_PRESALE = number;
//     FEMVERSE_MAX = FEMVERSE_GIFT + FEMVERSE_PRESALE + FEMVERSE_PUBLIC;
// }