contract Dickheads is ERC721, Ownable {
    event Mint(address indexed from, uint256 indexed tokenId);

    modifier mintingStarted() {
        require(mintingEnabled, "You are too early");
        _;
    }

    modifier callerNotAContract() {
        require(
            tx.origin == msg.sender,
            "The caller can only be a user and not a contract"
        );
        _;
    }

    uint16 private totalTokens = 6969;
    uint256 private maxTokensPerWallet = 150;
    uint256 private maxTokensPerTransaction = 10;
    bool private mintingEnabled = false;
    // Price per NFT: 0.03 ETH
    uint256 private tokenPrice = 30000000000000000;
    uint256 private totalMintedTokens = 0;
    string private baseURI;
    mapping(address => uint256) private claimedTokensPerWallet;
    uint256 public nextTokenToMint;

    constructor(string memory _initialBaseURI) ERC721("Dickheads", "PEN15") {
        baseURI = _initialBaseURI;
        nextTokenToMint = 1;
    }

    // ONLY OWNER

    /**
     * @dev Allows to withdraw the Ether in the contract to the address of the owner.
     */
    function withdraw() external onlyOwner {
        uint256 totalBalance = address(this).balance;
        payable(msg.sender).transfer(totalBalance);
    }

    /**
     * @dev Sets the base URI for the API that provides the NFT data.
     */
    function setBaseTokenURI(string memory _uri) external onlyOwner {
        baseURI = _uri;
    }

    /**
     * @dev Sets mintingEnabled
     */
    function setMintingEnabled(bool _mintingEnabled) external onlyOwner {
        mintingEnabled = _mintingEnabled;
    }

    /**
     * @dev Sets maxTokensPerTransaction
     */
    function setMaxTokensPerTransaction(uint256 _maxTokensPerTransaction)
        external
        onlyOwner
    {
        maxTokensPerTransaction = _maxTokensPerTransaction;
    }

    /**
     * @dev Sets the mint price for each token
     */
    function setTokenPrice(uint256 _tokenPrice) external onlyOwner {
        tokenPrice = _tokenPrice;
    }

    // END ONLY OWNER FUNCTIONS

    /**
     * @dev Claim bulk tokens at once
     */
    function mintTokens(uint256 amount)
        external
        payable
        callerNotAContract
        mintingStarted
    {
        require(
            msg.value >= tokenPrice * amount,
            "Not enough Ether to claim the tokens"
        );

        require(amount <= maxTokensPerTransaction, "Unable to mint this many");

        require(
            claimedTokensPerWallet[msg.sender] + amount <= maxTokensPerWallet,
            "You cannot claim more tokens"
        );

        require(
            totalTokens - (nextTokenToMint - 1) > amount,
            "No tokens left to be claimed"
        );

        uint256[] memory tokenIds = new uint256[](amount);

        claimedTokensPerWallet[msg.sender] += amount;
        totalMintedTokens += amount;

        for (uint256 i; i < amount; i++) {
            tokenIds[i] = getTokenToBeClaimed();
        }

        _batchMint(msg.sender, tokenIds);
    }

    function _batchMint(address to, uint256[] memory tokenIds)
        internal
        virtual
    {
        require(to != address(0), "ERC721: mint to the zero address");
        for (uint256 i; i < tokenIds.length; i++) {
            require(!_exists(tokenIds[i]), "ERC721: token already minted");
            _safeMint(to, tokenIds[i]);
        }
    }

    /**
     * @dev Returns the tokenId by index
     */
    function tokenByIndex(uint256 tokenId) external view returns (uint256) {
        require(
            _exists(tokenId),
            "ERC721: operator query for nonexistent token"
        );

        return tokenId;
    }

    /**
     * @dev Returns the base URI for the tokens API.
     */
    function baseTokenURI() external view returns (string memory) {
        return baseURI;
    }

    /**
     * @dev Returns how many tokens are still available to be claimed
     */
    function getAvailableTokens() external view returns (uint256) {
        return totalTokens - (nextTokenToMint - 1);
    }

    /**
     * @dev Returns the claim price
     */
    function getTokenPrice() external view returns (uint256) {
        return tokenPrice;
    }

    /**
     * @dev Returns the minting start date
     */
    function getMintingEnabled() external view returns (bool) {
        return mintingEnabled;
    }

    /**
     * @dev Returns the total supply
     */
    function totalSupply() external view virtual returns (uint256) {
        return totalMintedTokens;
    }

    // Private and Internal functions

    /**
     * @dev Returns a random available token to be claimed
     */
    function getTokenToBeClaimed() private returns (uint256) {
        uint256 tokenId = nextTokenToMint;
        nextTokenToMint++;
        return tokenId;
    }

    /**
     * @dev See {ERC721}.
     */
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }
}
function setMintingEnabled(bool _mintingEnabled) external onlyOwner {
    mintingEnabled = _mintingEnabled;
}

/**
 * @dev Sets mintingEnabled
 */