using System;
using System.Collections;
using System.Data;

namespace Orciid.Core
{
	/// <summary>
	/// Virtual collection of favorite images
	/// </summary>
	/// <remarks>
	/// This class is used to provide a searchable interface to other users' shared images (not
	/// including images that belong to the current user).
	/// While it provides a collection called "Shared Images" on the search screen, there is actually
	/// no such collection in the system, and the search results depend on the current user
	/// when searching this collection.
	/// </remarks>
	public class SharedImagesCollection:
		VirtualCollection
	{
		/// <summary>
		/// Constructor
		/// </summary>
		/// <remarks>
		/// This constructor overrides the default collection constructor. Since this
		/// collection is only virtual, no default access control etc. is required.
		/// </remarks>
		internal SharedImagesCollection():
			base()
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <remarks>
		/// This constructor overrides the collection constructor
		/// </remarks>
		/// <param name="init">Dummy parameter</param>
		internal SharedImagesCollection(bool init):
			base(init)
		{
		}

		/// <summary>
		/// Get the shared images collection
		/// </summary>
		/// <remarks>
		/// There is only one instance of this class, which can be retrieved using this method.
		/// </remarks>
		/// <returns>The only instance of the SharedImagesCollection class</returns>
		public static SharedImagesCollection GetSharedImagesCollection()
		{
			ArrayList ids = GetIDsFromCache(classid);
			if (ids != null)
				foreach (int id in ids)
				{
					Collection coll = (Collection)GetFromCache(classid, id);
					if (coll.Type == CollectionType.SharedImages)
						return coll as SharedImagesCollection;
				}
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					"SELECT ID FROM Collections WHERE Type='S'");
				SharedImagesCollection coll = GetByID(conn.DataToInt(conn.ExecScalar(query), 0)) 
					as SharedImagesCollection;
				if (coll != null)
					coll.accesscontrol = null;
				return coll;
			}
		}

		/// <summary>
		/// Access control object
		/// </summary>
		/// <remarks>
		/// This method returns the associated <see cref="AccessControl"/> object.
		/// </remarks>
		/// <returns>The access control object for this collection.</returns>
		public override IAccessControl GetAccessControl()
		{
			return new PersonalImagesCollectionAccessControl();
		}

		/// <summary>
		/// Get the number of images that are shared by other users and accessible to the
		/// current user
		/// </summary>
		/// <remarks>
		/// This method returns the number of shared images in all collections accssible to
		/// the current user, not including images shared by the current user.
		/// </remarks>
		/// <value>The number of images</value>
		public override int ImageCount
		{
			get
			{
				User user = User.CurrentUser();
				if (user == null)
					return 0;
				ArrayList ids = new ArrayList();
				foreach (Collection coll in Collection.GetCollections(CollectionType.Internal))
					ids.Add(coll.ID);
				if (ids.Count == 0)
					return 0;
				using (DBConnection conn = DBConnector.GetConnection())
				{
					Query query = new Query(conn,
						@"SELECT COUNT(*) FROM Images WHERE UserID IS NOT NULL 
						AND UserID<>0 AND UserID<>{userid} AND CollectionID IN {collectionids}
						AND (Flags&{shared}={shared})");
					query.AddParam("userid", user.ID);
					query.AddParam("collectionids", ids);
					query.AddParam("shared", ImageFlag.Shared);
					return conn.DataToInt(conn.ExecScalar(query), 0);
				}
			}
		}

		/// <summary>
		/// Search favorite images
		/// </summary>
		/// <remarks>
		/// A search on the favorite images collection simply maps the search onto a search of
		/// regular collections, with one added search criteria set to restrict the result to
		/// images contained in the current user's favorite images.
		/// </remarks>
		/// <param name="engine">The search engine to use for this search</param>
		/// <param name="conditions">An array of search conditions</param>
		/// <returns>An array of ImageIdentifiers, or <c>null</c> if no images were found.</returns>
		internal override ImageIdentifier[] Search(SearchCondition[] conditions, SearchEngine engine)
		{
			// create additional search condition to find others' shared images only
			OwnerCondition ownercond = new OwnerCondition();
			ownercond.FindOthers = true;
			SearchCondition[] myconditions = 
				new SearchCondition[(conditions == null ? 1 : conditions.Length + 1)];
			if (conditions != null)
				conditions.CopyTo(myconditions, 1);
			myconditions[0] = ownercond;
			// run search against all accessible internal collections
			ArrayList combined = new ArrayList();
			foreach (Collection coll in Collection.GetCollections(CollectionType.Internal))
			{
				ImageIdentifier[] r = coll.PerformSearch(myconditions, engine);
				if (r != null)
					combined.AddRange(r);
			}
			ImageIdentifier[] result = new ImageIdentifier[combined.Count];
			combined.CopyTo(result);
			return result;
		}

		/// <summary>
		/// Collection Type
		/// </summary>
		/// <remarks>
		/// This property is read-only.
		/// </remarks>
		/// <value>
		/// Always <see cref="CollectionType.Favorites"/>.
		/// </value>
		public override CollectionType Type
		{
			get
			{
				return CollectionType.SharedImages;
			}
		}

		/// <summary>
		/// Keyword searchable
		/// </summary>
		/// <remarks>
		/// The favorites collection does not itself have fields, so keyword
		/// searching must be explicitely allowed.
		/// </remarks>
		/// <value><c>true</c></value>
		public override bool IsKeywordSearchable
		{
			get
			{
				return true;
			}
		}

        /// <summary>
        /// Query distinct field values
        /// </summary>
        /// <remarks>
        /// This method supports collection browsing by returning distinct field values
        /// </remarks>
        /// <param name="field">The field for which to retrieve values</param>
        /// <param name="ascending">return values in ascending or descending order</param>
        /// <param name="count">number of values to return</param>
        /// <param name="include">Include or exclude the <c>startat</c> value</param>
        /// <param name="startat">The start value</param>
        /// <returns>A sorted ArrayList of strings containing the field values.</returns>
		public override string[] GetBrowseValues
			(Field field, string startat, bool include, bool ascending, int count)
		{
			if (field == null || !field.Browsable || field.ID >= 0)
				return null;

			ArrayList wheres = new ArrayList();
			User user = User.CurrentUser();

			foreach (int cid in GetRelevantCollectionIDs())
			{
				Collection coll = Collection.GetByID(cid);				
				bool canshare = User.HasPrivilege(Privilege.ShareImages, coll);
				Field f = coll.GetField(field.DCName, true, true);
				if (f != null)
					wheres.Add(String.Format("FieldData.CollectionID={0} AND FieldID={1} " +
						"AND 1={2}",						
						cid, 
						f.ID,
						canshare ? 1 : 0));
			}

			return GetBrowseValuesQuery(
				field, 
				startat, 
				include, 
				ascending, 
				count, 
				"",
				String.Format("Images.UserID<>{0}", user.ID),
				"Images.UserID<>0",
				String.Format("Flags&{0}={0}", (int)ImageFlag.Shared),
				"((" + String.Join(") OR (", (string[])wheres.ToArray(typeof(string))) + "))"
				);
		}

		/// <summary>
		/// Image Navigation
		/// </summary>
		/// <remarks>
		/// Indicates if images in the collection can be navigated by going
		/// forward/backward between images.
		/// </remarks>
		/// <returns><c>false</c></returns>
		public override bool HasImageNavigation()
		{
			return false;
		}

		/// <summary>
		/// Get relevant collections
		/// </summary>
		/// <remarks>
		/// Returns an array of collections that are relevant to this collection, meaning that
		/// this virtual collections combines other collections with regard to searching, browsing
		/// etc.
		/// </remarks>
		/// <returns>An array of collections</returns>
		protected override int[] GetRelevantCollectionIDs()
		{
			User user = User.CurrentUser();
			if (user == null)
				return null;
			// return all collections that contain shared images owned by other users
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"SELECT DISTINCT CollectionID FROM Images
					WHERE (UserID<>0) AND (UserID<>{userid}) AND (Flags&{shared}={shared})");
				query.AddParam("shared", ImageFlag.Shared);
				query.AddParam("userid", user.ID);
				return conn.TableToArray(conn.SelectQuery(query));
			}
		}
	}
}
