contract SAC is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    address private owner_;
    uint256 private tokenId;
    bytes32 public merkleRoot;
	bytes32 public merkleRootVIP;
	address private wallet1 = 0x25eDb46cBB7744De5507ebe50b5086D236B63073;
    address private wallet2 = 0x827dfa08e6282961b6491851869001cB444b840F;
    address private wallet3 = 0xDf958FE148633B49930775c1E0393594983a128B;
	address private wallet4 = 0x458f5AA4393035f4713bE4EB7176B89f8dD4A6F1;
    address private wallet5 = 0x5f09Bd6Ef7EBDB9CC7d090DC43f9047a2b7A2240;
    address private wallet6 = 0x0308539095797e6F562203C3d8F60Cae82566eBA;
    address private Authorized = 0x7a29d9a21A45E269F1bFFFa15a84c16BA0050E27;


    uint256 public SACPrice_whitelist = 80000000000000000;
	uint256 public SACPrice_public = 80000000000000000;
    uint public maxSACTx = 1;
	uint public maxSACPurchase = 10;
    uint public maxSACPurchaseWl = 1;
    uint public maxSACPurchaseVip = 2;

    uint256 public constant MAX_SAC = 6666;
    uint public SACReserve = 100;
	uint public SACWhitelistReserve = 3900;


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

	



    string public baseTokenURI;

    constructor() ERC721("Stoner Ape Club", "SAC") { }

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

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

    function reserveSAC(address _to, uint256 _reserveAmount) public onlyAuthorized {
        uint supply = totalSupply();
        require(_reserveAmount > 0 && _reserveAmount <= SACReserve, "Not enough reserve");
        for (uint i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i + 1);
        }
        SACReserve = SACReserve.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 mintSAC(uint numberOfTokens) public payable {
        require(publicSaleIsActive, "Sale not active");
        require(numberOfTokens > 0 && numberOfTokens <= maxSACTx, "1 pTX allowed");
        require(msg.value == SACPrice_public.mul(numberOfTokens), "Check ETH");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= maxSACPurchase,"Max pW reached");

        for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_SAC) {
                _safeMint(msg.sender, totalSupply()+1);
                max_mints_per_address[msg.sender] = max_mints_per_address[msg.sender].add(1);
            } else {
               publicSaleIsActive = !publicSaleIsActive;
                payable(msg.sender).transfer(numberOfTokens.sub(i).mul(SACPrice_public));
                break;
            }
        }
    }


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

	// to set the merkle root for VIP
    function updateMerkleRootVIP(bytes32 newmerkleRoot) external onlyAuthorized {
        merkleRootVIP = newmerkleRoot;
       
    }


    function whitelistedMints(uint numberOfTokens, bytes32[] calldata merkleProof ) payable external  {
        address user_ = msg.sender;
		require(whitelistSaleIsActive, "WL sale not active");
        require(numberOfTokens > 0 && numberOfTokens <= 1, "1 pTX allowed");
        require(msg.value == SACPrice_whitelist.mul(numberOfTokens), "Check ETH");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= maxSACPurchaseWl,"Max pW reached");

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


             if (totalSupply() <= SACWhitelistReserve) {
                _safeMint(msg.sender, totalSupply()+1);
                max_mints_per_address[msg.sender] = max_mints_per_address[msg.sender].add(1);
            }
		     else {
                whitelistSaleIsActive = !whitelistSaleIsActive;
            }


       
    }

	function whitelistedVIPMints(uint numberOfTokens, bytes32[] calldata merkleProof ) payable external  {
        address user_ = msg.sender;
		require(whitelistSaleIsActive, "WL sale not active");
        require(numberOfTokens > 0 && numberOfTokens <= 2, "2 pTX allowed");
        require(msg.value == SACPrice_whitelist.mul(numberOfTokens), "Check ETH");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= maxSACPurchaseVip,"Max pW reached");

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

		for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() <= SACWhitelistReserve) {
                _safeMint(msg.sender, totalSupply()+1);
                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(SACPrice_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(23).div(100)}("");
        (bool wallet2Success, ) = wallet2.call{value: _amount.mul(23).div(100)}("");
        (bool wallet3Success, ) = wallet3.call{value: _amount.mul(23).div(100)}("");
		(bool wallet4Success, ) = wallet4.call{value: _amount.mul(19).div(100)}("");
		(bool wallet5Success, ) = wallet5.call{value: _amount.mul(7).div(100)}("");
		(bool wallet6Success, ) = wallet6.call{value: _amount.mul(5).div(100)}("");

        require(wallet1Success && wallet2Success && wallet3Success && wallet4Success && wallet5Success && wallet6Success, "Withdrawal failed.");
    }

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

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

   function setMaxSACTx(uint256 newMaxSACTx) public onlyAuthorized {
        maxSACTx = newMaxSACTx;
    }

   function setMaxSACPurchase(uint256 newMaxSACPurchase) public onlyAuthorized {
        maxSACPurchase = newMaxSACPurchase;
    }

  function setMaxSACPurchaseWl(uint256 newMaxSACPurchaseWl) public onlyAuthorized {
            maxSACPurchaseWl = newMaxSACPurchaseWl;
        }

  function setMaxSACPurchaseVip(uint256 newMaxSACPurchaseVip) public onlyAuthorized {
                    maxSACPurchaseVip = newMaxSACPurchaseVip;
                } 

}
function updateMerkleRoot(bytes32 newmerkleRoot) external onlyAuthorized {
    merkleRoot = newmerkleRoot;
     
}

// to set the merkle root