/// <summary>
        /// Initializes a new instance of the <see cref="GalleryObjectSearcher" /> class.
        /// </summary>
        /// <param name="searchOptions">The search options.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="searchOptions" /> is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when one or more properties of the <paramref name="searchOptions" /> parameter is invalid.</exception>
        /// <exception cref="Events.CustomExceptions.InvalidGalleryException">Thrown when the gallery ID specified in the <paramref name="searchOptions" />
        /// parameter is invalid.</exception>
        public GalleryObjectSearcher(GalleryObjectSearchOptions searchOptions)
        {
            Validate(searchOptions);

            SearchOptions = searchOptions;

            if (SearchOptions.Roles == null)
            {
                SearchOptions.Roles = new GalleryServerRoleCollection();
            }
        }
        /// <summary>
        /// Validates the specified search options. Throws an exception if not valid.
        /// </summary>
        /// <param name="searchOptions">The search options.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="searchOptions" /> is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when one or more properties of the <paramref name="searchOptions" /> parameter is invalid.</exception>
        /// <exception cref="Events.CustomExceptions.InvalidGalleryException">Thrown when the gallery ID specified in the <paramref name="searchOptions" />
        /// parameter is invalid.</exception>
        private static void Validate(GalleryObjectSearchOptions searchOptions)
        {
            if (searchOptions == null)
                throw new ArgumentNullException("searchOptions");

            if (searchOptions.SearchType == GalleryObjectSearchType.NotSpecified)
                throw new ArgumentException("The SearchType property of the searchOptions parameter must be set to a valid search type.");

            if (searchOptions.IsUserAuthenticated && searchOptions.Roles == null)
                throw new ArgumentException("The Roles property of the searchOptions parameter must be specified when IsUserAuthenticated is true.");

            if (searchOptions.GalleryId < 0) // v3+ galleries start at 1, but galleries from earlier versions begin at 0
                throw new ArgumentException("Invalid gallery ID. The GalleryId property of the searchOptions parameter must refer to a valid gallery.");

            if ((searchOptions.SearchType == GalleryObjectSearchType.SearchByTag || searchOptions.SearchType == GalleryObjectSearchType.SearchByPeople) && (searchOptions.Tags == null || searchOptions.Tags.Length == 0))
                throw new ArgumentException("The Tags property of the searchOptions parameter must be specified when SearchType is SearchByTag or SearchByPeople.");

            if (searchOptions.SearchType == GalleryObjectSearchType.SearchByRating && (searchOptions.SearchTerms == null || searchOptions.SearchTerms.Length != 1))
                throw new ArgumentException("The SearchTerms property of the searchOptions parameter must contain a single string matching one of these values: highest, lowest, none, or a number from 0 to 5.");

            // This throws an exception when gallery ID doesn't exist or is the template gallery.
            Factory.LoadGallery(searchOptions.GalleryId);

            if (searchOptions.Filter == GalleryObjectType.Unknown || searchOptions.Filter == GalleryObjectType.NotSpecified)
                throw new ArgumentException(String.Format("The Filter property of the searchOptions parameter cannot be GalleryObjectType.{0}.", searchOptions.Filter));
        }
        //public static IQueryable<GalleryItem> GetGalleryItemsHavingTags(string[] tags, string[] people, int galleryId, MetadataItemName sortByMetaName, bool sortAscending, GalleryObjectType filter)
        //{
        //    IAlbum album = GetGalleryObjectsHavingTags(tags, people, filter, galleryId);
        //    IList<IGalleryObject> galleryObjects;
        //    if (MetadataItemNameEnumHelper.IsValidFormattedMetadataItemName(sortByMetaName))
        //    {
        //        galleryObjects = album.GetChildGalleryObjects(GalleryObjectType.All, !Utils.IsAuthenticated).ToSortedList(sortByMetaName, sortAscending, album.GalleryId);
        //    }
        //    else
        //    {
        //        galleryObjects = album.GetChildGalleryObjects(GalleryObjectType.All, !Utils.IsAuthenticated).ToSortedList();
        //    }
        //    return ToGalleryItems(galleryObjects).AsQueryable();
        //}
        /// <summary>
        /// Return a virtual album containing gallery objects whose title or caption contain the specified search strings and
        /// for which the current user has authorization to view. Guaranteed to not return null. A gallery 
        /// object is considered a match when all search terms are found in the relevant fields.
        /// </summary>
        /// <param name="searchStrings">The strings to search for.</param>
        /// <param name="filter">A filter that limits the types of gallery objects that are returned.
        /// Maps to the <see cref="GalleryObjectType" /> enumeration.</param>
        /// <param name="galleryId">The ID for the gallery containing the objects to search.</param>
        /// <returns>
        /// Returns an <see cref="IAlbum" /> containing the matching items. This may include albums and media
        /// objects from different albums.
        /// </returns>
        public static IAlbum GetGalleryObjectsHavingTitleOrCaption(string[] searchStrings, GalleryObjectType filter, int galleryId)
        {
            if (searchStrings == null)
                throw new ArgumentNullException();

            var tmpAlbum = Factory.CreateEmptyAlbumInstance(galleryId);
            tmpAlbum.IsVirtualAlbum = true;
            tmpAlbum.VirtualAlbumType = VirtualAlbumType.TitleOrCaption;
            tmpAlbum.Title = String.Concat(Resources.GalleryServerPro.Site_Search_Title, String.Join(Resources.GalleryServerPro.Site_Search_Concat, searchStrings));
            tmpAlbum.Caption = String.Empty;

            var searchOptions = new GalleryObjectSearchOptions
            {
                GalleryId = galleryId,
                SearchType = GalleryObjectSearchType.SearchByTitleOrCaption,
                SearchTerms = searchStrings,
                IsUserAuthenticated = Utils.IsAuthenticated,
                Roles = RoleController.GetGalleryServerRolesForUser(),
                Filter = filter
            };

            var searcher = new GalleryObjectSearcher(searchOptions);

            foreach (var galleryObject in searcher.Find())
            {
                tmpAlbum.AddGalleryObject(galleryObject);
            }

            return tmpAlbum;
        }