contract MePunkWhiteListSale is Ownable, ReentrancyGuard {
    using Strings for uint256;
    using MerkleProof for bytes32[];

    uint256 public whiteListSaleStartTime = 1648472400; // 3/28 9pm
    uint256 public whiteListSaleEndTime = whiteListSaleStartTime + 1 days;
    uint256 public whiteListSaleRemainingCount = 86;
    uint256 public whiteListSaleMintPrice = 0.11 ether;
    address public mePunk;
    bytes32 public whiteListMerkleRoot;

    mapping(address => bool) public whiteListPurchased;

    constructor(address _mePunk) {
        mePunk = _mePunk;
    }

    /* ************** */
    /* USER FUNCTIONS */
    /* ************** */

    function buyMePunk(bytes32[] calldata proof, uint256 numberOfTokens)
        external
        payable
        nonReentrant
    {
        require(block.timestamp > whiteListSaleStartTime, "not started");
        require(block.timestamp < whiteListSaleEndTime, "has ended");
        require(numberOfTokens > 0, "numberOfTokens cannot be 0");
        require(!whiteListPurchased[msg.sender], "whiteListPurchased already");
        require(
            proof.verify(
                whiteListMerkleRoot,
                keccak256(abi.encodePacked(msg.sender, numberOfTokens))
            ),
            "failed to verify WL merkle root"
        );
        require(
            whiteListSaleRemainingCount >= numberOfTokens,
            "whitelist sold out"
        );
        require(
            msg.value == whiteListSaleMintPrice * numberOfTokens,
            "sent ether value incorrect"
        );
        whiteListPurchased[msg.sender] = true;
        whiteListSaleRemainingCount -= numberOfTokens;
        for (uint256 i = 0; i < numberOfTokens; i++) {
            NFT(mePunk).mint(msg.sender);
        }
    }

    /* *************** */
    /* ADMIN FUNCTIONS */
    /* *************** */

    function setMerkleRoot(bytes32 _whiteListMerkleRoot) external onlyOwner {
        whiteListMerkleRoot = _whiteListMerkleRoot;
    }

    function setSaleData(
        uint256 _whiteListSaleStartTime,
        uint256 _whiteListSaleEndTime,
        uint256 _whiteListSaleRemainingCount,
        uint256 _whiteListSaleMintPrice
    ) external onlyOwner {
        whiteListSaleStartTime = _whiteListSaleStartTime;
        whiteListSaleEndTime = _whiteListSaleEndTime;
        whiteListSaleRemainingCount = _whiteListSaleRemainingCount;
        whiteListSaleMintPrice = _whiteListSaleMintPrice;
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
}
function setMerkleRoot(bytes32 _whiteListMerkleRoot) external onlyOwner {
    whiteListMerkleRoot = _whiteListMerkleRoot;
}

/* *************** */