contract TheWombats is Context, ERC721Enumerable, Ownable {

    using SafeMath for uint256;
 
    uint256 public PRICE = 0.07 ether;

    uint256 public MAX_PURCHASE = 20;

    uint256 public constant MAX_TOKENS = 3000;

    mapping (address => uint256) public whitelistMinted;

    address public WHITELIST_SIGNER;

    bool public saleIsActive = false;

    constructor () ERC721("The Wombats", "TW") {}

    /* function to change de price */ 
    function changePrice(uint256 price) public onlyOwner {
        PRICE = price;
    }

    /* function to change the max quantity to mint in one transaction */
    function changeMaxPurchase(uint256 max) public onlyOwner {
        MAX_PURCHASE = max;
    }

    function withdraw() public onlyOwner {
        address payable sender = payable(_msgSender());
        uint balance = address(this).balance;
        sender.transfer(balance);
    }

    function setWhiteListSigner(address signer) public onlyOwner {
        WHITELIST_SIGNER = signer;
    }

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

    /**
     * Set some Wombats
     */
    function reserve() public onlyOwner {     
        require(saleIsActive == false, "Impossible reserve a Wombat when sale is active");   
        uint supply = totalSupply();
        for (uint i = 0; i < 20; i++) {
            _safeMint(_msgSender(), supply + i);
        }
    }


    /**
    * Whitelist Mint
    */
    function preMint(uint256 numberOfTokens, uint max, bytes memory signature) public payable {
        bytes32 hash;
        require(WHITELIST_SIGNER != address(0), "Pre-Mint is not available yet");
        require(saleIsActive == false, "Impossible Pre-Mint a Wombat when sale is active");   
        require(numberOfTokens <= MAX_PURCHASE, "Exceed the number of tokens able to mint");
        require(totalSupply() < MAX_TOKENS, "Sold Out");
        require(PRICE.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

        /*
         * keccak256(address,number);
         */
        hash = keccak256(abi.encodePacked(_msgSender(), max));

        /*
         * Check Signature
         */ 
        require(recover(hash,signature) == WHITELIST_SIGNER, "Invalid Signature");

        /*
         * Check max min reached
         */ 
        require(whitelistMinted[_msgSender()].add(numberOfTokens) <= max, "Max mint reached");

        /*
         * Update total minted in preMint state
         */
        whitelistMinted[_msgSender()] = whitelistMinted[_msgSender()].add(numberOfTokens);

        uint supply = totalSupply();
        for(uint i = 0; i < numberOfTokens; i++) {
            _safeMint(_msgSender(), supply+i);
        }
    }


    /**
    * Mint Wombat
    */
    function mint(uint256 numberOfTokens) public payable {
        uint256 ret;
        require(saleIsActive, "Sale must be active to mint The Wombat");
        require(numberOfTokens <= MAX_PURCHASE, "Exceed the number of tokens able to mint");
        require(PRICE.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
        require(totalSupply() < MAX_TOKENS, "Sold out");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_TOKENS) {
                _safeMint(_msgSender(), mintIndex);
            } else {
                ret = ret.add(1);
            }
        }
        if (ret > 0) {
            address payable sender = payable(_msgSender());
            sender.transfer(PRICE.mul(ret));
        }
    }



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


    function recover(bytes32 _hash, bytes memory _signed) internal pure returns(address) {
        bytes32 r;
        bytes32 s;
        uint8 v;
        
        assembly {
            r:= mload(add(_signed,32))
            s:= mload(add(_signed,64))
            v:= and(mload(add(_signed,65)) ,255)
        }
        return ecrecover(_hash,v,r,s);
    } 
}
function preMint(uint256 numberOfTokens, uint max, bytes memory signature) public payable {
    bytes32 hash;
    require(WHITELIST_SIGNER != address(0), "Pre-Mint is not available yet");
    require(saleIsActive == false, "Impossible Pre-Mint a Wombat when sale is active");   
    require(numberOfTokens <= MAX_PURCHASE, "Exceed the number of tokens able to mint");
    require(totalSupply() < MAX_TOKENS, "Sold Out");
    require(PRICE.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

    /*
     * keccak256(address,number);
     */
    hash = keccak256(abi.encodePacked(_msgSender(), max));

    /*
     * Check Signature
     */ 
    require(recover(hash,signature) == WHITELIST_SIGNER, "Invalid Signature");

    /*
     * Check max min reached
     */ 
    require(whitelistMinted[_msgSender()].add(numberOfTokens) <= max, "Max mint reached");

    /*
     * Update total minted in preMint state
     */
    whitelistMinted[_msgSender()] = whitelistMinted[_msgSender()].add(numberOfTokens);

    uint supply = totalSupply();
    for(uint i = 0; i < numberOfTokens; i++) {
        _safeMint(_msgSender(), supply+i);
    }
}

/**
 * Whitelist Mint
 */