contract PandaParadise is ERC721Enumerable, Ownable {

    using Strings for uint256;

    string _baseTokenURI;
    uint256 private _reserved = 88;
    uint256 private _price = 0.08 ether;
    bool public _paused = true;
    bool public _pausedPrivate = true;
    bool public sealedTokenURI;
    mapping(address => bool) private _privateSaleWhitelist;



    // withdraw addresses
    address t1 = 0xe8E8561d8a4965633af42B1A99F60ED81411A9B4;
    address t2 = 0x92B66F71ffC366b15a37Bd9C5D9aEFd753624a52;

    // 8888 Pandas in total
    constructor(string memory baseURI) ERC721("PandaParadise", "PPA")  {
        sealedTokenURI = false;
        setBaseURI(baseURI);

        // team gets the first Panda
        _safeMint( t1, 0);
    }
    
    function sealTokenURI() external onlyOwner {
        sealedTokenURI = true;
    }

    function mint(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( !_paused,                              "Sale paused" );
        require( num < 19,                              "You can mint a maximum of 18 Paradise Pandas" );
        require( supply + num < 8889 - _reserved,       "Exceeds maximum Paradise Panda supply" );
        require( msg.value >= _price * num,             "Ether sent is not correct" );

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
    }

    function privateMint(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( !_pausedPrivate,                     "Private Sale paused" );
        require( num < 19,                            "You can mint a maximum of 18 Paradise Pandas" );
        require( supply + num < 8889 - _reserved,     "Exceeds maximum Paradise Panda supply" );
        require( msg.value >= _price * num,           "Ether sent is not correct" );
        require( isWhitelisted(msg.sender) == true,   "Not Whitelisted for private sale" );
        _privateSaleWhitelist[_msgSender()] = false;

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
    }

    function walletOfOwner(address _owner) public view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

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

    // In case Eth fluctuates too much
    function setPrice(uint256 _newPrice) public onlyOwner() {
        _price = _newPrice;
    }

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

    function setBaseURI(string memory baseURI) public onlyOwner {
        require(!sealedTokenURI, "baseURI is sealed");
        _baseTokenURI = baseURI;
    }

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

    function giveAway(address _to, uint256 _amount) external onlyOwner() {
        require( _amount <= _reserved, "Exceeds reserved Panda Paradise supply" );

        uint256 supply = totalSupply();
        for(uint256 i; i < _amount; i++){
            _safeMint( _to, supply + i );
        }

        _reserved -= _amount;
    }

    function pause(bool val) public onlyOwner {
        _paused = val;
    }

    function pausePrivate(bool val) public onlyOwner {
        _pausedPrivate = val;
    }

    function withdrawAll() public payable onlyOwner {
        uint256 _amount1 = (address(this).balance * 99) / 100;
        require(payable(t1).send(_amount1));

        uint256 _amount2 = address(this).balance;
        require(payable(t2).send(_amount2));
    }

    function isWhitelisted(address _from) public view returns (bool) {
        return _privateSaleWhitelist[_from];
    }
    
    function updateWhitelist(address[] calldata whitelist) public onlyOwner {
        for (uint256 i = 0; i < whitelist.length; i++) {
            _privateSaleWhitelist[whitelist[i]] = true;
        }
    }

    fallback() external payable { }
    
    receive() external payable { }
}
function setPrice(uint256 _newPrice) public onlyOwner() {
    _price = _newPrice;
}

// In case Eth fluctuates too much