contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {

    // Token class name e.g. "2019 Coachella Gathering Trophies" 
    string internal _name;

    // Token class symbol e.g. "CGT19"
    string internal _symbol;

    // Mapping for token URIs
    mapping(uint256 => string) internal _tokenURIs;

    // // Optional mapping for token names
    mapping(uint256 => string) internal _tokenNames;

    bytes4 internal constant _INTERFACE_ID_ERC721_METADATA = 0xbc7bebe8;
    /**
     * 0xbc7bebe8 ===
     *     bytes4(keccak256('name()')) ^
     *     bytes4(keccak256('symbol()')) ^
     *     bytes4(keccak256('tokenURI(uint256)')) ^
     *     bytes4(keccak256('tokenName(uint256)'))
     */

    /**
     * @dev Constructor function
     */
    constructor (string memory name, string memory symbol) public {
        _name = name;
        _symbol = symbol;

        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721_METADATA);
    }

    /**
     * @dev Gets the token name
     * @return string representing the token name
     */
    function name() external view returns (string memory) {
        return _name;
    }

    /**
     * @dev Gets the token symbol
     * @return string representing the token symbol
     */
    function symbol() external view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns an URI for a given token ID
     * Throws if the token ID does not exist. May return an empty string.
     * @param tokenId uint256 ID of the token to query
     */
    function tokenURI(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId));
        return _tokenURIs[tokenId];
    }

    /**
     * @dev Returns a trophy name for a given token ID
     * Throws if the token ID does not exist. May return an empty string.
     * @param tokenId uint256 ID of the token to query
     */
    function tokenName(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId));
        return _tokenNames[tokenId];
    }

    /**
     * @dev Internal function to set the token URI for a given token
     * Reverts if the token ID does not exist
     * @param tokenId uint256 ID of the token to set its URI
     * @param uri string URI to assign
     */
    function _setTokenURI(uint256 tokenId, string memory uri) internal {
        require(_exists(tokenId));
        _tokenURIs[tokenId] = uri;
    }

    /**
     * @dev Internal function that extracts the part of a string based on the desired length and offset. The
     *      offset and length must not exceed the lenth of the base string.
     * 
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string that will be used for 
     *              extracting the sub string from
     * @param _length The length of the sub string to extract
     * @param _offset The starting point to extract the sub string from
     * @return string The extracted sub string
     */

    function _substring(string memory _base, int _length, int _offset) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);

        assert(uint(_offset+_length) <= _baseBytes.length);

        string memory _tmp = new string(uint(_length));
        bytes memory _tmpBytes = bytes(_tmp);

        uint j = 0;
            for(uint i = uint(_offset); i < uint(_offset+_length); i++) {
                _tmpBytes[j++] = _baseBytes[i];
            }
            return string(_tmpBytes);
        }
}
function tokenName(uint256 tokenId) external view returns (string memory) {
    require(_exists(tokenId));
    return _tokenNames[tokenId];
}

/**
 * @dev Returns a trophy name for a given token ID
 * Throws if the token ID does not exist. May return an empty string.
 * @param tokenId uint256 ID of the token to query
 */