contract MoonwalkerFMNFT is ERC721, ERC721Enumerable,ERC721URIStorage, Ownable {
   using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    
    uint16 public constant maxSupply = 5000;
    string private _baseTokenURI;
    
    uint256 public _startDate = 1635818400;
    uint256 public _whitelistStartDate = 1634608800;
    
    mapping (address => uint16) private _whitelisted;

    constructor() ERC721("MoonwalkerFM", "MWL") {
         for (uint8 i = 0; i < 150; i++) {
            _tokenIds.increment();

            uint256 newItemId = _tokenIds.current();
            _mint(msg.sender, newItemId);
        }
    }
    
    function setStartDate(uint256 startDate) public onlyOwner {
        _startDate = startDate;
    }
    
    function setWhitelistStartDate(uint256 whitelistStartDate) public onlyOwner {
        _whitelistStartDate = whitelistStartDate;
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }


    function setBaseURI(string memory _newbaseTokenURI) public onlyOwner {
        _baseTokenURI = _newbaseTokenURI;
    }

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

    // Get minting limit (for a single transaction) based on current token supply
    function getCurrentMintLimit() public view returns (uint8) {
        if(block.timestamp >= _startDate) {
            return 15;
        }else{
            if(_whitelisted[msg.sender] == 1) {
                return 5;
            }else if(_whitelisted[msg.sender] == 2) {
                return 3;
            }else if(_whitelisted[msg.sender] == 3) {
                return 2;
            }else{
                return 0;
            }
        }
    }

    // Get ether price based on current token supply
    function getCurrentPrice() public pure returns (uint64) {
        return 60_000_000_000_000_000;
    }
    
    function addUserToWhitelist(address wallet, uint16 typeWL) public onlyOwner {
        _whitelisted[wallet] = typeWL;
    }
    
    function removeUserFromWhitelist(address wallet) public onlyOwner {
        _whitelisted[wallet] = 0;
    }
    
    // Mint new token(s)
    function mint(uint8 _quantityToMint) public payable {
        require(_startDate <= block.timestamp || (block.timestamp >= _whitelistStartDate && _whitelisted[msg.sender] > 0), block.timestamp <= _whitelistStartDate ? "Sale is not open" : "Not whitelisted");
        require(_quantityToMint >= 1, "Must mint at least 1");
        require(block.timestamp >= _whitelistStartDate && block.timestamp <= _startDate ? (_quantityToMint + balanceOf(msg.sender) <= getCurrentMintLimit()) : true, "Whitelisted mints are limited to 4 per wallet");
        require(
            _quantityToMint <= getCurrentMintLimit(),
            "Maximum current buy limit for individual transaction exceeded"
        );
        require(
            (_quantityToMint + totalSupply()) <= maxSupply,
            "Exceeds maximum supply"
        );
        require(
            msg.value == (getCurrentPrice() * _quantityToMint),
            "Ether submitted does not match current price"
        );

        for (uint8 i = 0; i < _quantityToMint; i++) {
            _tokenIds.increment();

            uint256 newItemId = _tokenIds.current();
            _mint(msg.sender, newItemId);
        }
    }
    
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    // Withdraw ether from contract
    function withdraw() public onlyOwner {
        require(address(this).balance > 0, "Balance must be positive");
        (bool success, ) = msg.sender.call{value: address(this).balance}("");
        require(success == true, "Failed to withdraw ether");
    }
}
function mint(uint8 _quantityToMint) public payable {
    require(_startDate <= block.timestamp || (block.timestamp >= _whitelistStartDate && _whitelisted[msg.sender] > 0), block.timestamp <= _whitelistStartDate ? "Sale is not open" : "Not whitelisted");
    require(_quantityToMint >= 1, "Must mint at least 1");
    require(block.timestamp >= _whitelistStartDate && block.timestamp <= _startDate ? (_quantityToMint + balanceOf(msg.sender) <= getCurrentMintLimit()) : true, "Whitelisted mints are limited to 4 per wallet");
    require(
        _quantityToMint <= getCurrentMintLimit(),
        "Maximum current buy limit for individual transaction exceeded"
    );
    require(
        (_quantityToMint + totalSupply()) <= maxSupply,
        "Exceeds maximum supply"
    );
    require(
        msg.value == (getCurrentPrice() * _quantityToMint),
        "Ether submitted does not match current price"
    );

    for (uint8 i = 0; i < _quantityToMint; i++) {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(msg.sender, newItemId);
    }
}

// Mint new token(s)