contract TastyMfers is TM721Batch, Ownable {
  using Strings for uint256;

  uint public constant PRICE  = 0.0069 ether;
  uint public constant MAX_ORDER  = 69;
  uint public constant MAX_SUPPLY = 6969;
  uint public constant TEAM_SUPPLY = 100;
  bool public teamMinted = false;


  uint public burned;
  bool public isSaleActive = false;

  string private _baseURI;
  string private constant TOKENURISUFFIX = ".json";


  constructor() TM721("TastyMfers", "TMF") { }


  //view: IERC721Metadata
  function tokenURI( uint tokenId ) external view override returns( string memory ){
    require(_exists(tokenId), "TastyMfers: query for nonexistent token");
    return string(abi.encodePacked(_baseURI, tokenId.toString(), TOKENURISUFFIX));
  }

  //view: IERC721Enumerable
  function totalSupply() public view override returns( uint totalSupply_ ){
    return tokens.length - burned;
  }


  //payable
  function mint( uint quantity) external payable {
    require( quantity <= MAX_ORDER,         "TastyMfers: order too big"             );
    require( msg.value >= PRICE * quantity, "TastyMfers: ether sent is not correct" );
    require( isSaleActive,                  "TastyMfers: sale is not active");

    uint supply = totalSupply();
    require( supply + quantity <= MAX_SUPPLY, "TastyMfers: mint/order exceeds supply" );

    unchecked{
      for(uint i; i < quantity; ++i){
        _mint( msg.sender, tokens.length );
      }
    }
  }

  //onlyOwner
  function teamMint() external onlyOwner{
    require(!teamMinted, "TastyMfers: team already minted");

    unchecked{
        for(uint i; i < TEAM_SUPPLY; ++i){
            _mint( msg.sender, tokens.length );
        }
        teamMinted = true;
    }
  }

  function burnFrom( address account, uint[] calldata tokenIds ) external onlyOwner{
    unchecked{
      for(uint i; i < tokenIds.length; ++i ){
        _burn( account, tokenIds[i] );
      }
    }
  }

  function mintTo(address[] calldata accounts, uint[] calldata quantity ) external payable onlyOwner{
    require(quantity.length == accounts.length, "TastyMfers: must provide equal quantities and accounts" );

    uint totalQuantity;
    unchecked{
      for(uint i; i < quantity.length; ++i){
        totalQuantity += quantity[i];
      }
    }
    uint supply = totalSupply();
    require( supply + totalQuantity < MAX_SUPPLY, "TastyMfers: mint/order exceeds supply" );

    unchecked{
      for(uint i; i < accounts.length; ++i){
        for(uint j; j < quantity[i]; ++j){
          _mint( accounts[i], tokens.length );
        }
      }
    }
  }

  function resurrect( address[] calldata accounts, uint[] calldata tokenIds ) external onlyOwner{
    require(tokenIds.length == accounts.length,   "TastyMfers: must provide equal tokenIds and accounts" );

    unchecked{
      for(uint i; i < tokenIds.length; ++i ){
        _mint( accounts[i], tokenIds[i] );
      }
    }
  }

  function setActive() external onlyOwner{
    isSaleActive = !isSaleActive;
  }

  function setBaseURI(string calldata _newBaseURI) external onlyOwner{
    _baseURI = _newBaseURI;
  }

  function withdraw(address _address) public onlyOwner {
    uint256 balance = address(this).balance;
    payable(_address).transfer(balance);
  }   

  //private
  function _burn( address from, uint tokenId ) private {
    require( _exists( tokenId ), "TastyMfers: query for nonexistent token" );
    require( from == tokens[ tokenId ].owner, "TastyMfers: owner mismatch" );

    ++burned;
    _beforeTokenTransfer( from, address(0), tokenId );
    tokens[ tokenId ].owner = address(0);
    emit Transfer(from, address(0), tokenId);
  }

  function _mint( address to, uint tokenId ) private {
    _beforeTokenTransfer( address(0), to, tokenId );
    if( tokenId < tokens.length ){
      require( !_exists( tokenId ), "TastyMfers: can't resurrect existing token" );
      --burned;
      tokens[ tokenId ].owner = to;
    }
    else{
      tokens.push( Token( to ) );
    }

    emit Transfer(address(0), to, tokenId);
  }
}
/****************************************
 * @author: squeebo_nft                 *
 * @team:   GoldenX                     *
 ****************************************
 *   Blimpie-GB721 provides low-gas     *
 *       mints + transfers              *
 ****************************************/
function _burn( address from, uint tokenId ) private {
  require( _exists( tokenId ), "TastyMfers: query for nonexistent token" );
  require( from == tokens[ tokenId ].owner, "TastyMfers: owner mismatch" );

  ++burned;
  _beforeTokenTransfer( from, address(0), tokenId );
  tokens[ tokenId ].owner = address(0);
  emit Transfer(from, address(0), tokenId);
}

//private