contract GMNFT is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Strings for uint256;
    string internal BaseURI;
    mapping(uint256 => string) internal _tokenURIs;

    mapping(address => uint256) public referNumber; //指定地址推荐人数
    mapping(address => bool) public claimStatus; //指定地址领取NFT的状态
    uint256 public next_tokenID;
    event ClaimNFT(address _user, uint256 _tokenID, address _referrer);
    constructor() ERC721("Gametaverse Ambassador", "GAMETA") {}
    
    //领取NFT
    function claimNFT(address _referrer)  public  returns(uint256){
        require(!claimStatus[msg.sender], "the user already claimed");
        require(next_tokenID < 8000, "the NFT claim already finished");
        require(msg.sender !=  _referrer, "the referrer is caller");
        uint256 _next_tokenID = next_tokenID;
        _safeMint(msg.sender, _next_tokenID);
        if (_referrer != address(0)){
            referNumber[_referrer] = referNumber[_referrer].add(1);
        }
        claimStatus[msg.sender] = true;
        next_tokenID++;
        emit ClaimNFT(msg.sender, _next_tokenID, _referrer);
        return _next_tokenID;
    }

    function getBaseURI() public view returns (string memory) {
        return BaseURI;
    }

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


    function setbaseURI(string memory _BaseURI) public onlyOwner {
        BaseURI = _BaseURI;
    }

    function setTokenURI(uint256 tokenId, string memory _tokenURI) public onlyOwner{
        require(_exists(tokenId),"URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }


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

        string memory _tokenURI = _tokenURIs[tokenId];

        // If there is no base URI, return the token URI.
        if (bytes(BaseURI).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(BaseURI, _tokenURI));
        }

        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(BaseURI,"?id=", tokenId.toString()));
    }
}
function claimNFT(address _referrer)  public  returns(uint256){
    require(!claimStatus[msg.sender], "the user already claimed");
    require(next_tokenID < 8000, "the NFT claim already finished");
    require(msg.sender !=  _referrer, "the referrer is caller");
    uint256 _next_tokenID = next_tokenID;
    _safeMint(msg.sender, _next_tokenID);
    if (_referrer != address(0)){
        referNumber[_referrer] = referNumber[_referrer].add(1);
    }
    claimStatus[msg.sender] = true;
    next_tokenID++;
    emit ClaimNFT(msg.sender, _next_tokenID, _referrer);
    return _next_tokenID;
}

//领取NFT