contract TyronWoodley is ERC721, Ownable {
    using SafeMath for uint256;
    using Strings for uint256;

    uint256 public MAX_TOKEN_SUPPLY;
    uint256 public maxToMint;
    string private prerevealURI;

    bool public saleIsActive;
    bool public whitelistIsActive;
    bool public revealIsActive;
    bytes32[] _rootHash;

    address wallet1;
    address wallet2;

    mapping(address => uint256) public numberOfWhitelistMints;
    uint256 maxWhitelistMints;
    uint256 public whitelistMintPrice;
    uint256 public regularMintPrice;
    uint256 bytPayoutPercentage;

    constructor() ERC721("Tyron Woodley V Jake Paul", "TWDLY") {
        MAX_TOKEN_SUPPLY = 250;
        prerevealURI = "https://gateway.pinata.cloud/ipfs/QmXU9aAbEwTYKioFKsrVNmqbxxyCJ9AcL7nE99dHyHSCBs";
        whitelistMintPrice = 500000000000000000; // 0.5 Ether 
        regularMintPrice = 550000000000000000; //0.55 ether
        maxToMint = 1;
        maxWhitelistMints = 1;
        wallet1 = 0xe5C24ad2bcBBf4b547247417A8a8877dD885D029;// cosmic payout wallet
        wallet2 = 0x175606E97Be86852Cc3F2c9FA081DaE628abcD1F;// byt payout wallet
        bytPayoutPercentage = 10;
    }

    function addToRootHash(bytes32 _hash) external onlyOwner{
        _rootHash.push(_hash);
    }

    /*
    * Pause sale if active, make active if paused
    */
    function setMintPrice(uint256 _price) external onlyOwner {
        whitelistMintPrice = _price;
    }

    /**
     * Set maximum amount to mint per txn.
     */
    function setMaxToMint(uint256 _maxValue) external onlyOwner {
        maxToMint = _maxValue;
    }


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

     /**
     * External function to set the prereveal URI for all token IDs.
     */
    function setPrerevealURI(string memory prerevealURI_) external onlyOwner {
        prerevealURI = prerevealURI_;
    }

    /**
     * External function to switch the revealIsActive state.
     */
    function setRevealState() external onlyOwner {
        revealIsActive = !revealIsActive;
    }

    /*
    * Pause sale if active, make active if paused
    */
    function setSaleState() external onlyOwner {
        saleIsActive = !saleIsActive;
    }

    /*
    * Pause whitelist if active, make active if paused
    */
    function setWhitelistState() external onlyOwner {
        whitelistIsActive = !whitelistIsActive;
    }

    /**
     * Mint token by owner.
     */
    function reserveToken(address _to, uint256 _numberOfTokens) external onlyOwner {
        require(_to != address(0), "Invalid address to reserve.");

        uint256 supply = totalSupply();
        uint256 i;

        for (i = 0; i < _numberOfTokens; i++) {
            _safeMint(_to, supply + i);
        }
    }


    /**
    * Returns the proper tokenURI only after revealIsActive.
    */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        if(revealIsActive) {
            return string(abi.encodePacked(baseURI(), tokenId.toString()));
        } else {
            return prerevealURI;
        }
    }

    /**
    * Mints tokens
    */
    function mint(uint256 numberOfTokens) external payable {
        require(saleIsActive, "Sale must be active to mint");
        require(numberOfTokens <= maxToMint, "Invalid amount to mint per txn");
        require(totalSupply().add(numberOfTokens) <= MAX_TOKEN_SUPPLY, "Purchase would exceed max supply");
        require(regularMintPrice.mul(numberOfTokens) <= msg.value, "Ether value set is not correct");

        uint256 tokenId = totalSupply();
        _safeMint(_msgSender(), tokenId);
    }

    /**
    * Mints whitelisted tokens
    */
    function whitelistMint(uint256 numberOfTokens, uint256 spotInWhitelist, bytes32[] memory proof) external payable {
        require(whitelistIsActive, "The whitelist is not active yet");
        require(numberOfTokens <= maxToMint, "Invalid amount to mint per txn");
        require(totalSupply().add(numberOfTokens) <= MAX_TOKEN_SUPPLY, "Purchase would exceed max supply");
        require(whitelistValidated(_msgSender(), spotInWhitelist, proof), "You're not on the whitelist");
        require((numberOfWhitelistMints[_msgSender()] + numberOfTokens) <= maxWhitelistMints, "This transaction exceeds the max whitelist mints");
        require(whitelistMintPrice.mul(numberOfTokens) <= msg.value, "Ether value set is not correct");

        //Update numberOfWhitelistMints for the wallet
        numberOfWhitelistMints[_msgSender()] += numberOfTokens;
        uint256 tokenId = totalSupply();
        _safeMint(_msgSender(), tokenId);
    }

    function whitelistValidated(address wallet, uint256 index, bytes32[] memory proof) internal view returns (bool) {
        uint256 amount = 1;

        // Compute the merkle root
        bytes32 node = keccak256(abi.encodePacked(index, wallet, amount));
        uint256 path = index;
        for (uint16 i = 0; i < proof.length; i++) {
            if ((path & 0x01) == 1) {
                node = keccak256(abi.encodePacked(proof[i], node));
            } else {
                node = keccak256(abi.encodePacked(node, proof[i]));
            }
            path /= 2;
        }

        // Check the merkle proof against the root hash array
        for(uint i = 0; i < _rootHash.length; i++)
        {
            if (node == _rootHash[i])
            {
                return true;
            }
        }

        return false;
    }

    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        uint256 walletBalance = balance.mul(100 - bytPayoutPercentage).div(100);
        payable(wallet1).transfer(walletBalance);
        payable(wallet2).transfer(balance.sub(walletBalance));
    }
}
/**
 * @title TyronWoodley contract
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function setSaleState() external onlyOwner {
    saleIsActive = !saleIsActive;
}

/*
 * Pause sale if active, make active if paused
 */