contract EMETH is ERC721("EMETH", "EMETH"), ERC721Enumerable, Ownable {
    using SafeMath for uint256;
    using Strings for uint256;
    /*
     * Currently Assuming there will be one baseURI.
     * If it fails to upload all NFTs data under one baseURI,
     * we will divide baseURI and tokenURI function will be changed accordingly.
    */
    string private baseURI;
    string private blindURI;
    uint256 public constant BUY_LIMIT_PER_TX = 10;
    uint256 public constant MAX_NFT_PUBLIC = 8688;
    uint256 private constant MAX_NFT = 8888;
    uint256 public NFTPrice = 100000000000000000;  // 0.10 ETH
    bool public reveal;
    bool public isActive;
    bool public isPresaleActive;
    bytes32 public root;
    uint256 public constant WHITELIST_MAX_MINT = 5;
    mapping(address => uint256) private whiteListClaimed;
    uint256 public giveawayCount;
    /*
     * Function to reveal all NFTs
    */
    function revealNow() 
        external 
        onlyOwner 
    {
        reveal = true;
    }
    
   
   
    
    /*
     * Function setIsActive to activate/desactivate the smart contract
    */
    function setIsActive(
        bool _isActive
    ) 
        external 
        onlyOwner 
    {
        isActive = _isActive;
    }
    
    /*
     * Function setPresaleActive to activate/desactivate the presale  
    */
    function setPresaleActive(
        bool _isActive
    ) 
        external 
        onlyOwner 
    {
        isPresaleActive = _isActive;
    }
    
    /*
     * Function to set Base and Blind URI 
    */
    function setURIs(
        string memory _blindURI, 
        string memory _URI
    ) 
        external 
        onlyOwner 
    {
        blindURI = _blindURI;
        baseURI = _URI;
    }
    
    /*
     * Function to withdraw collected amount during minting by the owner
    */
    function withdraw(
        address _to
    ) 
        public 
        onlyOwner 
    {
        uint balance = address(this).balance;
        require(balance > 0, "Balance should be more then zero");
        payable(_to).transfer(balance);
    }
    
    /*
     * Function to mint new NFTs during the public sale
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */
    function mintNFT(
        uint256 _numOfTokens
    ) 
        public 
        payable 
    {
    
        require(isActive, 'Contract is not active');
        require(!isPresaleActive, 'Only whiteing from White List');
        require(_numOfTokens <= BUY_LIMIT_PER_TX, "Cannot mint above limit");
        require(totalSupply().add(_numOfTokens).sub(giveawayCount) <= MAX_NFT_PUBLIC, "Purchase would exceed max public supply of NFTs");
        require(NFTPrice.mul(_numOfTokens) == msg.value, "Ether value sent is not correct");
        
        for(uint i = 0; i < _numOfTokens; i++) {
            _safeMint(msg.sender, totalSupply().sub(giveawayCount));
        }
    }
    
    /*
     * Function to mint new NFTs during the presale
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */ 
    function mintNFTDuringPresale(
        uint256 _numOfTokens,
        bytes32[] memory _proof
    ) 
        public 
        payable
    {
        require(isActive, 'Contract is not active');
        require(isPresaleActive, 'Only whiteing from White List');
        require(verify(_proof, bytes32(uint256(uint160(msg.sender)))), "Not whitelisted");
        require(totalSupply() < MAX_NFT_PUBLIC, 'All public tokens have been minted');
        require(_numOfTokens <= WHITELIST_MAX_MINT, 'Cannot purchase this many tokens');
        require(totalSupply().add(_numOfTokens).sub(giveawayCount) <= MAX_NFT_PUBLIC, 'Purchase would exceed max public supply of NFTs');
        require(whiteListClaimed[msg.sender].add(_numOfTokens) <= WHITELIST_MAX_MINT, 'Purchase exceeds max whiteed');
        require(NFTPrice.mul(_numOfTokens) == msg.value, "Ether value sent is not correct");
        for (uint256 i = 0; i < _numOfTokens; i++) {
            
            whiteListClaimed[msg.sender] += 1;
            _safeMint(msg.sender, totalSupply().sub(giveawayCount));
        }
    }
    
    /*
     * Function to mint all NFTs for giveaway and partnerships
    */
    function mintByOwner(
        address _to, 
        uint256 _tokenId
    ) 
        public 
        onlyOwner
    {
        require(_tokenId >= MAX_NFT_PUBLIC, "Tokens number to mint must exceed number of public tokens");
        require(_tokenId < MAX_NFT, "Tokens number to mint cannot exceed number of MAX tokens");
        _safeMint(_to, _tokenId);
        giveawayCount=giveawayCount.add(1);
    }
    
    /*
     * Function to mint all NFTs for giveaway and partnerships
    */
    function mintMultipleByOwner(
        address[] memory _to, 
        uint256[] memory _tokenId
    ) 
        public 
        onlyOwner
    {
        require(_to.length == _tokenId.length, "Should have same length");
        for(uint256 i = 0; i < _to.length; i++){
            require(_tokenId[i] >= MAX_NFT_PUBLIC, "Tokens number to mint must exceed number of public tokens");
            require(_tokenId[i] < MAX_NFT, "Tokens number to mint cannot exceed number of MAX tokens");
            _safeMint(_to[i], _tokenId[i]);
            giveawayCount = giveawayCount.add(1);
        }
    }
    
    /*
     * Function to get token URI of given token ID
     * URI will be blank untill totalSupply reaches MAX_NFT_PUBLIC
    */
    function tokenURI(
        uint256 _tokenId
    ) 
        public 
        view 
        virtual 
        override 
        returns (string memory) 
    {
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        if (!reveal) {
            return string(abi.encodePacked(blindURI));
        } else {
            return string(abi.encodePacked(baseURI, _tokenId.toString()));
        }
    }
    
    function supportsInterface(
        bytes4 _interfaceId
    ) 
        public
        view 
        override (ERC721, ERC721Enumerable) 
        returns (bool) 
    {
        return super.supportsInterface(_interfaceId);
    }

    // Standard functions to be overridden 
    function _beforeTokenTransfer(
        address _from, 
        address _to, 
        uint256 _tokenId
    ) 
        internal 
        override(ERC721, ERC721Enumerable) 
    {
        super._beforeTokenTransfer(_from, _to, _tokenId);
    }

    function setRoot(uint256 _root) onlyOwner() public {
        root = bytes32(_root);
    }

    function verify(bytes32[] memory proof, bytes32 leaf) public view returns (bool) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = sha256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = sha256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}
function tokenURI(
    uint256 _tokenId
) 
    public 
    view 
    virtual 
    override 
    returns (string memory) 
{
    require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
    if (!reveal) {
        return string(abi.encodePacked(blindURI));
    } else {
        return string(abi.encodePacked(baseURI, _tokenId.toString()));
    }
}

/*
 * Function to get token URI of given token ID
 * URI will be blank untill totalSupply reaches MAX_NFT_PUBLIC
 */