contract CyberDucksEvolution is ERC721Enumerable, Ownable {
    using Strings for uint256;

    uint256 public constant MAX_DUCKS = 7777;
    uint256 public constant RESERVES = 27;
    bool public paused = false;
    bool public revealed = false;
    string public baseExtension = ".json";
    string public notRevealedUri;
    string public baseTokenURI;
    bytes32 public merkleRoot;
    uint256 private _maxPerTx = 10;
    uint256 private _price = 40000000000000000; // .04 ETH
    uint256 private _price_wl = 30000000000000000; // .03 ETH
    uint256 private _saleTime = 1640736000; // 29/12/2021 @ 00:00:00 (GMT)
    address private OPENSEA_STORE = 0x495f947276749Ce646f68AC8c248420045cb7b5e;

    mapping(uint256 => bool) ducksUsedForFree;
    mapping(address => uint256) addressToWLDucksMinted;

    uint256[150] private genesisDucks = [
    33545808174778596038611434130458611713285808338920316774188539806835659505665,
    33545808174778596038611434130458611713285808338920316774188539807935171133441,
    33545808174778596038611434130458611713285808338920316774188539809034682761217,
    33545808174778596038611434130458611713285808338920316774188539810134194388993,
    33545808174778596038611434130458611713285808338920316774188539811233706016769,
    33545808174778596038611434130458611713285808338920316774188539812333217644545,
    33545808174778596038611434130458611713285808338920316774188539813432729272321,
    33545808174778596038611434130458611713285808338920316774188539814532240900097,
    33545808174778596038611434130458611713285808338920316774188539815631752527873,
    33545808174778596038611434130458611713285808338920316774188539816731264155649,
    33545808174778596038611434130458611713285808338920316774188539817830775783425,
    33545808174778596038611434130458611713285808338920316774188539818930287411201,
    33545808174778596038611434130458611713285808338920316774188539820029799038977,
    33545808174778596038611434130458611713285808338920316774188539821129310666753,
    33545808174778596038611434130458611713285808338920316774188539822228822294529,
    33545808174778596038611434130458611713285808338920316774188539823328333922305,
    33545808174778596038611434130458611713285808338920316774188539824427845550081,
    33545808174778596038611434130458611713285808338920316774188539825527357177857,
    33545808174778596038611434130458611713285808338920316774188539826626868805633,
    33545808174778596038611434130458611713285808338920316774188539827726380433409,
    33545808174778596038611434130458611713285808338920316774188539829925403688961,
    33545808174778596038611434130458611713285808338920316774188539831024915316737,
    33545808174778596038611434130458611713285808338920316774188539832124426944513,
    33545808174778596038611434130458611713285808338920316774188539833223938572289,
    33545808174778596038611434130458611713285808338920316774188539834323450200065,
    33545808174778596038611434130458611713285808338920316774188539835422961827841,
    33545808174778596038611434130458611713285808338920316774188539836522473455617,
    33545808174778596038611434130458611713285808338920316774188539837621985083393,
    33545808174778596038611434130458611713285808338920316774188539838721496711169,
    33545808174778596038611434130458611713285808338920316774188539839821008338945,
    33545808174778596038611434130458611713285808338920316774188539840920519966721,
    33545808174778596038611434130458611713285808338920316774188539842020031594497,
    33545808174778596038611434130458611713285808338920316774188539843119543222273,
    33545808174778596038611434130458611713285808338920316774188539844219054850049,
    33545808174778596038611434130458611713285808338920316774188539845318566477825,
    33545808174778596038611434130458611713285808338920316774188539846418078105601,
    33545808174778596038611434130458611713285808338920316774188539847517589733377,
    33545808174778596038611434130458611713285808338920316774188539848617101361153,
    33545808174778596038611434130458611713285808338920316774188539849716612988929,
    33545808174778596038611434130458611713285808338920316774188539850816124616705,
    33545808174778596038611434130458611713285808338920316774188539851915636244481,
    33545808174778596038611434130458611713285808338920316774188539853015147872257,
    33545808174778596038611434130458611713285808338920316774188539854114659500033,
    33545808174778596038611434130458611713285808338920316774188539855214171127809,
    33545808174778596038611434130458611713285808338920316774188539856313682755585,
    33545808174778596038611434130458611713285808338920316774188539857413194383361,
    33545808174778596038611434130458611713285808338920316774188539858512706011137,
    33545808174778596038611434130458611713285808338920316774188539859612217638913,
    33545808174778596038611434130458611713285808338920316774188539860711729266689,
    33545808174778596038611434130458611713285808338920316774188539861811240894465,
    33545808174778596038611434130458611713285808338920316774188539862910752522241,
    33545808174778596038611434130458611713285808338920316774188539864010264150017,
    33545808174778596038611434130458611713285808338920316774188539865109775777793,
    33545808174778596038611434130458611713285808338920316774188539866209287405569,
    33545808174778596038611434130458611713285808338920316774188539867308799033345,
    33545808174778596038611434130458611713285808338920316774188539868408310661121,
    33545808174778596038611434130458611713285808338920316774188539869507822288897,
    33545808174778596038611434130458611713285808338920316774188539870607333916673,
    33545808174778596038611434130458611713285808338920316774188539871706845544449,
    33545808174778596038611434130458611713285808338920316774188539872806357172225,
    33545808174778596038611434130458611713285808338920316774188539873905868800001,
    33545808174778596038611434130458611713285808338920316774188539875005380427777,
    33545808174778596038611434130458611713285808338920316774188539876104892055553,
    33545808174778596038611434130458611713285808338920316774188539877204403683329,
    33545808174778596038611434130458611713285808338920316774188539878303915311105,
    33545808174778596038611434130458611713285808338920316774188539879403426938881,
    33545808174778596038611434130458611713285808338920316774188539880502938566657,
    33545808174778596038611434130458611713285808338920316774188539881602450194433,
    33545808174778596038611434130458611713285808338920316774188539882701961822209,
    33545808174778596038611434130458611713285808338920316774188539883801473449985,
    33545808174778596038611434130458611713285808338920316774188539884900985077761,
    33545808174778596038611434130458611713285808338920316774188539886000496705537,
    33545808174778596038611434130458611713285808338920316774188539887100008333313,
    33545808174778596038611434130458611713285808338920316774188539888199519961089,
    33545808174778596038611434130458611713285808338920316774188539889299031588865,
    33545808174778596038611434130458611713285808338920316774188539890398543216641,
    33545808174778596038611434130458611713285808338920316774188539891498054844417,
    33545808174778596038611434130458611713285808338920316774188539892597566472193,
    33545808174778596038611434130458611713285808338920316774188539893697078099969,
    33545808174778596038611434130458611713285808338920316774188539894796589727745,
    33545808174778596038611434130458611713285808338920316774188539895896101355521,
    33545808174778596038611434130458611713285808338920316774188539896995612983297,
    33545808174778596038611434130458611713285808338920316774188539898095124611073,
    33545808174778596038611434130458611713285808338920316774188539899194636238849,
    33545808174778596038611434130458611713285808338920316774188539900294147866625,
    33545808174778596038611434130458611713285808338920316774188539901393659494401,
    33545808174778596038611434130458611713285808338920316774188539902493171122177,
    33545808174778596038611434130458611713285808338920316774188539903592682749953,
    33545808174778596038611434130458611713285808338920316774188539904692194377729,
    33545808174778596038611434130458611713285808338920316774188539905791706005505,
    33545808174778596038611434130458611713285808338920316774188539906891217633281,
    33545808174778596038611434130458611713285808338920316774188539907990729261057,
    33545808174778596038611434130458611713285808338920316774188539909090240888833,
    33545808174778596038611434130458611713285808338920316774188539910189752516609,
    33545808174778596038611434130458611713285808338920316774188539911289264144385,
    33545808174778596038611434130458611713285808338920316774188539912388775772161,
    33545808174778596038611434130458611713285808338920316774188539913488287399937,
    33545808174778596038611434130458611713285808338920316774188539914587799027713,
    33545808174778596038611434130458611713285808338920316774188539915687310655489,
    33545808174778596038611434130458611713285808338920316774188539916786822283265,
    33545808174778596038611434130458611713285808338920316774188539917886333911041,
    33545808174778596038611434130458611713285808338920316774188539918985845538817,
    33545808174778596038611434130458611713285808338920316774188539920085357166593,
    33545808174778596038611434130458611713285808338920316774188539921184868794369,
    33545808174778596038611434130458611713285808338920316774188539922284380422145,
    33545808174778596038611434130458611713285808338920316774188539923383892049921,
    33545808174778596038611434130458611713285808338920316774188539924483403677697,
    33545808174778596038611434130458611713285808338920316774188539925582915305473,
    33545808174778596038611434130458611713285808338920316774188539926682426933249,
    33545808174778596038611434130458611713285808338920316774188539927781938561025,
    33545808174778596038611434130458611713285808338920316774188539928881450188801,
    33545808174778596038611434130458611713285808338920316774188539929980961816577,
    33545808174778596038611434130458611713285808338920316774188539931080473444353,
    33545808174778596038611434130458611713285808338920316774188539932179985072129,
    33545808174778596038611434130458611713285808338920316774188539933279496699905,
    33545808174778596038611434130458611713285808338920316774188539934379008327681,
    33545808174778596038611434130458611713285808338920316774188539935478519955457,
    33545808174778596038611434130458611713285808338920316774188539936578031583233,
    33545808174778596038611434130458611713285808338920316774188539937677543211009,
    33545808174778596038611434130458611713285808338920316774188539938777054838785,
    33545808174778596038611434130458611713285808338920316774188539939876566466561,
    33545808174778596038611434130458611713285808338920316774188539940976078094337,
    33545808174778596038611434130458611713285808338920316774188539942075589722113,
    33545808174778596038611434130458611713285808338920316774188539943175101349889,
    33545808174778596038611434130458611713285808338920316774188539944274612977665,
    33545808174778596038611434130458611713285808338920316774188539945374124605441,
    33545808174778596038611434130458611713285808338920316774188539946473636233217,
    33545808174778596038611434130458611713285808338920316774188539947573147860993,
    33545808174778596038611434130458611713285808338920316774188539948672659488769,
    33545808174778596038611434130458611713285808338920316774188539949772171116545,
    33545808174778596038611434130458611713285808338920316774188539950871682744321,
    33545808174778596038611434130458611713285808338920316774188539951971194372097,
    33545808174778596038611434130458611713285808338920316774188539953070705999873,
    33545808174778596038611434130458611713285808338920316774188539954170217627649,
    33545808174778596038611434130458611713285808338920316774188539955269729255425,
    33545808174778596038611434130458611713285808338920316774188539956369240883201,
    33545808174778596038611434130458611713285808338920316774188539957468752510977,
    33545808174778596038611434130458611713285808338920316774188539958568264138753,
    33545808174778596038611434130458611713285808338920316774188539959667775766529,
    33545808174778596038611434130458611713285808338920316774188539960767287394305,
    33545808174778596038611434130458611713285808338920316774188539961866799022081,
    33545808174778596038611434130458611713285808338920316774188539962966310649857,
    33545808174778596038611434130458611713285808338920316774188539964065822277633,
    33545808174778596038611434130458611713285808338920316774188539965165333905409,
    33545808174778596038611434130458611713285808338920316774188539966264845533185,
    33545808174778596038611434130458611713285808338920316774188539967364357160961,
    33545808174778596038611434130458611713285808338920316774188539968463868788737,
    33545808174778596038611434130458611713285808338920316774188539969563380416513,
    33545808174778596038611434130458611713285808338920316774188539970662892044289,
    33545808174778596038611434130458611713285808338920316774188539971762403672065
    ];

    constructor(string memory baseURI, string memory initNotRevealedUri,bytes32 _merkleRoot)
    ERC721("CyberDucksEvolution", "CDE")
    {
        setBaseURI(baseURI);
        setNotRevealedURI(initNotRevealedUri);
        setMerkleRoot(_merkleRoot);
    }

    modifier preSaleIsOpen() {
        require(
            block.timestamp >= _saleTime - 172800, //2g
            "The presale has not yet started"
        );
        require(
            block.timestamp < _saleTime,
            "The public sale has already started"
        );
        _;
    }

    modifier saleIsOpen() {
        require(block.timestamp >= _saleTime, "Sale is not yet open");
        _;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }

    function setPrice(uint256 _newWEIPrice) public onlyOwner {
        _price = _newWEIPrice;
    }

    function getSaleTime() public view returns (uint256) {
        return _saleTime;
    }

    function getPrice() public view returns (uint256) {
        return _price;
    }

    function getWLDucksMinted(address _address) internal view returns (uint256) {
        return addressToWLDucksMinted[_address];
    }

    function getWLDucksAmount() internal view returns (uint256) {
        uint256 ducksOwned = 0;

        for (uint i = 0; i < genesisDucks.length; i ++) {
            if(Opensea(OPENSEA_STORE).balanceOf(msg.sender,genesisDucks[i])){
                ducksOwned += 1;
            }
        }

        if(ducksOwned == 0)
            ducksOwned = 1;

        return ducksOwned * 5;
    }

    function getGenesisDucks() internal returns (uint256) {
        uint256 ducksOwned = 0;

        for (uint i = 0; i < genesisDucks.length; i ++) {
            if(!ducksUsedForFree[genesisDucks[i]]){//Exclude genesis already used
                if(Opensea(OPENSEA_STORE).balanceOf(msg.sender,genesisDucks[i])){
                    ducksOwned += 1;
                    ducksUsedForFree[genesisDucks[i]] = true;
                }
            }
        }

        return ducksOwned;
    }

    function publicCheckFreeDucks() public view returns(uint256) {
        uint256 ducksOwned = 0;

        for (uint i = 0; i < genesisDucks.length; i ++) {
            if(!ducksUsedForFree[genesisDucks[i]]){//Exclude genesis already used
                if(Opensea(OPENSEA_STORE).balanceOf(msg.sender,genesisDucks[i])){
                    ducksOwned += 1;
                }
            }
        }

        return ducksOwned;
    }

    function getMintedDucks() public view returns(uint256){
        return totalSupply();
    }

    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) public pure returns (bool) {
        return MerkleProof.verify(root, leaf, proof);
    }

    function whiteListMint(uint256 _count,bytes32[] calldata _merkleProof) public payable preSaleIsOpen{
        uint256 totalSupply = totalSupply();

        require(
            !paused,
            "The contract is paused"
        );

        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));

        require(
            verify(_merkleProof, merkleRoot, leaf),
            "You are not in whitelist"
        );

        uint256 wlDucksAmount = getWLDucksAmount();

        if(addressToWLDucksMinted[msg.sender] + _count >= wlDucksAmount){
            _count = wlDucksAmount - addressToWLDucksMinted[msg.sender];
        }

        require(
            wlDucksAmount > 0,
            "No more Ducks to be minted"
        );

        require(
            _count <= _maxPerTx,
            "You can mint up to 10 Ducks per time"
        );

        require(
            _price_wl * _count <= msg.value,
            "Transaction value too low");

        for (uint256 i; i < _count; i++) {
            _safeMint(msg.sender, totalSupply + i);
        }

        addressToWLDucksMinted[msg.sender] += _count;
    }

    function freeMint() public payable {
        uint256 totalSupply = totalSupply();

        require(
            !paused,
            "The contract is paused"
        );

        uint256 _genesisDucks = getGenesisDucks();

        require(
            _genesisDucks > 0,
            "You cannot claim any other Ducks for free"
        );

        for (uint256 i; i < _genesisDucks; i++) {
            _safeMint(msg.sender, totalSupply + i);
        }
    }

    function mint(uint256 _count) public payable saleIsOpen{
        uint256 totalSupply = totalSupply();

        require(
            !paused,
            "The contract is paused"
        );

        require(
            _count > 0,
            "Mint at least 1 Duck"
        );

        require(
            _count <= _maxPerTx,
            "You can mint up to 10 Ducks per time"
        );

        require(
            totalSupply < MAX_DUCKS,
            "All Duck are already minted");

        require(
            totalSupply + _count <= MAX_DUCKS,
            "This amount of Ducks will exceed max supply"
        );

        require(
            _price * _count <= msg.value,
            "Transaction value too low");

        for (uint256 i; i < _count; i++) {
            _safeMint(msg.sender, totalSupply + i);
        }
    }

    function walletOfOwner(address _address) public view returns (uint256[] memory) {
        uint256 tokenCount = balanceOf(_address);
        if (tokenCount == 0) {
            return new uint256[](0);
        }

        uint256[] memory tokensId = new uint256[](tokenCount);
        for (uint256 i; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(_address, i);
        }
        return tokensId;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );

        if(revealed == false) {
            return notRevealedUri;
        }

        string memory currentBaseURI = _baseURI();
        return bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";
    }

    /*only owner*/
    function reveal(bool _reveal) public onlyOwner() {
        revealed = _reveal;
    }

    function pause(bool _state) public onlyOwner {
        paused = _state;
    }

    function setSaleTime(uint256 _time) public onlyOwner {
        _saleTime = _time;
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        baseTokenURI = baseURI;
    }

    function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner {
        merkleRoot = _merkleRoot;
    }

    function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
        baseExtension = _newBaseExtension;
    }

    function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
        notRevealedUri = _notRevealedURI;
    }

    function withdrawAll() public payable onlyOwner {

        (bool hs, ) = payable(0xA8AC6BFc4D6B1339A37574705B951475771f9a9D).call{value: address(this).balance * 15 / 100}("");
        require(hs);

        (bool os, ) = payable(owner()).call{value: address(this).balance}("");
        require(os);
    }

    function reserveDUCKS(uint256 _count) public onlyOwner {
        uint256 totalSupply = totalSupply();

        for (uint256 i; i < _count; i++) {
            _safeMint(msg.sender, totalSupply + i);
        }
    }
}
function reveal(bool _reveal) public onlyOwner() {
    revealed = _reveal;
}

/*only owner*/