contract CryptoMonks is Ownable, ERC721A, ReentrancyGuard {
    uint256 public constant MAX_SUPPLY = 500;
    
    mapping(uint16 => string) private customTokenUris;

    // // metadata URI
    string private _baseTokenURI;

    address public MINTER_ROLE;

        //_setupRole(, _msgSender());
   

    constructor() ERC721A("CryptoMonks", "CryptoMonks", 5, 500) {
    }

  function _baseURI() internal view virtual override returns (string memory) {
    return _baseTokenURI;
  }



   /**
     * @dev minting possible only by the migration bridge
     */
    function mintMachine(address _owner, uint256 _tokenId) external  returns (uint256) {
        require(_msgSender() == MINTER_ROLE, "Missing MINTER_ROLE");
        require(_tokenId > 0 && _tokenId <= MAX_SUPPLY, "Token out of bound");
        require(totalSupply() <= MAX_SUPPLY, "Max supply reached");

        super._safeMint(_owner, 1, _tokenId);
        return(1);
    }


    function exists(uint256 _tokenId) external view returns (bool) {
        return _exists(_tokenId);
    }

   /**
     * @dev returns token ids owned by a owner
     */
    function tokensOfOwner(address _owner) external view returns (uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }

    function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {  
        require(_exists(_tokenId), "ERC721: URI query for nonexistent token");

        string memory _tokenURI = customTokenUris[uint16(_tokenId)];

        // If custom token is set then we return it
        if (bytes(_tokenURI).length > 0) {
            return _tokenURI;
        }

        return string(abi.encodePacked(_baseTokenURI, Strings.toString(_tokenId)));
    }


  function setBaseURI(string calldata baseURI) external onlyOwner {
    _baseTokenURI = baseURI;
  }

  function setMinterRole(address _address) external onlyOwner {
    MINTER_ROLE = _address;
  }

}
// import "@openzeppelin/contracts/access/Ownable.sol";
// import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
//import "./ERC721A.sol";
// import "@openzeppelin/contracts/utils/Strings.sol";
function tokensOfOwner(address _owner) external view returns (uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }
/**
 * @dev returns token ids owned by a owner
 */