contract GNZ11 is ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    using MerkleProof for bytes32[];
    Counters.Counter private _tokenId;

    uint256 public constant MAX_SUPPLY = 11090;
    uint256 public price = 100000000000000000; //0.1 Ether
    uint256 public presalePrice = 80000000000000000; //0.08 Ether
    uint8 public countLimit = 20;

    string baseTokenURI;

    bytes32 public merkleRoot;

    bool public saleOpen = false;
    bool public presaleOpen = false;

    event GNZ11Minted(uint256 totalMinted);

    constructor(string memory baseURI) ERC721("GNZ-11", "GNZ-11") {
        setBaseURI(baseURI);
    }

    //Get token Ids of all tokens owned by _owner
    function walletOfOwner(address _owner)
        external
        view
        returns (uint256[] memory)
    {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for (uint256 i = 0; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }

        return tokensId;
    }

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

    function setPublicCountLimit(uint8 _newCountLimit) external onlyOwner {
        countLimit = _newCountLimit;
    }

    function changePrice(uint256 _newPrice) external onlyOwner {
        price = _newPrice;
    }

    function changePresalePrice(uint256 _PresalePrice) external onlyOwner {
        presalePrice = _PresalePrice;
    }

    //Close sale if open, open sale if closed
    function flipSaleState() external onlyOwner {
        saleOpen = !saleOpen;
    }

    function flipPresaleState() external onlyOwner {
        presaleOpen = !presaleOpen;
    }

    function withdrawAll() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No funds");
        _widthdraw(
            0xfFcA1E49B0810a12EF83D0F4403E476521d5f729,
            (balance * 2025) / 10000
        );
        _widthdraw(
            0xd0EE81D4262fFBeC384aBC0E3efc1eF4Cc1186Dd,
            (balance * 9) / 100
        );
        _widthdraw(
            0xDD812AC574b1575880dc0017d870a9b200864072,
            (balance * 315) / 1000
        );
        _widthdraw(
            0xf489E2EeBe635A91564a1B05D66571B631907F0E,
            (balance * 2925) / 10000
        );
        _widthdraw(
            0x9d109cC07711149d70153dad627e1D34cb3d8219,
            (balance * 10) / 100
        );
    }

    function _widthdraw(address _address, uint256 _amount) private {
        (bool success, ) = payable(_address).call{value: _amount}("");
        require(success, "Transfer failed.");
    }

    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
        merkleRoot = _merkleRoot;
    }

    function airdrop(address[] calldata _recipients) external onlyOwner {
        require(
            totalSupply() + _recipients.length <= MAX_SUPPLY,
            "Airdrop will exceed maximum supply of GNZ-11"
        );
        require(_recipients.length != 0, "Address not found");
        for (uint256 i = 0; i < _recipients.length; i++) {
            require(_recipients[i] != address(0), "Airdrop to Null address");
            _mint(_recipients[i]);
        }
    }

    function presaleMint(bytes32[] calldata _proof, uint256 _count)
        external
        payable
    {
        address user = msg.sender;
        require(
            merkleRoot != 0,
            "No address is eligible for presale minting yet"
        );
        require(presaleOpen, "Presale is not open yet");
        require(!saleOpen, "Presale is closed");
        require(
            MerkleProof.verify(
                _proof,
                merkleRoot,
                keccak256(abi.encodePacked(user))
            ),
            "Address not eligible for presale mint"
        );
        require(
            totalSupply() + _count <= 5000,
            "Transaction will exceed maximum NFTs alloted for presale"
        );
        require(
            _count > 0 && _count <= 20,
            "Minimum 1 and Maximum 20 GNZ-11 can be minted per transaction"
        );
        require(
            msg.value >= presalePrice * _count,
            "Ether sent with this transaction is not correct"
        );
        for (uint256 i = 0; i < _count; i++) {
            _mint(user);
        }
    }

    //mint GNZ11
    function mintGNZ11(uint256 _count) external payable {
        require(
            totalSupply() + _count <= MAX_SUPPLY,
            "Exceeds maximum supply of GNZ-11"
        );
        require(
            _count > 0,
            "Minimum 1 GNZ-11 has to be minted per transaction"
        );

        address _to = msg.sender;
        if (_to != owner()) {
            require(saleOpen, "Sale is not open yet");
            require(
                _count <= countLimit,
                "Count exceeds maximum mint limit per transaction"
            );
            require(
                msg.value >= price * _count,
                "Ether sent with this transaction is not correct"
            );
        }
        for (uint256 i = 0; i < _count; i++) {
            _mint(_to);
        }
    }

    function _mint(address _to) private {
        _tokenId.increment();
        uint256 tokenId = _tokenId.current();
        _safeMint(_to, tokenId);
        emit GNZ11Minted(tokenId);
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }
}
/// @author Hammad Ghazi
function walletOfOwner(address _owner)
    external
    view
    returns (uint256[] memory)
{
    uint256 tokenCount = balanceOf(_owner);

    uint256[] memory tokensId = new uint256[](tokenCount);
    for (uint256 i = 0; i < tokenCount; i++) {
        tokensId[i] = tokenOfOwnerByIndex(_owner, i);
    }

    return tokensId;
}

//Get token Ids of all tokens owned by _owner