private ENSessionFindNotesContext FindNotes_NextFindInLinkedScope(ENSessionFindNotesContext context)
		{
			if (context.linkedNotebooksToSearch.Count == 0)
			{
				context = FindNotes_ProcessResults(context);
				return context;
			}

			// Pull the first notebook off the list of pending linked notebooks.
			ENNotebook notebook = context.linkedNotebooksToSearch[0];
			context.linkedNotebooksToSearch.RemoveAt(0);

			ENNoteStoreClient noteStore = NoteStoreForLinkedNotebook(notebook.LinkedNotebook);
			NoteFilter notefilter = context.noteFilter;
			if (notebook.IsJoinedPublic)
			{
				// https://dev.evernote.com/doc/reference/NoteStore.html#Fn_NoteStore_findNotesMetadata
				// to search joined public notebook, the auth token can be nil, but notebookGuid must be set
				notefilter.NotebookGuid = notebook.Guid;
			}
			try
			{
				List<NoteMetadata> notesMetadataList = noteStore.FindNotesMetadata(context.noteFilter, context.maxResults, context.resultSpec);
				context.findMetadataResults.AddRange(notesMetadataList);
				// Do it again with the next linked notebook in the list.
				context = FindNotes_NextFindInLinkedScope(context);
			}
			catch (Evernote.EDAM.Error.EDAMUserException)
			{
			}
			catch (Exception ex)
			{
				ENSDKLogger.ENSDKLogError(string.Format("findNotes: Failed to find notes (linked).  Notebook = {0}; {1}", notebook, ex.Message));
				throw new Exception(ex.Message, ex.InnerException);
			}

			return context;
		}
		private ENSessionFindNotesContext FindNotes_ProcessResults(ENSessionFindNotesContext context)
		{
			// OK, now we have a complete list of note refs objects. If we need to do a local sort, then do so.
			if (context.order.HasFlag(SortOrder.RecentlyCreated))
			{
				if (context.sortAscending)
				{
					context.findMetadataResults.Sort((x, y) => x.Created.CompareTo(y.Created));
				}
				else
				{
					context.findMetadataResults.Sort((x, y) => y.Created.CompareTo(x.Created));
				}
			}
			else if (context.order.HasFlag(SortOrder.RecentlyCreated))
			{
				if (context.sortAscending)
				{
					context.findMetadataResults.Sort((x, y) => x.Updated.CompareTo(y.Updated));
				}
				else
				{
					context.findMetadataResults.Sort((x, y) => y.Updated.CompareTo(x.Updated));
				}
			}
			else
			{
				if (context.sortAscending)
				{
					context.findMetadataResults.Sort((x, y) => x.Title.CompareTo(y.Title));
				}
				else
				{
					context.findMetadataResults.Sort((x, y) => y.Title.CompareTo(x.Title));
				}
			}

			// Prepare a dictionary of all notebooks by GUID so lookup below is fast.
			Dictionary<string, ENNotebook> notebooksByGuid = null;
			if (context.scopeNotebook == null)
			{
				notebooksByGuid = new Dictionary<string, ENNotebook>();
				foreach (ENNotebook notebook in context.allNotebooks)
				{
					notebooksByGuid[notebook.Guid] = notebook;
				}
			}

			// Turn the metadata list into a list of note refs.
			List<ENSessionFindNotesResult> findNotesResults = new List<ENSessionFindNotesResult>();

			foreach (NoteMetadata metadata in context.findMetadataResults)
			{
				ENNoteRef @ref = new ENNoteRef();
				@ref.Guid = metadata.Guid;

				// Figure out which notebook this note belongs to. (If there's a scope notebook, it always belongs to that one.)
				ENNotebook notebook = null;
				if (context.scopeNotebook != null)
				{
					notebook = context.scopeNotebook;
				}
				else
				{
					notebooksByGuid.TryGetValue(metadata.NotebookGuid, out notebook);
				}
				if (notebook == null)
				{
					// This is probably a business notebook that we haven't explicitly joined, so we don't have it in our list.
					if (!(context.resultGuidsFromBusiness.Contains(metadata.Guid)))
					{
						// Oh, it's not from the business. We really can't find it. This is an error.
						ENSDKLogger.ENSDKLogError(string.Format("Found note metadata but can't determine owning notebook by guid. Metadata = {0}", metadata));
					}
					continue;
				}

				if (notebook.IsBusinessNotebook)
				{
					@ref.Type = ENNoteRef.ENNoteRefType.TypeBusiness;
					@ref.LinkedNotebook = ENLinkedNotebookRef.LinkedNotebookRefFromLinkedNotebook(notebook.LinkedNotebook);
				}
				else if (notebook.IsLinked)
				{
					@ref.Type = ENNoteRef.ENNoteRefType.TypeShared;
					@ref.LinkedNotebook = ENLinkedNotebookRef.LinkedNotebookRefFromLinkedNotebook(notebook.LinkedNotebook);
				}
				else
				{
					@ref.Type = ENNoteRef.ENNoteRefType.TypePersonal;
				}

				ENSessionFindNotesResult result = new ENSessionFindNotesResult();
				result.NoteRef = @ref;
				result.Notebook = notebook;
				result.Title = metadata.Title;
				result.Created = metadata.Created.ToDateTime();
				result.Updated = metadata.Updated.ToDateTime();

				findNotesResults.Add(result);

				// If the caller specified a max result count, and we've reached it, then stop fixing up
				// results here.
				if (context.maxResults > 0 && findNotesResults.Count > context.maxResults)
				{
					break;
				}
			}

			context.results = findNotesResults;
			return context;
		}
		private ENSessionFindNotesContext FindNotes_FindInLinkedScope(ENSessionFindNotesContext context)
		{
			// Skip linked scope if scope notebook is not a personal linked notebook, or if the
			// linked scope is not included.
			if (context.scopeNotebook != null)
			{
				if (!context.scopeNotebook.IsLinked || !context.scopeNotebook.IsBusinessNotebook)
				{
					context = FindNotes_ProcessResults(context);
					return context;
				}
			}
			else if (!(context.scope.HasFlag(SearchScope.PersonalLinked)))
			{
				context = FindNotes_ProcessResults(context);
				return context;
			}

			// Build a list of all the linked notebooks that we need to run the search against.
			context.linkedNotebooksToSearch = new List<ENNotebook>();
			if (context.scopeNotebook != null)
			{
				context.linkedNotebooksToSearch.Add(context.scopeNotebook);
			}
			else
			{
				foreach (ENNotebook notebook in context.allNotebooks)
				{
					if (notebook.IsLinked && !notebook.IsBusinessNotebook)
					{
						context.linkedNotebooksToSearch.Add(notebook);
					}
				}
			}

			context = FindNotes_NextFindInLinkedScope(context);
			return context;
		}
		private ENSessionFindNotesContext FindNotes_FindInBusinessScope(ENSessionFindNotesContext context)
		{
			// Skip the business scope if the user is not a business user, or the scope notebook
			// is not a business notebook, or the business scope is not included.
			if (!IsBusinessUser || (context.scopeNotebook != null && !context.scopeNotebook.IsBusinessNotebook) || (context.scopeNotebook == null && !(context.scope.HasFlag(SearchScope.Business))))
			{
				context = FindNotes_FindInLinkedScope(context);
				return context;
			}

			try
			{
				List<NoteMetadata> notesMetadataList = BusinessNoteStore.FindNotesMetadata(context.noteFilter, context.maxResults, context.resultSpec);
				context.findMetadataResults.AddRange(notesMetadataList);

				// Remember which note guids came from the business. We'll use this later to
				// determine if we're worried about an inability to map back to notebooks.
				context.resultGuidsFromBusiness = notesMetadataList.Select((x) => x.Guid).Distinct();
				//'Dim newList = notesMetadataList.[Select](Function(x) x.Guid).Distinct()


				context = FindNotes_FindInLinkedScope(context);
			}
			catch (Evernote.EDAM.Error.EDAMUserException ex)
			{
				if (ex.ErrorCode == Evernote.EDAM.Error.EDAMErrorCode.PERMISSION_DENIED)
				{
					// This is a business user, but apparently has an app notebook restriction that's
					// not in the business. Go look in linked scope.
					context = FindNotes_FindInLinkedScope(context);
				}
				else
				{
					ENSDKLogger.ENSDKLogError(string.Format("findNotes: Failed to find notes (business). {0}", ex.Message));
					throw new Exception(ex.Message, ex.InnerException);
				}
			}
			catch (Exception ex)
			{
				ENSDKLogger.ENSDKLogError(string.Format("findNotes: Failed to find notes (business). {0}", ex.Message));
				throw new Exception(ex.Message, ex.InnerException);
			}

			return context;
		}
		private ENSessionFindNotesContext FindNotes_FindInPersonalScope(ENSessionFindNotesContext context)
		{
			bool skipPersonalScope = false;
			// Skip the personal scope if the scope notebook isn't personal, or if the scope
			// flag doesn't include personal.
			if (context.scopeNotebook != null)
			{
				//If the scope notebook isn't personal, skip personal.
				if (context.scopeNotebook.IsLinked)
				{
					skipPersonalScope = true;
				}
			}
			else if (!(context.scope.HasFlag(SearchScope.Personal)))
			{
				// If the caller didn't request personal scope.
				skipPersonalScope = true;
			}
			else if (AppNotebookIsLinked)
			{
				// If we know this is an app notebook scoped app, and we know the app notebook is not personal.
				skipPersonalScope = true;
			}

			// If we're skipping personal scope, proceed directly to business scope.
			if (skipPersonalScope)
			{
				context = FindNotes_FindInBusinessScope(context);
				return context;
			}

			try
			{
				List<NoteMetadata> notesMetadataList = PrimaryNoteStore.FindNotesMetadata(context.noteFilter, context.maxResults, context.resultSpec);
				context.findMetadataResults.AddRange(notesMetadataList);
				context = FindNotes_FindInBusinessScope(context);
			}
			catch (Exception ex)
			{
				ENSDKLogger.ENSDKLogError(string.Format("findNotes: Failed to find notes (personal). {0}", ex.Message));
				throw new Exception(ex.Message, ex.InnerException);
			}

			return context;
		}
		private ENSessionFindNotesContext FindNotes_ListNotebooks(ENSessionFindNotesContext context)
		{
			// XXX: We do the full listNotebooks operation here, which is overkill in all situations,
			// and could wind us up doing a bunch of extra work. Optimization is to only look at -listNotebooks
			// if we're personal scope, and -listLinkedNotebooks for linked and business, without ever
			// authenticating to other note stores. But it's also true that a findNotes may well be followed
			// quickly by a fetchNote(s), which is going to require the full notebook list anyway, and by then
			// it'll be cached.

			List<ENNotebook> notebooks = ListNotebooks();
			if (notebooks != null)
			{
				context.allNotebooks = notebooks;
				context = FindNotes_FindInPersonalScope(context);
			}
			else
			{
				string err = "findNotes: Failed to list notebooks.";
				ENSDKLogger.ENSDKLogError(err);
				throw new Exception(err);
			}

			return context;
		}
		public List<ENSessionFindNotesResult> FindNotes(ENNoteSearch noteSearch, ENNotebook notebook, SearchScope scope, SortOrder order, int maxResults)
		{
			if (!IsAuthenticated)
			{
				throw new ENAuthExpiredException();
			}

			// App notebook scope is internally just an "all" search, because we don't a priori know where the app
			// notebook is. There's some room for a fast path in this flow if we have a saved linked record to a
			// linked app notebook, but that case is likely rare enough to prevent complexifying this code for.
			if (scope.HasFlag(SearchScope.AppNotebook))
			{
				scope = SearchScope.All;
			}

			// Validate the scope and sort arguments.
			if (notebook != null && scope != SearchScope.None)
			{
				ENSDKLogger.ENSDKLogError("No search scope necessary if notebook provided.");
				scope = SearchScope.None;
			}
			else if (notebook == null && scope == SearchScope.None)
			{
				ENSDKLogger.ENSDKLogError("Search scope or notebook must be specified. Defaulting to personal scope.");
				scope = SearchScope.DefaultScope;
			}

			bool requiresLocalMerge = false;
			if (scope != SearchScope.None)
			{
				// Check for multiple scopes. Because linked scope can subsume multiple linked notebooks, that *always* triggers
				// the multiple scopes. If not, then both personal and business must be set together.
				if ((scope.HasFlag(SearchScope.Personal) && scope.HasFlag(SearchScope.Business)) || scope.HasFlag(SearchScope.PersonalLinked))
				{
					// If we're asked for multiple scopes, relevance is not longer supportable (since we
					// don't know how to combine relevance on the client), so default to updated date,
					// which is probably the closest proxy to relevance.
					if (order.HasFlag(SortOrder.Relevance))
					{
						ENSDKLogger.ENSDKLogError("Cannot sort by relevance across multiple search scopes. Using update date.");
						order = (EvernoteSDK.ENSession.SortOrder)EN_FLAG_CLEAR(order, SortOrder.Relevance);
						order = (EvernoteSDK.ENSession.SortOrder)EN_FLAG_SET(order, SortOrder.RecentlyUpdated);
					}
					requiresLocalMerge = true;
				}
			}

			NotesMetadataResultSpec resultSpec = new NotesMetadataResultSpec();
			resultSpec.IncludeNotebookGuid = true;
			resultSpec.IncludeTitle = true;
			resultSpec.IncludeCreated = true;
			resultSpec.IncludeUpdated = true;
			resultSpec.IncludeUpdateSequenceNum = true;

			NoteFilter noteFilter = new NoteFilter();
			noteFilter.Words = noteSearch.SearchString;

			if (order.HasFlag(SortOrder.Title))
			{
				noteFilter.Order = (System.Int32)NoteSortOrder.TITLE;
			}
			else if (order.HasFlag(SortOrder.RecentlyCreated))
			{
				noteFilter.Order = (System.Int32)NoteSortOrder.CREATED;
			}
			else if (order.HasFlag(SortOrder.RecentlyUpdated))
			{
				noteFilter.Order = (System.Int32)NoteSortOrder.UPDATED;
			}
			else if (order.HasFlag(SortOrder.Relevance))
			{
				noteFilter.Order = (System.Int32)NoteSortOrder.RELEVANCE;
			}

			// "Normal" sort is ascending for titles, and descending for dates and relevance.
			bool sortAscending = order.HasFlag(SortOrder.Title) ? true : false;
			if (order.HasFlag(SortOrder.Reverse))
			{
				sortAscending = !sortAscending;
			}
			noteFilter.Ascending = sortAscending;

			if (notebook != null)
			{
				noteFilter.NotebookGuid = notebook.Guid;
			}

			// Set up context
			ENSessionFindNotesContext context = new ENSessionFindNotesContext();
			context.scopeNotebook = notebook;
			context.scope = scope;
			context.order = order;
			context.noteFilter = noteFilter;
			context.resultSpec = resultSpec;
			context.maxResults = maxResults;
			context.findMetadataResults = new List<NoteMetadata>();
			context.requiresLocalMerge = requiresLocalMerge;
			context.sortAscending = sortAscending;

			// If we have a scope notebook, we already know what notebook the results will appear in.
			// If we don't have a scope notebook, then we need to query for all the notebooks to determine
			// where to search.
			if (context.scopeNotebook == null)
			{
				context = FindNotes_ListNotebooks(context);
				return context.results;
			}

			context = FindNotes_FindInPersonalScope(context);
			return context.results;
		}