contract LoadingDots is ERC721, ERC721Enumerable, ERC721URIStorage, Pausable, Ownable, ERC721Burnable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    uint256 public maxSupply = 3333;
    uint256 public presalePrice = 60000000000000000;
    uint256 public publicPrice = 70000000000000000;
    bool public saleOpen = false;
    bool public presaleOpen = false;
    string public baseURI;

    address wallet1 = 0xb0672c640512ED9677c0047ed6D63ED0cfba068F;
    address wallet2 = 0xA6F3F11E16c5f2F0AB684aa7894D5B1aAa271ca7;

    mapping(address => uint256) public Whitelist;

    receive() external payable {}

    constructor() ERC721('Loading Dots', 'DOTS') {}

    function whitelistAddress(address[] memory who, uint256 amount) public onlyOwner {
        for (uint256 i = 0; i < who.length; i++) Whitelist[who[i]] = amount;
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(wallet1).transfer((balance * 500) / 1000);
        payable(wallet2).transfer((balance * 500) / 1000);
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function toggleSale() public onlyOwner {
        saleOpen = !saleOpen;
    }

    function togglePresale() public onlyOwner {
        presaleOpen = !presaleOpen;
    }

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

    function _baseURI() internal view override returns (string memory) {
        return baseURI;
    }

    function internalMint(address to) internal {
        require(totalSupply() < maxSupply, 'supply depleted');
        _safeMint(to, _tokenIdCounter.current());
        _tokenIdCounter.increment();
    }

    function safeMint(address to) public onlyOwner {
        internalMint(to);
    }

    function mint(uint256 amount) public payable {
        if (!saleOpen) {
            require(msg.value >= presalePrice * amount, 'Insufficient ETH amount');
            require(presaleOpen == true, 'Presale is not open');
            uint256 who = Whitelist[msg.sender];
            require(who > 0, 'This address is not whitelisted for the presale');
            require(amount <= who, 'This address is not allowed to mint that many during the presale');
            for (uint256 i = 0; i < amount; i++) internalMint(msg.sender);
            Whitelist[msg.sender] = who - amount;
            return;
        }
        else {
            require(msg.value >= publicPrice * amount, 'Insufficient ETH amount');
        }
        
        require(amount <= 30, 'Only 30 per transaction allowed');
        for (uint256 i = 0; i < amount; i++) internalMint(msg.sender);
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) whenNotPaused {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }
// The following functions are overrides required by Solidity.