contract MooncatPunks is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    address private owner_;
    uint256 private tokenId;
    bytes32 public merkleRoot;
	bytes32 public merkleRootVIP;
	address private wallet1 = 0xfe0C6901744105F9C6FB78536cD242abEb957334;
    address private Authorized = 0x3a5a8C267134d389157fCD1a497fC13B9C3a5CFf;


    uint256 public MCPPrice_whitelist = 7500000000000000;
	uint256 public MCPPrice_public = 15000000000000000;
    uint public maxMCPTx = 20;
	uint public maxMCPPurchase = 20;
    uint public maxMCPPurchaseWl = 20;
    uint public maxMCPPurchaseVip = 20;

    uint256 public constant MAX_MCP = 6969;
    uint public MCPReserve = 100;
	uint public MCPWhitelistReserve = 6969;


    bool public whitelistSaleIsActive = false;
	bool public publicSaleIsActive = false;
    mapping(address => uint) private max_whitelist_mints_per_address;

	



    string public baseTokenURI;

    constructor() ERC721("Mooncat Punks", "MEOW") { }

    modifier onlyAuthorized {
        require(_msgSender() == owner() || _msgSender() == Authorized , "Not authorized");
        _;
    }

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

    function reserveMCP(address _to, uint256 _reserveAmount) public onlyAuthorized {
        uint supply = totalSupply();
        require(_reserveAmount > 0 && _reserveAmount <= MCPReserve, "Not enough reserve");
        for (uint i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i + 1);
        }
        MCPReserve = MCPReserve.sub(_reserveAmount);
    }


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

    function setBaseURI(string memory baseURI) public onlyAuthorized {
        baseTokenURI = baseURI;
    }


    function flipPublicSaleState() public onlyAuthorized {
        publicSaleIsActive = !publicSaleIsActive;
    }

	 function flipWPSaleState() public onlyAuthorized {
        whitelistSaleIsActive = !whitelistSaleIsActive;
    }


     function mint(uint numberOfTokens) public payable {
        require(publicSaleIsActive, "Sale not active");
        require(numberOfTokens > 0 && numberOfTokens <= maxMCPTx, "Max pTX reached");
        require(msg.value == MCPPrice_public.mul(numberOfTokens), "Check ETH");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_MCP) {
                _safeMint(msg.sender, totalSupply()+1);
                max_whitelist_mints_per_address[msg.sender] = max_whitelist_mints_per_address[msg.sender].add(1);
            } else {
               publicSaleIsActive = !publicSaleIsActive;
                payable(msg.sender).transfer(numberOfTokens.sub(i).mul(MCPPrice_public));
                break;
            }
        }
    }


   // to set the merkle root
    function updateMerkleRoot(bytes32 newmerkleRoot) external onlyAuthorized {
        merkleRoot = newmerkleRoot;
     
    }

	function mintWhitelist(uint numberOfTokens, bytes32[] calldata merkleProof ) payable external  {
        address user_ = msg.sender;
		require(whitelistSaleIsActive, "WL sale not active");
        require(numberOfTokens > 0 && numberOfTokens <= 20, "20 pTX allowed");
        require(msg.value == MCPPrice_whitelist.mul(numberOfTokens), "Check ETH");
        require(max_whitelist_mints_per_address[msg.sender].add(numberOfTokens) <= maxMCPPurchaseVip,"Max whitelist mints pW reached");

        // Verify the merkle proof
        require(MerkleProof.verify(merkleProof, merkleRoot,  keccak256(abi.encodePacked(user_))  ), "Check proof");

		for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_MCP) {
                _safeMint(msg.sender, totalSupply()+1);
                max_whitelist_mints_per_address[msg.sender] = max_whitelist_mints_per_address[msg.sender].add(1);
            }
			else {
               whitelistSaleIsActive = !whitelistSaleIsActive;
                payable(msg.sender).transfer(numberOfTokens.sub(i).mul(MCPPrice_whitelist));
                break;
            }
		}


       
    }

    function withdrawAll() external onlyOwner {
        require(address(this).balance > 0, "No balance");
        uint256 _amount = address(this).balance;
        (bool wallet1Success, ) = wallet1.call{value: _amount.mul(100).div(100)}("");

        require(wallet1Success, "Withdrawal failed.");
    }

    function setPriceWL(uint256 newPriceWL) public onlyAuthorized {
        MCPPrice_whitelist = newPriceWL;
    }

    function setPrice(uint256 newPrice) public onlyAuthorized {
        MCPPrice_public = newPrice;
    }

   function setMaxMCPTx(uint256 newMaxMCPTx) public onlyAuthorized {
        maxMCPTx = newMaxMCPTx;
    }

   function setMaxMCPPurchase(uint256 newMaxMCPPurchase) public onlyAuthorized {
        maxMCPPurchase = newMaxMCPPurchase;
    }

  function setMaxMCPPurchaseWl(uint256 newMaxMCPPurchaseWl) public onlyAuthorized {
            maxMCPPurchaseWl = newMaxMCPPurchaseWl;
        }

  function setMaxMCPPurchaseVip(uint256 newMaxMCPPurchaseVip) public onlyAuthorized {
                    maxMCPPurchaseVip = newMaxMCPPurchaseVip;
                } 

}
function updateMerkleRoot(bytes32 newmerkleRoot) external onlyAuthorized {
        merkleRoot = newmerkleRoot;
     
    }
// to set the merkle root