contract Box is ERC721A, IERC2981, Ownable, ReentrancyGuard {
    using Counters for Counters.Counter;
    using Strings for uint256;

    Counters.Counter private tokenCounter;

    string private baseURI = "ipfs://QmdxnqFfvtJ8wARLMFj1xP5FkPrTaazBtYbR4momMjVC7S";

    uint256 public constant MAX_MINTS_PER_TX = 5;
    uint256 public maxSupply = 555;

    uint256 public constant PUBLIC_SALE_PRICE = 0.08 ether;
    bool public isPublicSaleActive = false;




    // ============ ACCESS CONTROL/SANITY MODIFIERS ============

    modifier publicSaleActive() {
        require(isPublicSaleActive, "Public sale is not open");
        _;
    }



    modifier maxMintsPerTX(uint256 numberOfTokens) {
        require(
            numberOfTokens <= MAX_MINTS_PER_TX,
            "Max mints per transaction exceeded"
        );
        _;
    }

    modifier canMintNFTs(uint256 numberOfTokens) {
        require(
            totalSupply() + numberOfTokens <=
                maxSupply,
            "Not enough mints remaining to mint"
        );
        _;
    }

    constructor(
    ) ERC721A("Mystery Box For Gamblers Only", "Box", 100, maxSupply) {
    }

modifier isCorrectPayment(uint256 price, uint256 numberOfTokens) {
        require(
            (price * numberOfTokens) == msg.value,
            "Incorrect ETH value sent"
        );
        _;
    }

    // ============ PUBLIC FUNCTIONS FOR MINTING ============

    function mint(uint256 numberOfTokens)
        external
        payable
        nonReentrant
        publicSaleActive
        isCorrectPayment(PUBLIC_SALE_PRICE, numberOfTokens)
        canMintNFTs(numberOfTokens)
        maxMintsPerTX(numberOfTokens)
    {

        _safeMint(msg.sender, numberOfTokens);
    }




    // ============ PUBLIC READ-ONLY FUNCTIONS ============

    function getBaseURI() external view returns (string memory) {
        return baseURI;
    }

    // ============ OWNER-ONLY ADMIN FUNCTIONS ============

    function setBaseURI(string memory _baseURI) external onlyOwner {
        baseURI = _baseURI;
    }


    function setIsPublicSaleActive(bool _isPublicSaleActive)
        external
        onlyOwner
    {
        isPublicSaleActive = _isPublicSaleActive;
    }



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

    function withdrawTokens(IERC20 token) public onlyOwner {
        uint256 balance = token.balanceOf(address(this));
        token.transfer(msg.sender, balance);
    }



    // ============ SUPPORTING FUNCTIONS ============

    function nextTokenId() private returns (uint256) {
        tokenCounter.increment();
        return tokenCounter.current();
    }

    // ============ FUNCTION OVERRIDES ============

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721A, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC2981).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(_exists(tokenId), "Nonexistent token");

        return
            string(abi.encodePacked(baseURI, "/", (tokenId).toString(), ".json"));
    }

    /**
     * @dev See {IERC165-royaltyInfo}.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        require(_exists(tokenId), "Nonexistent token");

        return (address(this), SafeMath.div(SafeMath.mul(salePrice, 5), 100));
    }
}
function mint(uint256 numberOfTokens)
        external
        payable
        nonReentrant
        publicSaleActive
        isCorrectPayment(PUBLIC_SALE_PRICE, numberOfTokens)
        canMintNFTs(numberOfTokens)
        maxMintsPerTX(numberOfTokens)
    {

        _safeMint(msg.sender, numberOfTokens);
    }
// ============ PUBLIC FUNCTIONS FOR MINTING ============