contract ERC721Token is ERC721Enumerable, ERC721URIStorage, Ownable, ERC721Burnable, ERC721Pausable {
  using EnumerableMap for EnumerableMap.UintToAddressMap;
  using Counters for Counters.Counter;

  // Counter that tracks incremental token ids
  Counters.Counter private _tokenIds;

  // Authors mapping (tokenId -> author address)
  EnumerableMap.UintToAddressMap private _tokenIdAuthor;

  // Constructor will be called only 1 time, when the Smart Contract gets deployed
  constructor(
    string memory _name,
    string memory _symbol
  ) ERC721(_name, _symbol) {}

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Enumerable, ERC721) returns (bool) {
    return super.supportsInterface(interfaceId);
  }

  /**
   * Minting function. Anyone can call it.
   */
  function mint(address _to, string memory _tokenURI) 
    public
    whenNotPaused
    returns (uint256) 
  {
    return _mint(_to, _tokenURI, _msgSender()); 
  }

  /**
   * Only Owner minting function. To use if minting in the name of an author.
   */
  function mintWithAuthor(address _to, string memory _tokenURI, address _author)
    public
    onlyOwner
    whenNotPaused
    returns (uint256) 
  {
    return _mint(_to, _tokenURI, _author);
  }

  /**
   * Minting function.
   *
   * Will mint a new NFT with the tokenURI as property (ideally should point to a distributed storage URI),
   * setup the mapping of the newly created token ID and the provided author and send the NFT to _to after minting.
   */ 
  function _mint(address _to, string memory _tokenURI, address _author)
    internal
    whenNotPaused
    returns (uint256)
  {
    require(bytes(_tokenURI).length > 0, "ERR: Empty tokenURI");
    require(_author != address(0), "ERR: 0x0 author address");

    _tokenIds.increment();
     
    uint256 _newTokenId = _tokenIds.current();
    super._mint(_to, _newTokenId);
    _setTokenURI(_newTokenId, _tokenURI);

    _tokenIdAuthor.set(_newTokenId, _author);

    return _newTokenId;
  }

  /**
   * Getter to retrieve the author for a given token Id
   */
  function tokenIdAuthor(uint256 _tokenId)
    external
    view
    returns (address author)
  {
    require(_tokenIdAuthor.contains(_tokenId), "ERR: Unexisting token");
 
    return _tokenIdAuthor.get(_tokenId);
  }

  function pause() public onlyOwner {
    super._pause();
  }

  function unpause() public onlyOwner {
    super._unpause();
  }

  function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Pausable, ERC721Enumerable) {
    super._beforeTokenTransfer(from, to, tokenId);
  }

  function _burn(uint256 tokenId) internal virtual whenNotPaused override(ERC721URIStorage, ERC721) {
    super._burn(tokenId);
  }

  function tokenURI(uint256 tokenId) public view virtual override (ERC721URIStorage, ERC721) returns (string memory) {
    return super.tokenURI(tokenId);
  }

  function transfer(address from, address to, uint256 tokenId) public virtual {
    return super.safeTransferFrom(from, to, tokenId);
  }

}
function tokenIdAuthor(uint256 _tokenId)
  external
  view
  returns (address author)
{
  require(_tokenIdAuthor.contains(_tokenId), "ERR: Unexisting token");
 
  return _tokenIdAuthor.get(_tokenId);
}

/**
 * Getter to retrieve the author for a given token Id
 */