contract MoonChadNFT is ERC721Enumerable, ERC721Burnable, Ownable {
    string public provenanceHash = "";
    string private baseTokenURI = "";
    uint256 public price = 30000000000000000;
    uint256 public supplyCap = 5555;
    uint256 public perTxLimit = 20;
    uint256 public reservedMints = 50;
    uint256 public tokenCounter = 0;
    uint256[] public firedChads;
    bool public salePaused = true;
    bool public burnPaused = true;

    constructor(string memory _baseTokenURI)
        payable
        ERC721("MoonChad NFT", "MCHAD")
    {
        baseTokenURI = _baseTokenURI;
    }

    function mint(address _sender, uint256 _amountMinted) public payable {
        if (_sender != owner()) {
            require(!salePaused, "ERC721: sale is paused.");
        }
        require(totalSupply() < supplyCap, "ERC721: sale is finished.");
        require(
            _amountMinted > 0 && _amountMinted <= perTxLimit,
            "ERC721: you may only mint up to 20 chads"
        );
        require(
            totalSupply() + _amountMinted <= supplyCap,
            "ERC721: transaction would exceed supply cap"
        );
        require(
            msg.value >= getTokenPrice(true) * _amountMinted,
            "ERC721: incorrect value sent"
        );

        for (uint256 i = 0; i < _amountMinted; i++) {
            _safeMint(_sender, tokenCounter);
            tokenCounter++;
        }
    }

    function regenerate(address _sender, uint256[] memory tokenIds) public {
        require(!burnPaused, "ERC721Burnable: burning is currently paused.");
        require(tokenIds.length > 1, "ERC721Burnable: you must burn at least 2 Chads.");
        require(tokenIds.length < 20, "ERC721Burnable: you may only burn 20 Chads at a time.");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            require(
                _isApprovedOrOwner(_sender, tokenIds[i]),
                "ERC721Burnable: caller is not owner nor approved."
            );
        }

        firedChads.push(tokenIds[0]);
        _burn(tokenIds[0]);

        for (uint256 i = 1; i < tokenIds.length; i++) {
            _burn(tokenIds[i]);
            _safeMint(_sender, tokenCounter);
            tokenCounter++;
        }
    }

    // ----------- Setters:

    function setProvenanceHash(string memory _provenanceHash) public onlyOwner() {
        provenanceHash = _provenanceHash;
    }

    function setBaseURI(string memory baseURI) public onlyOwner() {
        baseTokenURI = baseURI;
    }

    function setBurnPaused(bool _burnPaused) public onlyOwner() {
        burnPaused = _burnPaused;
    }

    function setSalePaused(bool _salePaused) public onlyOwner() {
        salePaused = _salePaused;
    }

    function withdrawAll() public onlyOwner() {
        require(address(this).balance > 0, "Balance must be greater than 0");
        require(payable(msg.sender).send(address(this).balance));
    }

    // ----------- Getters:
    function getTokenPrice(bool showFree) public view returns (uint256) {
        if (!showFree) {
            return price;
        }
        if (totalSupply() <= reservedMints) {
            return 0;
        }
        return price;
    }

    function getHoldersTokens(address _holder)
        public
        view
        returns (uint256[] memory)
    {
        uint256 tokenCount = balanceOf(_holder);

        uint256[] memory tokensId = new uint256[](tokenCount);

        for (uint256 i; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(_holder, i);
        }

        return tokensId;
    }

    // ----------- Overrides to satisfy openzeppelin ERC721 dependencies:
    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
////import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
////import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
////import "@openzeppelin/contracts/access/Ownable.sol";
function _baseURI() internal view virtual override returns (string memory) {
    return baseTokenURI;
}

// ----------- Overrides to satisfy openzeppelin ERC721 dependencies: