// Contract: DigitalArtists
// Target function: function mintTo(
Uri;
    }

    /**
     * @notice Returns boolean flag indicating whether collection's base URI is locked
     * @param collectionId_ collection ID
     */
    function isCollectionBaseUriLocked(uint256 collectionId_)
        public
        view
        virtual
        returns (bool)
    {
        return _collections[collectionId_].baseUriLocked > 0;
    }

    /**
     * @notice Allows collection authorized or contract owner to update max supply and premint max supply
     * @param collectionId_ collection ID
     * @param shouldUpdateMaxSupply_ flag indicating whether max supply value should be updated
     * @param maxSupply_ new max supply
     * @param shouldUpdatePremintMaxSupply_ flag indicating whether premint max supply value should be updated
     * @param premintMaxSupply_ new premint max supply
     */
    function updateCollectionSupplies(
        uint256 collectionId_,
        uint8 shouldUpdateMaxSupply_,
        uint32 maxSupply_,
        uint8 shouldUpdatePremintMaxSupply_,
        uint32 premintMaxSupply_
    ) public onlyAuthorized(collectionId_) {
        if (shouldUpdateMaxSupply_ != 0) {
            unchecked {
                require(
                    totalSupplyByCollectionId(collectionId_) <= maxSupply_ &&
                        _collections[collectionId_].maxSupply + 10000 >
                        maxSupply_,
                    "Invalid max supply"
                );
            }

            _collections[collectionId_].maxSupply = maxSupply_;
        }

        if (shouldUpdatePremintMaxSupply_ != 0) {
            unchecked {
                require(
                    totalSupplyByCollectionId(collectionId_) <=
                        premintMaxSupply_ &&
                        _collections[collectionId_].maxSupply + 10000 >
                        premintMaxSupply_,
                    "Invalid premint max supply"
                );
            }
            _collections[collectionId_].premintMaxSupply = premintMaxSupply_;
        }
    }

    /**
     * @notice Returns collection supplies - max supply, premint max supply and current total supply
     * @param collectionId_ collection ID
     */
    function collectionSupplies(uint256 collectionId_)
        public
        view
        returns (
            uint32,
            uint32,
            uint256
        )
    {
        return (
            _collections[collectionId_].maxSupply,
            _collections[collectionId_].premintMaxSupply,
            totalSupplyByCollectionId(collectionId_)
        );
    }

    /**
     * @notice Allows collection authorized or contract owner to change state
     * @param collectionId_ collection ID
     * @param state_ state - 1(premint), 2(public mint), everything else is considered not active
     */
    function updateCollectionState(uint256 collectionId_, uint8 state_)
        public
        onlyAuthorized(collectionId_)
    {
        uint8 oldState = _collections[collectionId_].state;
        _collections[collectionId_].state = state_;
        emit CollectionStateUpdated(collectionId_, state_, oldState);
    }

    /**
     * @notice Returns collection state - 1(premint), 2(public mint), everything else is considered not active
     * @param collectionId_ collection ID
     */
    function collectionState(uint256 collectionId_)
        public
        view
        returns (uint8)
    {
        return _collections[collectionId_].state;
    }

    /**
     * @notice Returns how many NFTs give address minted on premint and public mint for specific collection
     * @param collectionId_ collection ID
     * @param addr_ checked address
     */
    function collectionAddressMintedStats(uint256 collectionId_, address addr_)
        public
        view
        returns (uint32, uint32)
    {
        return (
            _collections[collectionId_].minted[addr_],
            _collections[collectionId_].preminted[addr_]
        );
    }

    /**
     * @notice Returns generated balance from mint campaign of collection
     * @param collectionId_ collection ID
     */
    function collectionBalance(uint256 collectionId_)
        public
        view
        returns (uint256)
    {
        return _collections[collectionId_].balance;
    }

    /**
     * @notice Returns boolean flag indicating if address is collection authorized
     * @param collectionId_ collection ID
     * @param addr_ checked address
     */
    function isCollectionAuthorized(uint256 collectionId_, address addr_)
        public
        view
        returns (bool)
    {
        return _collections[collectionId_].authorized[addr_];
    }

    /**
     * @notice Returns collection bounds - starting token id, last token id, current token id
     * @param collectionId_ collection ID
     */
    function collectionBounds(uint256 collectionId_)
        public
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        unchecked {
            return (
                _collections[collectionId_].tokenIdFrom + 1,
                _collections[collectionId_].tokenIdFrom +
                    _collections[collectionId_].maxSupply,
                _collections[collectionId_].tokenId
            );
        }
    }

    /**
     * @notice Returns info about collection - collection name, artist name, base URI, artist address, max supply
     * @param collectionId_ collection ID
     */
    function collectionInfo(uint256 collectionId_)
        public
        view
        returns (
            string memory,
            string memory,
            string memory,
            address,
            uint32
        )
    {
        return (
            string(abi.encodePacked(_collections[collectionId_].name)),
            string(
                abi.encodePacked(
                    _artists[_collections[collectionId_].artistAddr].name
                )
            ),
            string(abi.encodePacked(_collections[collectionId_].baseUri)),
            _collections[collectionId_].artistAddr,
            _collections[collectionId_].maxSupply
        );
    }

    /**
     * @notice Allows collection authorized or contract owner to retrive split payments information
     * @param collectionId_ collection ID
     */
    function collectionPaymentsInfo(uint256 collectionId_)
        public
        view
        onlyAuthorized(collectionId_)
        returns (
            uint256,
            uint256,
            uint16,
            uint16
        )
    {
        return (
            _collections[collectionId_].withdrawn[0],
            _collections[collectionId_].withdrawn[1],
            _collections[collectionId_].percentage[0],
            _collections[collectionId_].percentage[1]
        );
    }

    /**
     * @notice Allows contract owner or known artists to create new collectins on this contract
     * @param artistAddr_ collection artists address (required)
     * @param maxSupply_ collection max supply (required)
     * @param premintMaxSupply_ collection premint max supply (optional)
     */
    function createCollection(
        address artistAddr_,
        uint32 maxSupply_,
        uint32 premintMaxSupply_
    ) public {
        _nonZeroAddress(artistAddr_);
        require(maxSupply_ > 0 && maxSupply_ <= 5000000, "Invalid supply");

        if (_isCreateCollectionPublic == 0) {
            // Leave an option to publicly allow this operation
            require(
                _msgSender() == owner() || _knownArtists[_msgSender()],
                "Unauthorized"
            );
        }

        Collection storage c = _collections[++_lastCollectionId];
        c.artistAddr = artistAddr_;
        c.maxSupply = maxSupply_;

        if (premintMaxSupply_ > 0) {
            _collections[_lastCollectionId]
                .premintMaxSupply = premintMaxSupply_;
        }

        uint256 prevIdx;
        unchecked {
            prevIdx = _lastCollectionId - 1;
            _artists[artistAddr_].collectionsCount++;
        }

        // Every new collection's tokenId will start from the previous end + 10000(gap)
        c.tokenId =
            _collections[prevIdx].tokenIdFrom +
            _collections[prevIdx].maxSupply +
            10000;
        c.tokenIdFrom = c.tokenId;
    }

    /**
     * @notice Returns boolean flag indicating whether collection exists
     * @param collectionId_ collection ID
     */
    function collectionExists(uint256 collectionId_)
        public
        view
        returns (bool)
    {
        return _collections[collectionId_].artistAddr != address(0);
    }

    /**
     * @notice Allows collection authorized or contract owner to toggle address collection authorization state
     * @param collectionId_ collection ID
     * @param addr_ address to toggle authorization state for
     * @param state_ authorization state - true or false
     */
    function toggleCollectionAuthorization(
        uint256 collectionId_,
        address addr_,
        bool state_
    ) public onlyAuthorized(collectionId_) {
        _nonZeroAddress(addr_);
        _collections[collectionId_].authorized[addr_] = state_;
        emit CollectionAuthorizationUpdated(collectionId_, addr_, state_);
    }

    /**
     * @notice Allows contract owner to set royalty for specific collection
     * @param collectionId_ collection ID
     * @param royaltyAddr_ new royalty address
     * @param royaltyBasis_ new royalty basis
     */
    function updateCollectionRoyalty(
        uint256 collectionId_,
        address royaltyAddr_,
        uint16 royaltyBasis_
    ) public onlyOwner {
        require(royaltyBasis_ <= 5000, "Maximum 50% royalty");
        _collections[collectionId_].royaltyAddr = royaltyAddr_;
        _collections[collectionId_].royaltyBasis = royaltyBasis_;
    }

    /**
     * @notice Returns royalty information about collection
     * @param collectionId_ collection ID
     */
    function collectionRoyaltyInfo(uint256 collectionId_)
        public
        view
        returns (address, uint16)
    {
        return (
            _collections[collectionId_].royaltyAddr == address(0)
                ? _collections[collectionId_].artistAddr
                : _collections[collectionId_].royaltyAddr,
            _collections[collectionId_].royaltyBasis
        );
    }

    /**
     * @notice Allows artist or contract owner to update on-chain info about artist by address
     * @param addr_ artist address
     * @param name_ new artist name
     * @param info_ new artist info - free text
     */
    function updateArtist(
        address addr_,
        string memory name_,
        string memory info_
    ) public {
        require(
            _msgSender() == owner() || _msgSender() == addr_,
            "Unauthorized"
        );

        if (bytes(name_).length > 0) {
            _artists[addr_].name = name_;
        }

        if (bytes(info_).length > 0) {
            _artists[addr_].info = info_;
        }
    }

    /**
     * @notice Allows contract owner to update on-chain collection name
     * @param collectionId_ collection ID
     * @param collectionName_ collection name
     */
    function updateCollectionName(
        uint256 collectionId_,
        string memory collectionName_
    ) public onlyOwner {
        _collections[collectionId_].name = bytes32(bytes(collectionName_));
    }

    /**
     * @notice Allows contract owner to update collection's artist address
     * @param collectionId_ collection ID
     * @param artistAddr_ new artist address
     */
    function updateCollectionArtistAddr(
        uint256 collectionId_,
        address artistAddr_
    ) public onlyOwner {
        _nonZeroAddress(artistAddr_);
        address oldAddress = _collections[collectionId_].artistAddr;
        _collections[collectionId_].artistAddr = artistAddr_;
        emit CollectionArtistAddressUpdated(
            collectionId_,
            artistAddr_,
            oldAddress
        );
    }

    /**
     * @notice Returns artist address for collection
     * @param collectionId_ collection ID
     */
    function collectionArtistAddr(uint256 collectionId_)
        public
        view
        returns (address)
    {
        return _collections[collectionId_].artistAddr;
    }

    /**
     * @notice Allows collection authorized or contract owner to add addresses to premint list
     * @param collectionId_ collection ID
     * @param addresses_ array of addresses
     */
    function addToCollectionPremintList(
        uint256 collectionId_,
        address[] memory addresses_
    ) public onlyAuthorized(collectionId_) {
        Collection storage c = _collections[collectionId_];

        require(
            c.premintListCount + addresses_.length <= c.premintListCap,
            "Presale list overflow"
        );

        for (uint256 i = 0; i < addresses_.length; i++) {
            if (!c.premintList[addresses_[i]]) {
                c.premintList[addresses_[i]] = true;
                unchecked {
                    c.premintListCount++;
                }
            }
        }
    }

    /**
     * @notice Allows collection authorized or contract owner to remove addresses from premint list
     * @param collectionId_ collection ID
     * @param addresses_ array of addresses
     */
    function removeFromCollectionPremintList(
        uint256 collectionId_,
        address[] memory addresses_
    ) public onlyAuthorized(collectionId_) {
        Collection storage c = _collections[collectionId_];

        for (uint256 i = 0; i < addresses_.length; i++) {
            if (c.premintList[addresses_[i]]) {
                c.premintList[addresses_[i]] = false;
                unchecked {
                    c.premintListCount--;
                }
            }
        }
    }

    /**
     * @notice Returns boolean flag indicating if address is on the collection's premint list
     * @param collectionId_ collection ID
     * @param addr_ checked address
     */
    function isOnCollectionPremintList(uint256 collectionId_, address addr_)
        public
        view
        returns (bool)
    {
        return _collections[collectionId_].premintList[addr_];
    }

    /**
     * @notice Returns collection's premint list capacity and current count
     * @param collectionId_ collection ID
     */
    function collectionPremintListDetails(uint256 collectionId_)
        public
        view
        returns (uint16, uint16)
    {
        return (
            _collections[collectionId_].premintListCap,
            _collections[collectionId_].premintListCount
        );
    }

    /**
     * @notice Allows collection authorized or contract owner to update premint and mint prices for collection
     * @param collectionId_ collection ID
     * @param shouldUpdateMintPrice_ flag indicating whether mint price should be updated
     * @param mintPrice_ mint price to be set if shouldUpdateMintPrice_ > 0
     * @param shouldUpdatepremintPrice_ flag indicating whether premint price should be updated
     * @param premintPrice_ premint price to be set if shouldUpdatepremintPrice_ > 0
     */
    function updateCollectionMintPrices(
        uint256 collectionId_,
        uint8 shouldUpdateMintPrice_,
        uint256 mintPrice_,
        uint8 shouldUpdatepremintPrice_,
        uint256 premintPrice_
    ) public onlyAuthorized(collectionId_) {
        // using parameter which determines if value should be updated
        // proceed with update if shouldUpdate..._ paramters != 0

        if (shouldUpdateMintPrice_ != 0) {
            _collections[collectionId_].mintPrice = mintPrice_;
        }

        if (shouldUpdatepremintPrice_ != 0) {
            _collections[collectionId_].premintPrice = premintPrice_;
        }
    }

    /**
     * @notice Returns collection's mint and premint prices
     * @param collectionId_ collection ID
     */
    function collectionMintPrices(uint256 collectionId_)
        public
        view
        returns (uint256, uint256)
    {
        return (
            _collections[collectionId_].mintPrice,
            _collections[collectionId_].premintPrice
        );
    }

    /**
     * @notice Allows collection authorized or contract owner to update collection capacities - mint, premint, premint list
     * @param collectionId_ collection ID
     * @param shouldUpdateMintCap_ flag if mint capacity is updated
     * @param mintCap_ mint capacity to set
     * @param shouldUpdatePremintCap_ flag if premint capacity is updated
     * @param premintCap_ premint capacity to set
     * @param shouldUpdatePremintListCap_ flag if premint list capacity is updated
     * @param premintListCap_ premint list capacity to set
     */
    function updateCollectionCaps(
        uint256 collectionId_,
        uint8 shouldUpdateMintCap_,
        uint16 mintCap_,
        uint8 shouldUpdatePremintCap_,
        uint16 premintCap_,
        uint8 shouldUpdatePremintListCap_,
        uint16 premintListCap_
    ) public onlyAuthorized(collectionId_) {
        // using parameter which determines if value should be updated
        // proceed with update if shouldUpdate..._ paramters != 0

        if (shouldUpdateMintCap_ != 0) {
            _collections[collectionId_].mintCap = mintCap_;
        }

        if (shouldUpdatePremintCap_ != 0) {
            _collections[collectionId_].premintCap = premintCap_;
        }

        if (shouldUpdatePremintListCap_ != 0) {
            _collections[collectionId_].premintListCap = premintListCap_;
        }
    }

    /**
     * @notice Returns collection's mint and premint capacities
     * @param collectionId_ collection ID
     */
    function collectionMintCaps(uint256 collectionId_)
        public
        view
        returns (uint16, uint16)
    {
        return (
            _collections[collectionId_].mintCap,
            _collections[collectionId_].premintCap
        );
    }

    /**
     * @notice Mint function - will mint to message sender
     * @param collectionId_ collection ID
     * @param amount_ minted NFTs count
     */
    function mint(uint256 collectionId_, uint16 amount_) public payable {
        mintTo(collectionId_, amount_, _msgSender());
    }