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

namespace Orciid.Core
{
	/// <summary>
	/// Collection group
	/// </summary>
	/// <remarks>
	/// Collections can be organized in groups, e.g. by topic or subject
	/// </remarks>
	public class CollectionGroup:
		CachableObject, IComparable
	{
		private const string classid = "Orciid.Core.CollectionGroup";

		private int id;
		private string title;

		/// <summary>
		/// Default constructor
		/// </summary>
		/// <remarks>
		/// Creates a new collection group
		/// </remarks>
		public CollectionGroup():
			base()
		{
		}

		/// <summary>
		/// Internal constructor
		/// </summary>
		/// <remarks>
		/// This constructor needs to be called if a CollectionGroup object is constructed with data from the
		/// database. No privilege check or other initialization is performed.
		/// </remarks>
		/// <param name="init">Dummy parameter, should always be <c>false</c></param>
		private CollectionGroup(bool init):
			base(init)
		{
		}

		/// <summary>
		/// Check creation privilege
		/// </summary>
		/// <remarks>
		/// Users require the <see cref="Privilege.ManageCollectionGroups"/> privilege
		/// to create new collection groups.
		/// </remarks>
		/// <param name="user">The user for which to check the privilege</param>
		/// <returns><c>true</c> if the specified user can create collection groups,
		/// <c>false</c> otherwise</returns>
		public override bool CanCreate(User user)
		{
			return User.HasPrivilege(Privilege.ManageCollectionGroups, user);
		}

		/// <summary>
		/// Check modification privilege
		/// </summary>
		/// <remarks>
		/// Users require the <see cref="Privilege.ManageCollectionGroups"/> privilege
		/// to modify collection groups.
		/// </remarks>
		/// <param name="user">The user for which to check the privilege</param>
		/// <returns><c>true</c> if the specified user can modify collection groups,
		/// <c>false</c> otherwise</returns>
		public override bool CanModify(User user)
		{
			return User.HasPrivilege(Privilege.ManageCollectionGroups, user);
		}

		/// <summary>
		/// Compare collection groups
		/// </summary>
		/// <remarks>
		/// Collection groups are compared by their title.
		/// </remarks>
		/// <param name="c">Another collection group to compare this one to</param>
		/// <returns>The result of the comparison of the two collection group titles.</returns>
		public int CompareTo(object c)
		{
			return title.CompareTo(((CollectionGroup)c).title);
		}

		/// <summary>
		/// Delete
		/// </summary>
		/// <remarks>
		/// Removes a collection group from the system.  All collections in this collection
		/// group will be moved to the default collection group.
		/// </remarks>
		public void Delete()
		{
			User.RequirePrivilege(Privilege.ManageCollectionGroups);
			if (id > 0)
			{
				using (DBConnection conn = DBConnector.GetConnection())
				{
					Query query = new Query(conn,
						"UPDATE Collections SET GroupID=0 WHERE GroupID={groupid}");
					query.AddParam("groupid", id);
					conn.ExecQuery(query);

					query = new Query(conn,
						"DELETE FROM CollectionGroups WHERE ID={id}");
					query.AddParam("id", id);
					conn.ExecQuery(query);
				}
				RemoveFromCache(this, true);
				id = 0;
			}
			ResetModified();
		}

		/// <summary>
		/// Identifier
		/// </summary>
		/// <remarks>
		/// The internal identifier of this collection group.
		/// </remarks>
		/// <value>
		/// The internal identifier of this collection group.
		/// </value>
		public int ID
		{
			get
			{
				return id;
			}
		}

		/// <summary>
		/// Identifier
		/// </summary>
		/// <remarks>
		/// The internal identifier of this collection group.
		/// </remarks>
		/// <returns>
		/// The internal identifier of this collection group.
		/// </returns>
		public override int GetID()
		{
			return id;
		}

		/// <summary>
		/// Class Identifier
		/// </summary>
		/// <remarks>
		/// The class identifier, used for caching
		/// </remarks>
		/// <returns><c>"Orciid.Core.CollectionGroup"</c></returns>
		protected override string GetClassIdentifier()
		{
			return classid;
		}

		/// <summary>
		/// Title
		/// </summary>
		/// <remarks>
		/// The title of this collection group.  Must not be <c>null</c> or empty.
		/// </remarks>
		/// <value>The title of this collection group</value>
		public string Title
		{
			get
			{
				return title;
			}
			set
			{
				if (value != null && value.Length > 0) 
				{
					MarkModified();
					title = (value.Length > 255 ? value.Substring(0, 255) : value);
				}
				else
					throw new CoreException("Title must not be null or empty.");
			}
		}

		/// <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>
		/// <exception cref="CoreException">Thrown if the object cannot be written
		/// to the database.</exception>
		/// <exception cref="CoreException">Thrown if the title for
		/// the collection is not set.</exception>
		protected override void CommitChanges()
		{
			if (title == null || title.Length == 0)
				throw new CoreException("Title has to be set for a collection group");
			int result;
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query;
				if (id == 0) 
				{
					query = new Query(conn,
						"INSERT INTO CollectionGroups (Title) VALUES ({title})");
					query.AddParam("title", title);
					result = conn.ExecQuery(query);
					if (result == 1)
					{
						id = conn.LastIdentity("CollectionGroups");
						AddToCache(this, true);
					}
					else
						throw new CoreException("Could not write new collection group object to database");
				}
				else
				{
					query = new Query(conn,
						"UPDATE CollectionGroups SET Title={title} WHERE ID={id}");
					query.AddParam("title", title);
					query.AddParam("id", id);
					result = conn.ExecQuery(query);
				}
			}
			if (result != 1) 
				throw new CoreException("Could not write modified collection group object to database");
		}

