contract AncientGods is ERC721Enumerable, ReentrancyGuard, Ownable {
    using Strings for uint256;
    using MerkleProof for bytes32[];

    // for whitelist
    bytes32 merkleRoot;
    mapping(address => uint256) public whiteUsers;

    // Wednesday, September 8, 2021 4:00:00 PM
    uint256 public startTime = 1631116800;
    uint256 public whiteClaimCount = 1000;
    uint256 public price = 0.02 ether;
    string public blindboxURI = '';
    string private _baseURIExtended;

    //global token id
    uint256 _globalTokenId = 1;

    function setMerkleRoot(bytes32 root) external onlyOwner {
        require(merkleRoot == bytes32(0), 'merkleRoot has set');
        merkleRoot = root;
    }

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

    function setBlindboxURI(string memory _URI) external onlyOwner {
        blindboxURI = _URI;
    }

    function setBaseURI(string memory baseURI_) external onlyOwner {
        _baseURIExtended = baseURI_;
    }

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

        string memory _tokenURI = blindboxURI;
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString()));
    }

    // default to claim
    receive() external payable {
        claim();
    }

    function claim() public payable nonReentrant {
        require(block.timestamp > startTime, 'not yet started');
        require(msg.value >= price, 'pay 0.02 eth');
        require(whiteClaimCount + _globalTokenId < 7778, 'Token ID invalid');
        _safeMint(_msgSender(), _globalTokenId);
        _globalTokenId++;
    }

    function whiteClaim(bytes32[] calldata proof) public payable nonReentrant {
        require(whiteClaimCount > 0, 'end');
        require(proof.verify(merkleRoot, keccak256(abi.encodePacked(msg.sender))), 'not white');
        require(whiteUsers[msg.sender] < 2, 'claimed');
        whiteUsers[msg.sender] = whiteUsers[msg.sender] + 1;
        _safeMint(_msgSender(), _globalTokenId);
        whiteClaimCount--;
        _globalTokenId++;
    }

    function releaseWhiteShare() external onlyOwner {
        whiteClaimCount = 0;
    }

    function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
        require(tokenId > 7777 && tokenId < 8001, 'Token ID invalid');
        _safeMint(owner(), tokenId);
        _globalTokenId++;
    }

    function withdraw(uint256 amount) external onlyOwner {
        payable(owner()).transfer(amount);
    }

    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT license
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return '0';
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    constructor() ERC721('AncientGods', 'ANCIENTGODS') Ownable() {}
}
// default to claim