contract Dicks is ERC721, Ownable {

  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;

  // override for the private URI inside OZ
  string public __baseURI; 

  // kind ID => true indicates that the dick exists
  mapping (uint256 => bool) public kindExists;

  // dick ID => kind ID
  mapping (uint256 => uint256) public kind;
  
  // kind ID => URI
  mapping (uint256 => string) public kindURIs;

  // the contract that has the right to mint
  address public minter;

  modifier onlyMinter() {
    require(msg.sender == minter, "Cannot mint");
    _;
  }

  constructor() public ERC721("Dicks: The Seed Collection (FUN)", "DICK") {
  }

  /// @dev Allows the owner to set a new minter
  function setMinter(address _minter) external onlyOwner {
    minter = _minter;
  }

  /// @dev Allows the minter to mint a new dick
  function mint(address _recipient, uint256 _kind) external onlyMinter returns (uint256) {
    require(kindExists[_kind], "No such dick");
    _tokenIds.increment();
    uint256 newDickId = _tokenIds.current();
    _safeMint(_recipient, newDickId);
    kind[newDickId] = _kind;
    return newDickId;
  }

  /// @dev Returns the URI of the token (dick) provided that it exists
  function tokenURI(uint256 tokenId) public view override returns (string memory) {
    require(_exists(tokenId), "Dick: URI query for nonexistent token");

    string memory kindURI = kindURIs[kind[tokenId]];
    string memory base = __baseURI;

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

  /// @dev Allows the owner to change the base URI that hosts the images
  function setBaseURI(string calldata _uri) external onlyOwner {
    __baseURI = _uri;
  }

  /// @dev Allows the owner to set the kind URI 
  function setKindExistsAndURI(uint256 _kind, bool _exists, string calldata _uri) external onlyOwner {
    kindURIs[_kind] = _uri; 
    kindExists[_kind] = _exists;
  }

  /// @dev OZ override
  function baseURI() public view override returns (string memory) {
    return __baseURI;
  }
}
function mint(address _recipient, uint256 _kind) external onlyMinter returns (uint256) {
  require(kindExists[_kind], "No such dick");
  _tokenIds.increment();
  uint256 newDickId = _tokenIds.current();
  _safeMint(_recipient, newDickId);
  kind[newDickId] = _kind;
  return newDickId;
}

/// @dev Allows the minter to mint a new dick