contract _8BitMFers is ERC721("8Bit-mfers", "bmfers") {

    /* -------------------------------------------------------------------------- */
    /*                                  CONSTANTS                                 */
    /* -------------------------------------------------------------------------- */
    address public owner = msg.sender;

    uint256 constant MINT_FEE = 0.0099 ether; 
    
    uint256 constant MAX_GIVEAWAY_SUPPLY = 500;

    uint256 constant maxMintPerWallet = 25;

    uint256 constant MAX_SUPPLY = 6969;

    address constant MF_TEAM = address(0xA0c8041d9e225cba96089778C0cA3bf9fb7AFb7D); // change this to your address

    string constant unrevealedURI = "https://ipfs.io/ipfs/QmTYzJLzsaCqpceocDAuU2vsmZxRgNJSbvhrEZ8PcMLDAF";

    /* -------------------------------------------------------------------------- */
    /*                                MUTABLE STATE                               */
    /* -------------------------------------------------------------------------- */

    uint256 public giveawaySupply;

    uint256 public totalSupply;

    uint256 public revealedSupply;

    string public revealedURI;

    /* -------------------------------------------------------------------------- */
    /*                               MF_TEAM_METHODS                              */
    /* -------------------------------------------------------------------------- */

    function giveaway(address account, uint256 amount) external {
        // make sure no more than max supply can be minted
        require(totalSupply + amount <= MAX_SUPPLY);
        // make sure only MF team can call this method
        require(msg.sender == MF_TEAM);
        // make sure max giveaway supply is satisfied
        require(giveawaySupply + amount < MAX_GIVEAWAY_SUPPLY);

        for (uint i; i < amount; i++) {
            // increase totalSupply by 1
            totalSupply++;
            // increase giveawaySupply by 1
            giveawaySupply++;
            // mint user 1 nft
            _mint(account, totalSupply);
        }
    }

    function withdraw(address account, uint256 amount) external {
        // make sure only MF team can call this method
        require(msg.sender == MF_TEAM);
        
        // transfer amount to account
        payable(account).transfer(amount);
    }

    function reveal(string memory updatedURI, uint256 _revealedSupply) external {
        // make sure only MF team can call this method
        require(msg.sender == MF_TEAM);

        require(revealedSupply <= MAX_SUPPLY);

        revealedSupply = _revealedSupply;

        revealedURI = updatedURI;
    }
    /* -------------------------------------------------------------------------- */
    /*                               PUBLIC METHODS                               */
    /* -------------------------------------------------------------------------- */


    function mint(address account) external payable {
        //tokenbalance check
        require(balanceOf[account] + 1 <= maxMintPerWallet);
        // if supply is less than or equal to 500 allow user to mint free
        if (totalSupply > 500) require(msg.value >= MINT_FEE);


        require(totalSupply + 1 <= MAX_SUPPLY);
        totalSupply++;

        _mint(account, totalSupply);

    }

    function batchMint(address account, uint256 amount) external payable {
        //tokenbalance check
        require(balanceOf[account] + 1 <= maxMintPerWallet);

        require(totalSupply + amount <= MAX_SUPPLY);

  
        if (totalSupply > 500) require(msg.value >= MINT_FEE * amount);

        for (uint i; i < amount; i++) {

            totalSupply++;

            _mint(account, totalSupply);

        }
    }

    function tokenURI(uint256 tokenId) public override view returns (string memory) {

        if (tokenId > revealedSupply) return unrevealedURI;

        return string(abi.encodePacked(revealedURI, "/", _toString(tokenId), ".json"));
    }


    /* -------------------------------------------------------------------------- */
    /*                              INTERNAL METHODS                              */
    /* -------------------------------------------------------------------------- */

    function _toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

}
function giveaway(address account, uint256 amount) external {
        // make sure no more than max supply can be minted
        require(totalSupply + amount <= MAX_SUPPLY);
        // make sure only MF team can call this method
        require(msg.sender == MF_TEAM);
        // make sure max giveaway supply is satisfied
        require(giveawaySupply + amount < MAX_GIVEAWAY_SUPPLY);

        for (uint i; i < amount; i++) {
            // increase totalSupply by 1
            totalSupply++;
            // increase giveawaySupply by 1
            giveawaySupply++;
            // mint user 1 nft
            _mint(account, totalSupply);
        }
    }
/* -------------------------------------------------------------------------- */