contract NFT is AccessControl, ERC721Burnable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and initial NFT(s).
     */
    constructor(
        address[] memory owner, 
        string memory name, 
        string memory symbol, 
        string memory baseURI, 
        uint256[] memory tokenId, 
        string[] memory tokenURI
    ) ERC721(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, owner[0]);
        _setupRole(MINTER_ROLE, owner[0]);
        _setBaseURI(baseURI);
        
        for (uint256 i = 0; i < owner.length; i++) {
            _mint(owner[i], tokenId[i]);
            _setTokenURI(tokenId[i], tokenURI[i]);
        }
    }
    
    /**
     * @dev Returns bool confirming whether tokenId exists.
     */
    function exists(uint256 tokenId) external view returns (bool) {
        return _exists(tokenId);
    }

    /**
     * @dev Creates a new token for `to` with `tokenId` and `tokenURI`. 
     *
     * See {ERC721-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 tokenId, string calldata tokenURI) external virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "NFT: must have minter role");

        _mint(to, tokenId);
        _setTokenURI(tokenId, tokenURI);
    }
    
    /**
     * @dev Creates a new token batch for `to` with iterated `tokenId` and `tokenURI`. 
     *
     * See {ERC721-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     * - array lengths must match.
     */
    function mintBatch(address[] calldata to, uint256[] calldata tokenId, string[] calldata tokenURI) external virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "NFT: must have minter role");
        require(to.length == tokenId.length && to.length == tokenURI.length, "!to/tokenId/tokenURI");
        
        for (uint256 i = 0; i < to.length; i++) {
            _mint(to[i], tokenId[i]);
            _setTokenURI(tokenId[i], tokenURI[i]);
        }

    }
    
    /**
     * @dev Creates a new token for `to` with `tokenId`, `tokenURI` and `data`. 
     *
     * See {ERC721-_safeMint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function safeMint(address to, uint256 tokenId, string calldata tokenURI, bytes calldata data) external virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "NFT: must have minter role");

        _safeMint(to, tokenId, data);
        _setTokenURI(tokenId, tokenURI);
    }
    
    /**
     * @dev Creates a new token batch for `to` with iterated `tokenId`, `tokenURI` and `data`. 
     *
     * See {ERC721-_safeMint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     * - array lengths must match.
     */
    function safeMintBatch(address[] calldata to, uint256[] calldata tokenId, string[] calldata tokenURI, bytes[] calldata data) external virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "NFT: must have minter role");
        require(to.length == tokenId.length && to.length == tokenURI.length, "!to/tokenId/tokenURI");
        
        for (uint256 i = 0; i < to.length; i++) {
            _safeMint(to[i], tokenId[i], data[i]);
            _setTokenURI(tokenId[i], tokenURI[i]);
        }

    }
    
    /**
     * @dev Transfers batch `tokenId` from `from` to `to`.
     *
     * Requirements:
     *
     * - array lengths must match.
     */
    function transferBatch(address[] calldata to, uint256[] calldata tokenId) external {
        require(to.length == tokenId.length, "!to/tokenId");

        for (uint256 i = 0; i < to.length; i++) {
            _transfer(_msgSender(), to[i], tokenId[i]);
        }
    }
}
/**
 * @dev {ERC721} token ("NFT"), including:
 *  
 *  - ability for holders to burn (destroy) their tokens
 *  - an admin role that allows for role management
 *  - a minter role that allows for token minting (creation)
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 */
function mint(address to, uint256 tokenId, string calldata tokenURI) external virtual {
    require(hasRole(MINTER_ROLE, _msgSender()), "NFT: must have minter role");

    _mint(to, tokenId);
    _setTokenURI(tokenId, tokenURI);
}

/**
 * @dev Creates a new token for `to` with `tokenId` and `tokenURI`. 
 *
 * See {ERC721-_mint}.
 *
 * Requirements:
 *
 * - the caller must have the `MINTER_ROLE`.
 */