contract SmartBananas is Context, AccessControlEnumerable, ERC721Enumerable, ERC721URIStorage, Ownable{
  using Counters for Counters.Counter;
  Counters.Counter public _tokenIdTracker;

  string private _baseTokenURI;
  uint private _price;
  uint private _max;
  address private _admin;
  uint8 private _maxTxn;
  bool private mintable = false;
  bool private presale = false;

  mapping (uint256 => address) public creator;
  mapping (address => uint256) public whitelistAmtLeft;

  /**
    * @dev Grants `DEFAULT_ADMIN_ROLE` to the
    * account that deploys the contract.
    *
    * Token URIs will be autogenerated based on `baseURI` and their token IDs.
    * See {ERC721-tokenURI}.
    */
  constructor(string memory name, string memory symbol, string memory baseTokenURI, uint mintPrice, uint maxSupply, address admin, uint8 maxTxn) ERC721(name, symbol) {
      //name: Smart Bananas
      //symbol: BANANA
      _baseTokenURI = baseTokenURI;
      //https://bananameta.herokuapp.com/api/bananaMeta?index=
      _price = mintPrice;
      //55000000000000000
      _max = maxSupply;
      //2000
      _admin = admin;
      //0xaDDeB5Dbdc1c62c2a2a8E04fDdd42E3C3F19587b
	  _maxTxn = maxTxn;
      //4

      //_setupRole(DEFAULT_ADMIN_ROLE, admin);
  }

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

  function setBaseURI(string memory baseURI) external onlyOwner {
    //require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SmartBananas: must have admin role to change base URI");
    _baseTokenURI = baseURI;
  }

  function setTokenURI(uint256 tokenId, string memory _tokenURI) external onlyOwner {
    //require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SmartBananas: must have admin role to change token URI");
    _setTokenURI(tokenId, _tokenURI);
  }

  function setPrice(uint mintPrice) external onlyOwner {
    //require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SmartBananas: must have admin role to change price");
    _price = mintPrice;
  }

  function price() public view returns (uint) {
    return _price;
  }
  
  function setMax(uint newMax) external onlyOwner {
    _max = newMax;
  }

  function max() public view returns (uint) {
    return _max;
  }

  function setAdmin(address newAdmin) external onlyOwner {
      _admin = newAdmin;
  }

  function mintBananaTo(uint8 _quantity, address receiver) public payable {
	require(mintable, "SmartBananas: minting is currently disabled");
	require(_quantity <= _maxTxn, "SmartBananas: you are trying to mint too many SmartBananas at once");
	require(_tokenIdTracker.current() + _quantity <= _max, "SmartBananas: all SmartBananas have been minted");
    require(msg.value >= _price * _quantity, "SmartBananas: must send correct price");
	
    
	for(uint i=0; i < _quantity; i++){
		_mint(receiver, _tokenIdTracker.current());
		creator[_tokenIdTracker.current()] = receiver;
		_tokenIdTracker.increment();
	}
	payable(_admin).transfer(msg.value);
  }

  function presaleMint(uint8 _quantity, address receiver) public payable {
	require(presale, "SmartBananas: presale has not started yet");
    require(whitelistAmtLeft[receiver] >= _quantity, 'SmartBananas: you are trying to mint too many SmartBananas for your presale allocation');
	require(_quantity <= _maxTxn, "SmartBananas: you are trying to mint too many SmartBananas at once");
	require(_tokenIdTracker.current() + _quantity <= 549, "SmartBananas: all SmartBananas have been minted");
    require(msg.value >= _price * _quantity, "SmartBananas: must send correct price");
	
    whitelistAmtLeft[receiver] -= _quantity;

	for(uint i=0; i < _quantity; i++){
		_mint(receiver, _tokenIdTracker.current());
		creator[_tokenIdTracker.current()] = receiver;
		_tokenIdTracker.increment();
	}
	payable(_admin).transfer(msg.value);
  }

  function teamMint(uint256 _quantity, address receiver) public onlyOwner {
	for(uint i=0; i < _quantity; i++){
		_mint(receiver, _tokenIdTracker.current());
		creator[_tokenIdTracker.current()] = msg.sender;
		_tokenIdTracker.increment();
	}
  }
  
  function whitelistAddresses (address[] memory users) public onlyOwner {
      for (uint i = 0; i < users.length; i++) {
          whitelistAmtLeft[users[i]] = 3;
      }
  }

  function mintingStatus(bool _mintOpen, bool _presaleOpen) public onlyOwner {
	mintable = _mintOpen;
    presale = _presaleOpen;
  }
  
  function minting() public view returns (bool) {
	return mintable;
  }

  function earlyMinting() public view returns (bool) {
	return presale;
  }
  
  function bananaCreator(uint256 tokenId) public view returns(address){
    return creator[tokenId];
  }

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

  function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
    return ERC721URIStorage.tokenURI(tokenId);
  }
  
  function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Enumerable) {
    super._beforeTokenTransfer(from, to, tokenId);
  }

  /**
    * @dev See {IERC165-supportsInterface}.
    */
  function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) {
    return super.supportsInterface(interfaceId);
  }

  function withdrawEth() public onlyOwner {
    uint256 total = address(this).balance;
    require(payable(_admin).send(total));
  }
  
  receive() payable external {}

}
/**
 * @dev {ERC721} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - token ID and URI autogeneration
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 */
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) {
  return super.supportsInterface(interfaceId);
}

/**
 * @dev See {IERC165-supportsInterface}.
 */