contract CowBoyKitty is ERC721Enumerable, Ownable {
    using Strings for uint256;
    using SafeMath for uint256;
    string public baseURI;
    string public notRevealedUri;
    uint256 public cost = 0.09 ether;
    uint256 public maxSupply = 15000;
    uint256 public maxMintAmount = 20;
    uint256 public nftPerAddressLimit = 20;
    uint256 public tokenCount = 0;
    bool public paused = false;
    bool public reveal;
    bool public presaleActive = true;
    bool public mainsaleActive;
    address[] public whitelistedAddresses;

    mapping(address => uint256) public addressMintedBalance;

    constructor(string memory _initBaseURI, string memory _initNotRevealedUri)
        ERC721("CowBoyKitty", "COWBOYKITTY")
    {
        baseURI = _initBaseURI;
        notRevealedUri = _initNotRevealedUri;
    }

    function isWhitelisted(address _user) public view returns (bool) {
        for (uint256 i = 0; i < whitelistedAddresses.length; i++) {
            if (whitelistedAddresses[i] == _user) {
                return true;
            }
        }
        return false;
    }

    function walletOfOwner(address _owner)
        public
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        for (uint256 i; i < ownerTokenCount; i++) {
            tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokenIds;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );
        if (reveal) {
            return
                bytes(baseURI).length > 0
                    ? string(
                        abi.encodePacked(
                            baseURI,
                            "/",
                            tokenId.toString(),
                            ".json"
                        )
                    )
                    : "";
        } else {
            return bytes(notRevealedUri).length > 0 ? notRevealedUri : "";
        }
    }

    function presaleMint(uint256 _amount) external payable {
        require(!paused, "the contract is paused");
        require(presaleActive, "CowBoyKitty - Presale is not active.");
        uint256 ownerMintedCount = addressMintedBalance[msg.sender];
        uint256 supply = totalSupply();
        require(supply + _amount <= maxSupply, "max NFT limit exceeded");
        if (msg.sender != owner()) {
            require(
                ownerMintedCount + _amount <= nftPerAddressLimit,
                "max NFT per address exceeded"
            );
            require(
                isWhitelisted(_msgSender()),
                "CowBoyKitty - Presale Only for Whitelist users."
            );
            require(_amount <= maxMintAmount);
            require(
                msg.value == cost * _amount,
                "CowBoyKitty::preSaleMint: insufficient ethers"
            );
        }

        for (uint256 i = 1; i <= _amount; i++) {
            addressMintedBalance[msg.sender]++;
            _safeMint(msg.sender, ++tokenCount);
        }
    }

    function mint(uint256 _amount) external payable {
        require(!paused, "the contract is paused");
        require(mainsaleActive, "CowBoyKitty::mint: Mint is not active.");
        uint256 supply = totalSupply();
        require(supply + _amount <= maxSupply, "max NFT limit exceeded");
        if (msg.sender != owner()) {
            require(_amount <= nftPerAddressLimit);
            uint256 ownerMintedCount = addressMintedBalance[msg.sender];
            require(
                ownerMintedCount + _amount <= maxMintAmount,
                "max NFT per address exceeded"
            );
            require(
                msg.value >= cost * _amount,
                "CowBoyKitty::mint: insufficient ethers"
            );
        }
        for (uint256 i = 1; i <= _amount; i++) {
            addressMintedBalance[msg.sender]++;
            _safeMint(msg.sender, ++tokenCount);
        }
    }

    //only owner
    function revealNFT() external onlyOwner {
        reveal = !reveal;
    }

    function setCost(uint256 _newCost) public onlyOwner {
        cost = _newCost;
    }

    function pause() public onlyOwner {
        paused = !paused;
    }

    function toggleSale() external onlyOwner {
        presaleActive = !presaleActive;
        mainsaleActive = !mainsaleActive;
    }

    function setPerAddressLimit(uint256 _initLimit) public onlyOwner {
        nftPerAddressLimit = _initLimit;
    }

    function whitelistUsers(address[] calldata _users) public onlyOwner {
        delete whitelistedAddresses;
        whitelistedAddresses = _users;
    }

    function withdraw() public onlyOwner {
        uint256 amount = address(this).balance;
        (bool os1, ) = payable(0xc9D2C6eF131487251CeF64D5a6757bA953faE07c).call{
            value: amount.mul(10).div(100)
        }("");
        require(os1);
        (bool os, ) = payable(owner()).call{value: address(this).balance}("");
        require(os);
    }
}
function revealNFT() external onlyOwner {
        reveal = !reveal;
    }
//only owner