using System;
using System.Collections;
using System.Data;
using Orciid.Core.Util;

namespace Orciid.Core
{
	/// <summary>
	/// Virtual collection of favorite images
	/// </summary>
	/// <remarks>
	/// This class is used to provide a searchable interface to a user's favorite images.
	/// While it provides a collection called "Favorites" 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 FavoritesCollection:
		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 FavoritesCollection():
			base()
		{
		}

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

		/// <summary>
		/// Get the favorites 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 FavoritesCollection class</returns>
		public static FavoritesCollection GetFavoritesCollection()
		{
			ArrayList ids = GetIDsFromCache(classid);
			if (ids != null)
				foreach (int id in ids)
				{
					Collection coll = (Collection)GetFromCache(classid, id);
					if (coll != null && coll.Type == CollectionType.Favorites)
						return coll as FavoritesCollection;
				}
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					"SELECT ID FROM Collections WHERE Type='F'");
				return GetByID(conn.DataToInt(conn.ExecScalar(query), 0)) as FavoritesCollection;
			}
		}

		/// <summary>
		/// Get the number of images in current user's favorites collection
		/// </summary>
		/// <remarks>
		/// This method returns the number of images in the favorites collection of
		/// the current user; not the total number of images for all users.
		/// </remarks>
		/// <value>The number of images</value>
		public override int ImageCount
		{
			get
			{
				User user = User.CurrentUser();
				if (user == null)
					return 0;
				using (DBConnection conn = DBConnector.GetConnection())
				{
					Query query = new Query(conn,
						"SELECT COUNT(*) FROM FavoriteImages WHERE UserID={userid}");
					query.AddParam("userid", user.ID);
					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="conditions">An array of search conditions</param>
		/// <param name="engine">The search engine to use for this search</param>
		/// <returns>An array of ImageIdentifiers, or <c>null</c> if no images were found.</returns>
		internal override ImageIdentifier[] Search(SearchCondition[] conditions, SearchEngine engine)
		{
			User user = User.CurrentUser();
			if (user == null)
				return null;
//			SearchCondition[] myconditions = 
//				new SearchCondition[(conditions == null ? 1 : conditions.Length + 1)];
//			if (conditions != null)
//				conditions.CopyTo(myconditions, 1);
			Hashtable ids = new Hashtable();
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					"SELECT ImageID,CollectionID FROM FavoriteImages WHERE UserID={userid}");
				query.AddParam("userid", user.ID);
/*				DataTable table = conn.SelectQuery(query);
				foreach (DataRow row in table.Rows)
				{
					ImageIdentifier imageid;
					imageid.ID = conn.DataToInt(row["ImageID"], 0);
					imageid.CollectionID = conn.DataToInt(row["CollectionID"], 0);
					if (ids.ContainsKey(imageid.CollectionID))
					{
						((ArrayList)ids[imageid.CollectionID]).Add(imageid);
					}
					else
					{
						ArrayList a = new ArrayList();
						a.Add(imageid);
						ids.Add(imageid.CollectionID, a);
					}
				}
*/
				using (IDataReader reader = conn.ExecReader(query))
				{
					while (reader.Read())
					{
						int id = conn.DataToInt(reader.GetValue(0), 0);
						int cid = conn.DataToInt(reader.GetValue(1), 0);
						if (ids.ContainsKey(cid))
						{
							((ArrayList)ids[cid]).Add(id);
						}
						else
						{
							ArrayList a = new ArrayList();
							a.Add(id);
							ids.Add(cid, a);
						}
					}
				}
			}
	
			ArrayList combined = new ArrayList();
			foreach (int collectionid in ids.Keys)
			{
				Collection coll = Collection.GetByID(collectionid);
				if (coll != null)
				{
//					ArrayList a = (ArrayList)ids[collectionid];
//					IDCondition idcondition = new IDCondition();
//					idcondition.ImageIdentifiers = new ImageIdentifier[a.Count];
//					a.CopyTo(idcondition.ImageIdentifiers);
//					myconditions[0] = idcondition;
//					ImageIdentifier[] r = coll.PerformSearch(myconditions);
					int[] r = coll.PerformRawSearch(conditions, engine);
					int[] fav = (int[])((ArrayList)ids[collectionid]).ToArray(typeof(int));
					r = IntegerListTools.Intersect(r, fav);
					if (r != null)
						foreach (int i in r)
							combined.Add(new ImageIdentifier(i, collectionid));
				}
			}
//			ImageIdentifier[] result = new ImageIdentifier[combined.Count];
//			combined.CopyTo(result);
			return (ImageIdentifier[])combined.ToArray(typeof(ImageIdentifier));
		}

		/// <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.Favorites;
			}
		}

		/// <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 (Images.UserID=0 OR Images.UserID IS NULL OR Images.UserID={2} " +
						"OR (1={3} AND Flags&{4}={4}))", 
						cid, 
						f.ID, 
						user.ID,
						canshare ? 1 : 0,
						(int)ImageFlag.Shared));
			}

			return GetBrowseValuesQuery(
				field, 
				startat, 
				include, 
				ascending, 
				count, 
				"INNER JOIN FavoriteImages ON (FieldData.CollectionID=FavoriteImages.CollectionID AND FieldData.ImageID=FavoriteImages.ImageID)",
				String.Format("FavoriteImages.UserID={0}", user.ID),
				"((" + 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;
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					"SELECT DISTINCT CollectionID FROM FavoriteImages WHERE UserID={userid}");
				query.AddParam("userid", user.ID);
				return conn.TableToArray(conn.SelectQuery(query));
			}
		}
	}
}
