contract MetaLordz is ERC721Enumerable, Ownable {
    
    using SafeMath for uint256;

    address private owner_;
    bytes32 public merkleRoot;
	address public wallet1 = 0x94C967079DFB253E4dE24c0a845ff2A13E279bbA;
    address public wallet2 = 0x82d7ed59eB4a573e7AaceFc6f84F579014f433BE;
    address public wallet3 = 0xcA0ff953D73c0c86C8aA9069A092de1010c5Ae99;
    address public wallet4 = 0x16A0A11E0dbff1Cc1e7144A9311D1D4EA9e0DEc3;

    uint256 public constant metalordzPrice = 100000000000000000;
    uint public constant maxMetalordzPurchase = 2;
    uint256 public constant MAX_METALORDZ = 9999;
    uint public metalordzReserve = 250;

    bool public saleIsActive = false;
	bool public whitelistSaleIsActive = false;
    mapping(address => bool) private whitelisted_minters;
    mapping(address => uint) private max_mints_per_address;
    
	event WhitelistedMint(address minter);
    event MerkleRootUpdated(bytes32 new_merkle_root);
	event wallet1AddressChanged(address _wallet1);
    event wallet2AddressChanged(address _wallet2);
    event wallet3AddressChanged(address _wallet3);
    event wallet4AddressChanged(address _wallet4);
    
    string public baseTokenURI;
    
    constructor() ERC721("MetaLordz", "MTL") { }
    
    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

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


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

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


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

    function flipWhitelistSaleState() public onlyOwner {
        whitelistSaleIsActive = !whitelistSaleIsActive;
    }    
       
     function mintMetalordz(uint numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint MetaLordz");
        require(numberOfTokens > 0 && numberOfTokens <= maxMetalordzPurchase, "Can only mint 2 tokens at a time");
        require(msg.value == metalordzPrice.mul(numberOfTokens), "Ether value sent is not correct");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= 4,"Max 4 mints per wallet allowed");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_METALORDZ) {
                _safeMint(msg.sender, totalSupply());
                max_mints_per_address[msg.sender] = max_mints_per_address[msg.sender].add(1);
            } else {
               saleIsActive = !saleIsActive;
                payable(msg.sender).transfer(numberOfTokens.sub(i).mul(metalordzPrice));
                break;
            }
        }
    }

   
   // to set the merkle proof
    function updateMerkleRoot(bytes32 newmerkleRoot) external onlyOwner {
        merkleRoot = newmerkleRoot;
        emit MerkleRootUpdated(merkleRoot);
    }


    function whitelistedMints(uint numberOfTokens, bytes32[] calldata merkleProof ) payable external  {
        address user_ = msg.sender;
        require(whitelistSaleIsActive, "Sale must be active to mint MetaLordz");
        require(numberOfTokens > 0 && numberOfTokens <= 2, "Can only mint 2 tokens at a time");
        require(msg.value == metalordzPrice.mul(numberOfTokens), "Ether value sent is not correct");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= 4,"Max 4 mints allowed per whitelisted wallet");

        // Verify the merkle proof
        require(MerkleProof.verify(merkleProof, merkleRoot,  keccak256(abi.encodePacked(user_))  ), "Invalid proof");
		
		for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_METALORDZ) {
                _safeMint(msg.sender, totalSupply());
                max_mints_per_address[msg.sender] = max_mints_per_address[msg.sender].add(1);
            } else {
			    whitelistSaleIsActive = !whitelistSaleIsActive;
                payable(msg.sender).transfer(numberOfTokens.sub(i).mul(metalordzPrice));
                break;
            }
        }

        emit WhitelistedMint(user_);
    }
	
    function setWallet_1(address _address) external onlyOwner {
        wallet1 = _address;
        emit wallet1AddressChanged(_address);
    }
    
    function setWallet_2(address _address) external onlyOwner {
        wallet2 = _address;
        emit wallet2AddressChanged(_address);
    }

    function setWallet_3(address _address) external onlyOwner {
        wallet3 = _address;
        emit wallet3AddressChanged(_address);
    }
    
    function setWallet_4(address _address) external onlyOwner {
        wallet4 = _address;
        emit wallet4AddressChanged(_address);
    }

    function withdrawAll() external onlyOwner {
        require(address(this).balance > 0, "No balance to withdraw.");
        uint256 _amount = address(this).balance;
        (bool wallet1Success, ) = wallet1.call{value: _amount.mul(33).div(100)}("");
        (bool wallet2Success, ) = wallet2.call{value: _amount.mul(33).div(100)}("");
        (bool wallet3Success, ) = wallet3.call{value: _amount.mul(33).div(100)}("");
        (bool wallet4Success, ) = wallet4.call{value: _amount.mul(1).div(100)}("");

        require(wallet1Success && wallet2Success && wallet3Success && wallet4Success, "Withdrawal failed.");
    }
}
function updateMerkleRoot(bytes32 newmerkleRoot) external onlyOwner {
    merkleRoot = newmerkleRoot;
    emit MerkleRootUpdated(merkleRoot);
}

// to set the merkle proof