contract EliteAlpha is ERC1155, Ownable {
    string public constant name = "ELIT3ALPHA";
    string public constant symbol = "EA";

    using SafeMath for uint256;
    using Strings for uint256;
    string private baseURI;
    uint256 public totalSupply = 0;
    uint256 public constant MAX_NFT_PUBLIC = 9969;
    uint256 private constant MAX_NFT = 9999;
    uint256 public constant maxGiveaway=30;
    uint256 public constant maxPerWalletPresale=10;
    uint256 public giveawayCount;
    uint256 public privateSalePrice = 200000000000000000;  // 0.2 ETH
    uint256 public presalePrice = 200000000000000000;  // 0.2 ETH
    uint256 public NFTPrice = 200000000000000000;  // 0.2 ETH
    bool public isActive;
    bool public isPrivateSaleActive;
    bool public isPresaleActive;
    bool public canReveal;
    bytes32 public root;
    mapping(address => uint) public nftBalances;

    

    /*
     * Function to validate owner
    */
    function _ownerOf(address owner, uint256 tokenId) internal view returns (bool) {
        return balanceOf(owner, tokenId) != 0;
    }

    /*
     * Function to mint NFTs
    */
    function mint(address to, uint32 count) internal {
        if (count > 1) {
            uint256[] memory ids = new uint256[](uint256(count));
            uint256[] memory amounts = new uint256[](uint256(count));

            for (uint32 i = 0; i < count; i++) {
                ids[i] = totalSupply + i;
                amounts[i] = 1;
            }

            _mintBatch(to, ids, amounts, "");
        } else {
            _mint(to, totalSupply, 1, "");
        }

        totalSupply += count;
    }

    /*
     * Function setCanReveal to activate/desactivate reveal option
    */
    function setCanReveal(
        bool _isActive
    )
        external
        onlyOwner
    {
        canReveal = _isActive;
    }

    /*
     * Function setIsActive to activate/desactivate the smart contract
    */
    function setIsActive(
        bool _isActive
    )
        external
        onlyOwner
    {
        isActive = _isActive;
    }

    /*
     * Function setPrivateSaleActive to activate/desactivate the presale
    */
    function setPrivateSaleActive(
        bool _isActive
    )
        external
        onlyOwner
    {
        isPrivateSaleActive = _isActive;
    }

    /*
     * Function setPresaleActive to activate/desactivate the presale
    */
    function setPresaleActive(
        bool _isActive
    )
        external
        onlyOwner
    {
        isPresaleActive = _isActive;
    }

    /*
     * Function to set Base URI
    */
    function setURI(
        string memory _URI
    )
        external
        onlyOwner
    {
        baseURI = _URI;
    }

    /*
     * Function to withdraw collected amount during minting by the owner
    */
    function withdraw(
    )
        public
        onlyOwner
    {
        uint balance = address(this).balance;
        require(balance > 0, "Balance should be more then zero");

        payable(0xfbc7A660e4820DE480Cfdf65CfEaE6dE317aD0ca).transfer((balance * 450) / 1000);
        payable(0xEdF400bFDF2E87D196c304480B53d0EAa2c86076).transfer((balance * 450) / 1000);
        payable(0xB7316530C6145Aa96CA5Be0e9888d208bed9A4dB).transfer((balance * 100) / 1000);
    }

    /*
     * Function to set NFT Price
    */
    function setNFTPrice(uint256 _price) external onlyOwner {
        NFTPrice = _price;
    }
    
    /*
     * Function to mint new NFTs during the public sale
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */
    function mintNFT(
        uint32 _numOfTokens
    )
        public
        payable
    {
        require(isActive, 'Contract is not active');
        require(!isPrivateSaleActive, 'Private sale still active');
        require(!isPresaleActive, 'Presale still active');
        require(totalSupply.add(_numOfTokens).sub(giveawayCount) <= MAX_NFT_PUBLIC, "Purchase would exceed max public supply of NFTs");
        require(msg.value >= NFTPrice.mul(_numOfTokens), "Ether value sent is not correct");
        mint(msg.sender,_numOfTokens);
    }

    /*
     * Function to mint new NFTs during the private sale & presale
     * It is payable.
    */
    function mintNFTDuringPresale(
        uint32 _numOfTokens,
        bytes32[] memory _proof
    )
        public
        payable
    {
        require(isActive, 'Contract is not active');
        require(verify(_proof, bytes32(uint256(uint160(msg.sender)))), "Not whitelisted");

        if (!isPresaleActive) {
            require(isPrivateSaleActive, 'Private sale not active');
            require(msg.value >= privateSalePrice.mul(_numOfTokens), "Ether value sent is not correct");
            require(nftBalances[msg.sender].add(_numOfTokens)<= maxPerWalletPresale, 'Max per wallet reached for this phase');

            mint(msg.sender,_numOfTokens);
            nftBalances[msg.sender] = nftBalances[msg.sender].add(_numOfTokens);
            return;
        }

        require(msg.value >= presalePrice.mul(_numOfTokens), "Ether value sent is not correct");
        require(nftBalances[msg.sender].add(_numOfTokens)<= maxPerWalletPresale, 'Max per wallet reached for this phase');

        mint(msg.sender,_numOfTokens);
        nftBalances[msg.sender] = nftBalances[msg.sender].add(_numOfTokens);
    }

    /*
     * Function to mint all NFTs for giveaway and partnerships
    */
    function mintByOwner(
        address _to
    )
        public
        onlyOwner
    {
        require(giveawayCount.add(1)<=maxGiveaway,"Cannot do more giveaway");
        require(totalSupply.add(1) < MAX_NFT, "Tokens number to mint cannot exceed number of MAX tokens");
        mint(_to,1);
        giveawayCount=giveawayCount.add(1);
    }

    /*
     * Function to mint all NFTs for giveaway and partnerships
    */
    function mintMultipleByOwner(
        address[] memory _to
    )
        public
        onlyOwner
    {
        require(totalSupply.add(_to.length) < MAX_NFT, "Tokens number to mint cannot exceed number of MAX tokens");
        require(giveawayCount.add(_to.length)<=maxGiveaway,"Cannot do that much giveaway");
        for(uint256 i = 0; i < _to.length; i++){
            mint(_to[i],1);
        }
        giveawayCount=giveawayCount.add(_to.length);

    }

    /*
     * Function to get token URI of given token ID
     * URI will be blank untill totalSupply reaches MAX_NFT_PUBLIC
    */
    function uri(
        uint256 _tokenId
    )
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(_tokenId<totalSupply, "ERC1155Metadata: URI query for nonexistent token");

        if (!canReveal) {
            return string(abi.encodePacked(baseURI));
        } else {
            return string(abi.encodePacked(baseURI, _tokenId.toString()));
        }
    }

    /*
     * Function to withdraw collected amount during minting by the owner
    */
     function withdrawOwner(
        ) 
            public 
            onlyOwner 
        {
            uint balance = address(this).balance;
            require(balance > 0, "Balance should be more then zero");
            payable(owner()).transfer(balance);
        }
    /*
     * Function to set the merkle root
    */
    function setRoot(uint256 _root) onlyOwner() public {
        root = bytes32(_root);
    }

    /*
     * Function to verify the proof
    */
    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 setCanReveal(
        bool _isActive
    )
        external
        onlyOwner
    {
        canReveal = _isActive;
    }
/*
 * Function setCanReveal to activate/desactivate reveal option
 */