contract SupRecords is ERC1155(""), ReentrancyGuard, Pausable, Ownable {
    mapping(address => bool) private _minters;

    mapping(uint256 => uint256) private _totalSupplies;
    mapping(uint256 => uint256) private _maxSupplies;
    mapping(uint256 => uint256) private _prices;
    mapping(uint256 => string) private _hashes;

    uint256 _totalTokens;

    string constant BASE_URI = "https://ipfs.io/ipfs/";
    uint256 constant public MAX_MINT_COUNT = 10;

    address payable private payoutAddress;

    string public name;
    string public symbol;

    event AllocatedTokens(uint256 tokenId, uint256 price, uint256 count);
    event SetMintable(address minter, bool mintable);

    modifier minterOrOwner() {
        // Caller must either be a minter or the owner.
        if (!_minters[_msgSender()]) {
            require(owner() == _msgSender(), "Not minter or owner");
        }

        _;
    }

    constructor() {
        payoutAddress = payable(_msgSender());
        name = "Sup? Records";
        symbol = "SUP?";
    }

    // For OpenSea
    function uri(uint256 tokenId) public view override returns (string memory) {
        string memory ipfsHash = _hashes[tokenId];

        return bytes(ipfsHash).length > 0 ? string(abi.encodePacked(BASE_URI, ipfsHash)) : "";
    }
    function totalSupply(uint256 tokenId) external view returns (uint256) {
        return _totalSupplies[tokenId];
    }
    function maxSupply(uint256 tokenId) external view returns (uint256) {
        return _maxSupplies[tokenId];
    }

    // Views
    function getPrice(uint256 tokenId) external view returns (uint256) {
        require(_maxSupplies[tokenId] > 1, "Invalid token id");

        return _prices[tokenId];
    }
    function getTotalTokens() external view returns (uint256) {
        return _totalTokens;
    }

    /** Mint a non-unique NFT. */
    function mint(uint256 tokenId, uint256 count) external payable nonReentrant whenNotPaused {
        require(count > 0 && count <= MAX_MINT_COUNT, "Invalid mint count");
        uint256 maxTokenSupply = _maxSupplies[tokenId];
        require(maxTokenSupply > 1 && _totalSupplies[tokenId] + count <= maxTokenSupply, "No supply");
        require(msg.value == _prices[tokenId] * count, "Wrong value");

        _totalSupplies[tokenId] += count;

        _mint(_msgSender(), tokenId, count, "");
    }

    /** Sets the allocation for a non-unique NFT. */
    function createNonUnique(uint256 price, uint256 count, string memory ipfsHash) external nonReentrant minterOrOwner {
        uint256 tokenId = ++_totalTokens;
        require(_maxSupplies[tokenId] == 0, "Already created.");
        require(price > 0, "Must have price");
        require(count > 1, "Must be non-unique");
        require(bytes(ipfsHash).length > 0, "Need ipfs hash");

        _maxSupplies[tokenId] = count;
        _prices[tokenId] = price;
        _hashes[tokenId] = ipfsHash;

        emit AllocatedTokens(tokenId, price, count);
    }

    /** Mints a unique, one of one, NFT. */
    function mintUnique(string memory ipfsHash) external nonReentrant minterOrOwner {
        uint256 tokenId = ++_totalTokens;
        require(_maxSupplies[tokenId] == 0, "Already minted");
        require(bytes(ipfsHash).length > 0, "Need ipfs hash");

        _totalSupplies[tokenId] = 1;
        _maxSupplies[tokenId] = 1;
        _hashes[tokenId] = ipfsHash;

        _mint(_msgSender(), tokenId, 1, "");
    }


    // Minter
    function isMinter(address minter) external view returns (bool) {
        return _minters[minter];
    }
    /** Set the minter role for a given set of addresses. */
    function setMintable(address[] calldata minters, bool[] calldata mintables) external nonReentrant onlyOwner {
        uint256 count = minters.length;
        require(count > 0, "No minters");
        require(count == mintables.length, "Nonmatching length");

        address minter;
        bool mintable;
        for (uint256 i; i < count; i++) {
            minter = minters[i];
            mintable = mintables[i];
            _minters[minter] = mintable;

            emit SetMintable(minter, mintable);
        }
    }

    // Owner
    function withdraw() public {
        payoutAddress.transfer(address(this).balance);
    }
    function renounceOwnership() public override nonReentrant {
        revert();
    }
    function setPayoutAddress(address payable newAddress) external nonReentrant onlyOwner {
        require(newAddress != address(0) && newAddress != address(this), "Invalid address");

        payoutAddress = newAddress;
    }
    function pause() external nonReentrant onlyOwner {
        _pause();
    }
    function unpause() external nonReentrant onlyOwner {
        _unpause();
    }
}
function mintUnique(string memory ipfsHash) external nonReentrant minterOrOwner {
    uint256 tokenId = ++_totalTokens;
    require(_maxSupplies[tokenId] == 0, "Already minted");
    require(bytes(ipfsHash).length > 0, "Need ipfs hash");

    _totalSupplies[tokenId] = 1;
    _maxSupplies[tokenId] = 1;
    _hashes[tokenId] = ipfsHash;

    _mint(_msgSender(), tokenId, 1, "");
}

/** Mints a unique, one of one, NFT. */