		/// <summary>
		/// Get all collection groups
		/// </summary>
		/// <remarks>
		/// Retrieves all collection groups
		/// </remarks>
		/// <returns>An array of collection groups sorted by title</returns>
		public static CollectionGroup[] GetCollectionGroups()
		{
			ArrayList ids = GetCollectionGroupIDs();
			CollectionGroup[] groups = new CollectionGroup[ids.Count];
			for (int i = 0; i < ids.Count; i++)
				groups[i] = GetByID((int)ids[i]);
			Array.Sort(groups);
			return groups;
		}

		/// <summary>
		/// Get individual collection group
		/// </summary>
		/// <remarks>
		/// Retrieves a collection group specified by its internal identifier
		/// </remarks>
		/// <param name="id">The internal identifier of the collection to retrieve</param>
		/// <returns>The collection group or <c>null</c> if the collection group was not found</returns>
		public static CollectionGroup GetByID(int id)
		{
			CollectionGroup group = (CollectionGroup)GetFromCache(classid, id);
			if (group == null)
				using (DBConnection conn = DBConnector.GetConnection())
				{
					Query query = new Query(conn,
						"SELECT ID,Title FROM CollectionGroups WHERE ID={id}");
					query.AddParam("id", id);
					DataTable table = conn.SelectQuery(query);
					if (table.Rows.Count > 0)
					{
						DataRow row = table.Rows[0];
						group = new CollectionGroup(false);
						group.id = conn.DataToInt(row["ID"], 0);
						group.title = conn.DataToString(row["Title"]);
						AddToCache(group);
					}
				}
			return group;
		}

		/// <summary>
		/// Get set of collection groups
		/// </summary>
		/// <remarks>
		/// Retrieves collection groups specified by their internal identifier
		/// </remarks>
		/// <param name="id">The internal identifiers of the collections to retrieve</param>
		/// <returns>An array of collection groups.  Individual elements may be <c>null</c> 
		/// if the respective collection group was not found</returns>
		public static CollectionGroup[] GetByID(int[] id)
		{
			CollectionGroup[] groups = new CollectionGroup[id.Length];
			for (int i = 0; i < id.Length; i++)
				groups[i] = GetByID(id[i]);
			return groups;
		}

		private static ArrayList GetCollectionGroupIDs()
		{
			ArrayList ids = GetIDsFromCache(classid);
			if (ids == null)
			{
				ids = new ArrayList();
				using (DBConnection conn = DBConnector.GetConnection())
				{
					Query query = new Query(conn,
						"SELECT ID FROM CollectionGroups");
					DataTable table = conn.SelectQuery(query);
					foreach (DataRow row in table.Rows)
						ids.Add(conn.DataToInt(row["ID"], 0));
				}
				AddIDsToCache(classid, ids);
			}
			return ids;
		}
	}
}
