using System;
using System.Collections;

namespace Orciid.Core
{
	/// <summary>
	/// Base class for virtual collections
	/// </summary>
	/// <remarks>
	/// This is the base class for virtual collections like FavoritesCollection etc.
	/// It provides some functionality common to virtual collections that is different
	/// from regular collections, e.g. not using the standard access control, only 
	/// having a few modifiable properties etc.
	/// </remarks>
	public abstract class VirtualCollection:
		Collection
	{
		internal VirtualCollection():
			base()
		{
		}

		internal VirtualCollection(bool init):
			base(init)
		{
		}

		/// <summary>
		/// Check Modification Privilege
		/// </summary>
		/// <remarks>
		/// Since this class represents a virtual collection, it cannot be modified.
		/// </remarks>
		/// <param name="user">The user object to check the privileges for</param>
		/// <returns><c>false</c></returns>
		public override bool CanModify(User user)
		{
			return (user != null && user.Administrator);
		}

		/// <summary>
		/// Check Creation Privilege
		/// </summary>
		/// <remarks>
		/// Since this class represents a virtual collection, no new instances can be created.
		/// </remarks>
		/// <param name="user">The user object to check the privileges for</param>
		/// <returns><c>false</c></returns>
		public override bool CanCreate(User user)
		{
			return false;
		}

		/// <summary>
		/// Check image creation privilege
		/// </summary>
		/// <remarks>
		/// This method checks if the specified user has sufficient privileges
		/// to create an image.
		/// </remarks>
		/// <param name="user">The user to check privileges for</param>
		/// <param name="personal"><c>true</c> if a personal image should be created,
		/// <c>false</c> if a regular image for the collection should be created</param>
		/// <returns><c>false</c></returns>
		public override bool CanCreateImage(User user, bool personal)
		{
			return false;
		}

		/// <summary>
		/// Check image modification privilege
		/// </summary>
		/// <remarks>
		/// This method checks if the specified user has sufficient privileges
		/// to modify the given image.
		/// </remarks>
		/// <param name="user">The user to check privileges for</param>
		/// <param name="image">The image to be modified</param>
		/// <returns><c>false</c></returns>
		public override bool CanModifyImage(User user, Image image)
		{
			return false;
		}


		/// <summary>
		/// Read only collection
		/// </summary>
		/// <remarks>
		/// The favorites collection does not hold any fields or records and
		/// is read only.
		/// </remarks>
		/// <value><c>true</c></value>
		public override bool IsReadOnly
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Writes a modified collection object to the database
		/// </summary>
		/// <remarks>After writing the collection to the database, the object is returned
		/// to an unmodifiable state.
		/// </remarks>
		protected override void CommitChanges()
		{
			if (title == null || title.Length == 0 || description == null || description.Length == 0)
				throw new CoreException("Title and Description have to be set for a virtual collection");
			int result;
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query;
				if (id == 0) 
					throw new CoreException("Additional virtual collections cannot be created");
				query = new Query(conn,
					@"UPDATE Collections 
					SET Title={title},Description={description},GroupID={groupid}
					WHERE ID={id}");
				query.AddParam("title", title);
				query.AddParam("description", description);
				query.AddParam("groupid", group);
				query.AddParam("id", id);
				result = conn.ExecQuery(query);
			}
			if (result != 1) 
				throw new CoreException("Could not write modified virtual collection object to database");
		}

		/// <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 abstract int[] GetRelevantCollectionIDs();

		/// <summary>
		/// Compiles a list of fields for this virtual collection
		/// </summary>
		/// <remarks>
		/// This function compiles a list of common fields from all collections underlying this
		/// virtual collection.
		/// </remarks>
		/// <returns>An array of Field objects.  These objects do not have an identifier,
		/// since they are not stored in the database.</returns>
		public override Field[] GetFields()
		{
			SortedList fields = new SortedList();
			int[] collids = GetRelevantCollectionIDs();
			if (collids == null || collids.Length == 0)
				return new Field[0];
			// retrieve collections for given IDs
			ArrayList colls = new ArrayList(collids.Length);
			foreach (int cid in collids)
			{
				Collection coll = Collection.GetByID(cid);
				if (coll != null)
					colls.Add(coll);
			} 
			if (colls.Count == 0)
				return new Field[0];
			int fieldid = 0; // to give each field a dummy identifier
			// look at fields of first collection
			foreach (Field field in ((Collection)colls[0]).GetFields())
			{
				if (field.Searchable && field.DCElement != null && !fields.ContainsKey(field.DCName))
				{
					Field f = new Field();
					f.SetID(--fieldid);
					f.DCElement = field.DCElement;
					f.DCRefinement = field.DCRefinement;
					f.Name = f.DCName;
					f.Label = f.DCName; // FogBugz case 550: nice labels for dublin core fields
					f.Searchable = true;
					f.Browsable = field.Browsable;
					f.Type = FieldType.Text;
					fields.Add(f.DCName, f);
				}
			}
			// go through all collections and remove fields that don't exist in all of them
			ArrayList remove = new ArrayList();
			foreach (Collection coll in colls)
			{
				foreach (string dc in fields.Keys)
				{
					Field f = coll.GetField(dc, true, true);
					if (f == null)
						remove.Add(dc);
					else
					{
						Field s = (Field)fields[dc];
						s.Browsable = s.Browsable && f.Browsable;
					}
				}
			}
			ArrayList result = new ArrayList();
			foreach (string dc in fields.Keys)
				if (!remove.Contains(dc))
					result.Add(fields[dc]);
			return (Field[])result.ToArray(typeof(Field));
		}
	}
}
