contract EthsideMafia is ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenId;

    uint256 public constant MAX_ESNFT = 8888;
    uint8 public allowMintCount;
    uint256 public price = 88800000000000000; //0.0888 Ether
    string baseTokenURI;
    bool public saleOpen = false;
    bool public presaleOpen = false;
    bool public allowClaim = false;
    bytes32 public merkleRoot;
    mapping(address => bool) public nftClaimed;

    event EthsideMafiaMinted(uint256 totalMinted);

    constructor(string memory baseURI) ERC721("Ethside Mafia", "ESNFT") {
        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 setPrice(uint256 _newPrice) external onlyOwner {
        price = _newPrice;
    }

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

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

    function flipAllowClaimState() external onlyOwner {
        allowClaim = !allowClaim;
    }


function withdrawAll() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No funds");
        _widthdraw(
            0xF577D42DfebE79AF9225a8749E09aCef245E67AB,
            (balance * 55) / 100
        );
        _widthdraw(
            0x73cF34EA15377130cE4Df9793afc8d64Bbe37D1c,
            (balance * 40) / 100
        );
        _widthdraw(
            0x80850B9732d57B2f42BCCe8761bD1A9D73a5861E,
            (balance * 2) / 100
        );
        _widthdraw(
            0x4d46Ab048c9AF60710879779d20E52cB1f2bdfC1,
            (balance * 3) / 100
        );
    }

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

    function setMerkleRootAndMintCount(bytes32 _merkleRoot, uint8 _allowMintCount) external onlyOwner {
        merkleRoot = _merkleRoot;
        allowMintCount=_allowMintCount;
    }

      function claimESNFT(bytes32[] calldata _proof) external {
        address user = msg.sender;
        require(allowClaim, "Claiming is not live yet");
        require(
            merkleRoot != 0,
            "No address can claim free ESNFT yet"
        );
        
        require(
            totalSupply() + 1 <= MAX_ESNFT,
            "Claim will exceed maximum supply of Ethside Mafia"
        );
        require(
            MerkleProof.verify(
                _proof,
                merkleRoot,
                keccak256(abi.encodePacked(user))
            ),
            "Address not eligible for claiming free ESNFT"
        );
        require(
            nftClaimed[user] != true,
            "Address has already claimed an ESNFT"
        );

        nftClaimed[user] = true;
        _mint(user);
    }


    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 <= MAX_ESNFT,
            "Exceeds maximum supply of Ethside Mafia"
        );
        require(
            _count > 0,
            "Minimum 1 has to be minted per address in presale"
        );
        require(
            allowMintCount >= balanceOf(user) + _count,
            "Purchase will exceed max presale mint amount per address"
        );
        require(
            msg.value >= price * _count,
            "Ether sent with this transaction is not correct"
        );

        for (uint256 i = 0; i < _count; i++) {
            _mint(user);
        }
    }

    //mint EthsideMafia
    function mintEthsideMafia(uint256 _count)
        external
        payable
    {
        address buyer=msg.sender;
        require(
            totalSupply() + _count <= MAX_ESNFT,
            "Exceeds maximum supply of Ethside Mafia"
        );
        require(
            _count > 0,
            "Minimum 1 Ethside Mafia has to be minted per transaction"
        );
        if (buyer != owner()) {
            require(saleOpen, "Sale is not open yet");
            require(
                _count <= 10,
                "Maximum 10 Ethside Mafia can be minted per transaction"
            );
            require(
                msg.value >= price * _count,
                "Ether sent with this transaction is not correct"
            );
        }

        for (uint256 i = 0; i < _count; i++) {
            _mint(buyer);
        }
    }

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

    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }
}
function flipSaleState() external onlyOwner {
    saleOpen = !saleOpen;
}

//Close sale if open, open sale if closed