contract MB1155 is ERC1155Burnable, Ownable {
    // whether minting by owner is allowed
    bool public manualMintAllowed = true;
    // individual uri per type
    mapping (uint256 => string) public typeToUri;
    // whether main uri is freezed
    bool public isUriFreezed;
    // whether each individual uri is freezed
    mapping (uint256 => bool) public typeIsUriFreezed;
    // total supply tracker per type
    mapping (uint256 => uint256) public totalSupply;
    // supply limit per type Id
    mapping(uint256 => uint256) public typeSupplyLimit;
    // price per type Id
    mapping(uint256 => uint256) public typePrice;
    // limit per tx per type Id
    mapping(uint256 => uint256) public typeLimitPerTx;
    // addresses auto-approved by owner
    mapping (address => bool) public isAutoApproved;

    // triggered when mint settings are set for a specific type
    event SetMintSettings(uint256 indexed typeId);
    // triggered when an address is set for auto-approve
    event SetAutoApproved(address indexed addr);

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory _uri)
        ERC1155(_uri)
    {}
    
    /**
     * @dev Lock further manual minting by owner
     */
    function disableManualMint() public onlyOwner {
        manualMintAllowed = false;
    }

    /**
     * @dev Set auto-approved address
     */
    function setAutoApproved(address addr, bool value) public onlyOwner {
        isAutoApproved[addr] = value;
        emit SetAutoApproved(addr);
    }
            
    /**
     * @dev Airdrop tokens to owners
     */
    function mintOwner(address[] calldata owners, uint256[] calldata types, uint256[] calldata counts) public onlyOwner {
      require(manualMintAllowed, "Not allowed");
      require(owners.length == types.length && types.length == counts.length, "Bad array lengths");
         
      for (uint256 i = 0; i < owners.length; i++) {
        _mint(owners[i], types[i], counts[i], "");
      }
    }

    /**
     * @dev Airdrop single tokens to owners
     */
    function mintOwnerOneToken(address[] calldata owners, uint256 typeId) public onlyOwner {
      require(manualMintAllowed, "Not allowed");
         
      for (uint256 i = 0; i < owners.length; i++) {
        _mint(owners[i], typeId, 1, "");
      }
    }

    /**
     * @dev Set mint settings for type
     */
    function setMintSettings(uint256 typeId, uint256 supplyLimit, uint256 price, uint256 limitPerTx) public onlyOwner {
        typeSupplyLimit[typeId] = supplyLimit;
        typePrice[typeId] = price;
        typeLimitPerTx[typeId] = limitPerTx;

        emit SetMintSettings(typeId);
    }

    /**
     * @dev Set mint settings for multiple types
     */
    function setMintSettingsArray(uint256[] calldata typeIds, uint256[] calldata supplyLimits, uint256[] calldata prices, uint256[] calldata limitsPerTx) public onlyOwner {
        for (uint256 i=0; i<typeIds.length; i++) {
            typeSupplyLimit[typeIds[i]] = supplyLimits[i];
            typePrice[typeIds[i]] = prices[i];
            typeLimitPerTx[typeIds[i]] = limitsPerTx[i];

            emit SetMintSettings(typeIds[i]);
        }
    }

    /**
     * @dev Public mint
     */
    function mintPublic(uint256 typeId, uint256 amount) public payable {
        require(msg.value == amount*typePrice[typeId], "Wrong ETH value");
        require(amount <= typeLimitPerTx[typeId], "Per tx limit exceeded");
        require(amount > 0, "Can't mint 0");
        require(totalSupply[typeId] + amount <= typeSupplyLimit[typeId], "Supply exceeded");

        totalSupply[typeId] += amount;
        _mint(msg.sender, typeId, amount, "");
    }

    /**
     * @dev Type uri
     */
    function uri(uint256 typeId) public view override returns (string memory) {
        string memory typeUri = typeToUri[typeId];
        if (bytes(typeUri).length == 0) {
            return super.uri(typeId);
        } else {
            return typeUri;
        }
    }
   
    /**
     * @dev Updates the metadata URI
     */
    function updateUri(string calldata newUri) public onlyOwner {
        require(!isUriFreezed, "Freezed");
        _setURI(newUri);
    }

    /**
     * @dev Freezes the metadata URI
     */
    function freezeUri() public onlyOwner {
        isUriFreezed = true;
    }

    /**
     * @dev Updates and freezes the metadata URI
     */
    function permanentSetUri(string calldata newUri) public onlyOwner {
        updateUri(newUri);
        freezeUri();
    }

    /**
     * @dev Updates the metadata URI for a specific type
     */
    function updateUriForType(string calldata newUri, uint256 typeId) public onlyOwner {
        require(!typeIsUriFreezed[typeId], "Freezed");
        typeToUri[typeId] = newUri;
    }

    /**
     * @dev Freezes the metadata URI
     */
    function freezeUriForType(uint256 typeId) public onlyOwner {
        typeIsUriFreezed[typeId] = true;
    }

    /**
     * @dev Updates and freezes the metadata URI
     */
    function permanentSetUriForType(string calldata newUri, uint256 typeId) public onlyOwner {
        updateUriForType(newUri, typeId);
        freezeUriForType(typeId);
    }

    /**
     * @dev isApprovedForAll override
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return isAutoApproved[operator] || super.isApprovedForAll(account, operator);
    }

    /**
     * @dev Withdraw ether from this contract (Callable by owner)
     */
    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
}
function setMintSettings(uint256 typeId, uint256 supplyLimit, uint256 price, uint256 limitPerTx) public onlyOwner {
    typeSupplyLimit[typeId] = supplyLimit;
    typePrice[typeId] = price;
    typeLimitPerTx[typeId] = limitPerTx;

    emit SetMintSettings(typeId);
}

/**
 * @dev Set mint settings for type
 */