contract BonkNftMinter is ERC721Full, Ownable, Callable {

  using SafeMath for uint256;

  // Mapping from token ID to the creator's address.
  mapping(uint256 => address) private tokenCreators;

  // Counter for creating token IDs
  uint256 private idCounter;

  // BONK ERC20 token
  IERC20 public bonkToken;

  // Where to send collected fees
  address public feeCollector;

  // BONK fee amount with decimals, for example, 1*10**18 means one BONK
  uint256 public bonkFee;

  // Event indicating metadata was updated.
  event TokenURIUpdated(uint256 indexed _tokenId, string _uri);

  // Event indicating bonk fee was updated.
  event BonkFeeUpdated(uint256 _newFee, uint _timestamp);

  constructor(
    string memory _name,
    string memory _symbol,
    address _bonkToken,
    address _feeCollector,
    uint256 _bonkFee
  )
  public
  ERC721Full(_name, _symbol)
  {
    bonkToken = IERC20(_bonkToken);
    feeCollector = _feeCollector;
    bonkFee = _bonkFee;
  }

  /**
   * @dev Checks that the token is owned by the sender.
   * @param _tokenId uint256 ID of the token.
   */
  modifier onlyTokenOwner(uint256 _tokenId) {
    address owner = ownerOf(_tokenId);
    require(owner == msg.sender, "must be the owner of the token");
    _;
  }

  /**
  * @dev Checks that the caller is BONK token.
  */
  modifier onlyBonkToken() {
    require(msg.sender == address(bonkToken), "must be BONK token");
    _;
  }

  /**
   * @dev callback function that is called by BONK token. Adds new NFT token. Trusted.
   * @param _from who sent the tokens.
   * @param _tokens how many tokens were sent.
   * @param _data extra call data.
   * @return success.
   */
  function tokenCallback(address _from, uint256 _tokens, bytes calldata _data)
  external
  onlyBonkToken
  returns (bool) {
    if (bonkFee > 0) {
      uint256 tokensWithTransferFee = _tokens * 100 / 99; // there is 1% fee upon some transfers of BONK
      require(tokensWithTransferFee >= bonkFee, "not enough tokens");
      _forwardBonkTokens();
    }
    _createToken(string(_data), _from);
    return true;
  }

  /**
   * @dev Adds a new unique token to the supply.
   * @param _uri string metadata uri associated with the token.
   */
  function addNewToken(string calldata _uri) external {
    if (bonkFee > 0) {
      require(bonkToken.transferFrom(msg.sender, address(this), bonkFee), "fee transferFrom failed");
      _forwardBonkTokens();
    }
    _createToken(_uri, msg.sender);
  }

  /**
   * @dev Deletes the token with the provided ID.
   * @param _tokenId uint256 ID of the token.
   */
  function deleteToken(uint256 _tokenId) external onlyTokenOwner(_tokenId) {
    _burn(msg.sender, _tokenId);
  }

  /**
   * @dev Allows owner of the contract updating the token metadata in case there is a need.
   * @param _tokenId uint256 ID of the token.
   * @param _uri string metadata URI.
   */
  function updateTokenMetadata(uint256 _tokenId, string calldata _uri) external onlyOwner {
    _setTokenURI(_tokenId, _uri);
    emit TokenURIUpdated(_tokenId, _uri);
  }

  /**
   * @dev change address of BONK token
   * @param _bonkToken address of ERC20 contract
   */
  function setBonkToken(address _bonkToken) external onlyOwner {
    bonkToken = IERC20(_bonkToken);
  }

  /**
   * @dev change address of where collected fees are sent
   * @param _feeCollector address where to send the fees
   */
  function setFeeCollector(address _feeCollector) external onlyOwner {
    feeCollector = _feeCollector;
  }

  /**
   * @dev change BONK fee
   * @param _bonkFee new fee in BONK (with decimals)
   */
  function setBonkFee(uint _bonkFee) external onlyOwner {
    bonkFee = _bonkFee;
    emit BonkFeeUpdated(_bonkFee, now);
  }

  /**
   * @dev allows withdrawal of ETH in case it was sent by accident
   * @param _beneficiary address where to send the eth.
   */
  function withdrawEth(address payable _beneficiary) external onlyOwner {
    _beneficiary.transfer(address(this).balance);
  }

  /**
   * @dev allows withdrawal of ERC20 token in case it was sent by accident
   * @param _tokenAddress address of ERC20 token.
   * @param _beneficiary address where to send the tokens.
   * @param _amount amount to send.
   */
  function withdrawERC20(address _tokenAddress, address _beneficiary, uint _amount) external onlyOwner {
    IERC20(_tokenAddress).transfer(_beneficiary, _amount);
  }

  /**
  * @dev Gets the current fee in BONK.
  * @return BONK fee.
  */
  function getBonkFee() public view returns (uint256) {
    return bonkFee;
  }

  /**
  * @dev Gets the creator of the token.
  * @param _tokenId uint256 ID of the token.
  * @return address of the creator.
  */
  function tokenCreator(uint256 _tokenId) public view returns (address) {
    return tokenCreators[_tokenId];
  }

  /**
   * @dev Internal function for setting the token's creator.
   * @param _tokenId uint256 id of the token.
   * @param _creator address of the creator of the token.
   */
  function _setTokenCreator(uint256 _tokenId, address _creator) internal {
    tokenCreators[_tokenId] = _creator;
  }

  /**
   * @dev Internal function creating a new token.
   * @param _uri string metadata uri associated with the token
   * @param _creator address of the creator of the token.
   */
  function _createToken(string memory _uri, address _creator) internal returns (uint256) {
    uint256 newId = idCounter;
    idCounter++;
    _mint(_creator, newId);
    _setTokenURI(newId, _uri);
    _setTokenCreator(newId, _creator);
    return newId;
  }

  /**
   * @dev Internal function for forwarding collected fees to the fee collector.
   */
  function _forwardBonkTokens() internal {
    uint balance = IERC20(bonkToken).balanceOf(address(this));
    require(IERC20(bonkToken).transfer(feeCollector, balance), "fee transfer failed");
  }
}
function _setTokenCreator(uint256 _tokenId, address _creator) internal {
    tokenCreators[_tokenId] = _creator;
  }
/**
 * @dev Internal function for setting the token's creator.
 * @param _tokenId uint256 id of the token.
 * @param _creator address of the creator of the token.
 */