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

    uint256 public constant MAX_GPUNK = 11000;
    string baseTokenURI;
    bool public saleOpen = true;

    event GoosePunksMinted(uint256 totalMinted);

    constructor(string memory baseURI) ERC721("GoosePunks Renegades", "GPR") {
        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;
    }

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

    function withdrawAll() external onlyOwner {
        (bool success, ) = msg.sender.call{value: address(this).balance}("");
        require(success, "Transfer failed.");
    }

    function getNFTPrice() public view returns (uint256) {
        uint256 currentSupply = totalSupply();

        if (currentSupply > 8000) {
            return 90000000000000000; // 8001 - 11000 0.09 ETH
        } else if (currentSupply > 4500) {
            return 80000000000000000; // 4501 - 8000 0.08 ETH
        } else {
            return 70000000000000000; // 0 - 4500 0.07 ETH
        }
    }

    function presale(address[] calldata _recipients) external onlyOwner {
        require(
            totalSupply() + _recipients.length <= MAX_GPUNK,
            "Presale minting will exceed maximum supply of GoosePunks"
        );
        require(_recipients.length != 0, "Address not found for minting");
        for (uint256 i = 0; i < _recipients.length; i++) {
            require(_recipients[i] != address(0), "Minting to Null address");
            _mint(_recipients[i]);
        }
    }

    //mint GoosePunks
    function mintGoosePunks(uint256 _count) external payable {
        if (msg.sender != owner()) {
            require(saleOpen, "Sale is not open yet");
        }
        require(
            _count > 0 && _count <= 20,
            "Min 1 & Max 20 GoosePunks can be minted per transaction"
        );
        require(
            totalSupply() + _count <= MAX_GPUNK,
            "Transaction will exceed maximum supply of GoosePunks"
        );
        require(
            msg.value >= getNFTPrice() * _count,
            "Ether sent with this transaction is not correct"
        );

        address _to = msg.sender;

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

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

    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }
    
    //Pause sale
    function pauseSale() public onlyOwner {
        saleOpen = false;
    }
    
     //Unpasue sale
    function unpauseSale() public onlyOwner {
        saleOpen = true;
    }
}
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