contract CHRON is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    address private owner_;
    uint256 private tokenId;
    bytes32 public merkleRoot;
	bytes32 public merkleRootVIP;
  address private wallet1 = 0xFc8b02cF70b906bF9d161CE6CF1F235E852Fc59B;
  address private wallet2 = 0x10bc7482947b6483Acd14ED2415172765270Ec78;
  address private wallet3 = 0xC9518dfAE4Bd3aA6BABD9E18197F91606431eDc1;
  address private wallet4 = 0xa982d77A678723319Ff5C9c2cFE318faD040FDd9;
  address private wallet5 = 0xB246F19944550D55f7B61C00E391F906EFd2f403;
  address private Authorized = 0x7a29d9a21A45E269F1bFFFa15a84c16BA0050E27;


    uint256 public CHRONPrice_whitelist = 80000000000000000;
	uint256 public CHRONPrice_public = 80000000000000000;
    uint public maxCHRONTx = 10;
	uint public maxCHRONPurchase = 10;
    uint public maxCHRONPurchaseWl = 20;
    uint public maxCHRONPurchaseVip = 20;

    uint256 public constant MAX_CHRON = 5555;
    uint public CHRONReserve = 100;
	uint public CHRONWhitelistReserve = 5000;


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





    string public baseTokenURI;

    constructor() ERC721("Gargoyles", "CHRON") { }

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

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

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

        for(uint i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_CHRON) {
                _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(CHRONPrice_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 <= maxCHRONTx, "1 pTX allowed");
        require(msg.value == CHRONPrice_whitelist.mul(numberOfTokens), "Check ETH");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= maxCHRONPurchaseWl,"Max pW reached");

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


             if (totalSupply() <= CHRONWhitelistReserve) {
                _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 <= maxCHRONTx, "2 pTX allowed");
        require(msg.value == CHRONPrice_whitelist.mul(numberOfTokens), "Check ETH");
        require(max_mints_per_address[msg.sender].add(numberOfTokens) <= maxCHRONPurchaseVip,"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() <= CHRONWhitelistReserve) {
                _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(CHRONPrice_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(26).div(100)}("");
        (bool wallet2Success, ) = wallet2.call{value: _amount.mul(26).div(100)}("");
        (bool wallet3Success, ) = wallet3.call{value: _amount.mul(15).div(100)}("");
		(bool wallet4Success, ) = wallet4.call{value: _amount.mul(25).div(100)}("");
		(bool wallet5Success, ) = wallet5.call{value: _amount.mul(8).div(100)}("");

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

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

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

   function setMaxCHRONTx(uint256 newMaxCHRONTx) public onlyAuthorized {
        maxCHRONTx = newMaxCHRONTx;
    }

   function setMaxCHRONPurchase(uint256 newMaxCHRONPurchase) public onlyAuthorized {
        maxCHRONPurchase = newMaxCHRONPurchase;
    }

  function setMaxCHRONPurchaseWl(uint256 newMaxCHRONPurchaseWl) public onlyAuthorized {
            maxCHRONPurchaseWl = newMaxCHRONPurchaseWl;
        }

  function setMaxCHRONPurchaseVip(uint256 newMaxCHRONPurchaseVip) public onlyAuthorized {
                    maxCHRONPurchaseVip = newMaxCHRONPurchaseVip;
                }

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

    }
// to set the merkle root