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

namespace Orciid.Core
{
	/// <summary>
	/// Saved image searches
	/// </summary>
	/// <remarks>
	/// Users can save their favorite searches and load or run them again later
	/// </remarks>
	public class SavedSearch: IComparable
	{
		private Search.SearchParameters parameters;
		private DateTime lastrun;
		private int id;
		private int userid;
		private string title;
		private int newrecords;

		private SavedSearch(int i, int u, string t, Search.SearchParameters p, DateTime lr, int nr)
		{
			id = i;
			userid = u;
			parameters = p;
			lastrun = lr;
			title = t;
			newrecords = nr;
		}

		/// <summary>
		/// Create a new saved search
		/// </summary>
		/// <remarks>
		/// The SavedSearch constructor is private, use this method to create new
		/// SavedSearch objects instead.
		/// </remarks>
		/// <param name="user">Owner of the new saved search</param>
		/// <param name="t">Title for this search</param>
		/// <param name="p">Parameters for this search</param>
		/// <returns>The new SavedSearch object</returns>
		public static SavedSearch Create(User user, string t, Search.SearchParameters p)
		{
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"INSERT INTO SavedSearches (UserID,Title,Parameters) 
					VALUES ({userid},{title},{parameters})");
				query.AddParam("userid", user.ID);
				query.AddParam("title", t.Length > 255 ? t.Substring(0, 255) : t);
				query.AddParam("parameters", p.ToXml());
				int result = conn.ExecQuery(query);
				if (result == 1)
					return new SavedSearch(
						conn.LastIdentity("SavedSearches"), user.ID, t, p, DateTime.MinValue, 0);
				else
					throw new CoreException("Could not save search.");
			}
		}

		/// <summary>
		/// Get all saved searches for a user
		/// </summary>
		/// <remarks>
		/// Returns an empty array if the user does not have any saved searches.
		/// </remarks>
		/// <param name="user">The user whose saved searches to retrieve</param>
		/// <returns>A non-null, possibly empty array of saved searches</returns>
		public static SavedSearch[] GetAll(User user)
		{
			ArrayList searches = new ArrayList();
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"SELECT ID,Title,Parameters,LastRun,NewRecords FROM SavedSearches
					WHERE UserID={userid} ORDER BY Title");
				query.AddParam("userid", user.ID);
				DataTable table = conn.SelectQuery(query);
				foreach (DataRow row in table.Rows)
				{
					int i = conn.DataToInt(row["ID"], 0);
					string t = conn.DataToString(row["Title"]);
					string p = conn.DataToString(row["Parameters"]);
					DateTime lr = conn.DataToDateTime(row["LastRun"], DateTime.MinValue);
					int nr = conn.DataToInt(row["NewRecords"], 0);
					searches.Add(new SavedSearch(i, user.ID, t, new Search.SearchParameters(p), lr, nr));
				}
			}
			return (SavedSearch[])searches.ToArray(typeof(SavedSearch));
		}

		/// <summary>
		/// Get a saved search by identifier
		/// </summary>
		/// <remarks>
		/// Returns null if no search exists with the specified identifier or if the 
		/// search belongs to a user other than the specified user.
		/// </remarks>
		/// <param name="user">The owner of the saved search</param>
		/// <param name="i">The internal identifier of the saved search</param>
		/// <returns>The saved search or <c>null</c></returns>
		public static SavedSearch Get(User user, int i)
		{
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"SELECT Title,Parameters,LastRun,NewRecords FROM SavedSearches
					WHERE UserID={userid} AND ID={id}");
				query.AddParam("userid", user.ID);
				query.AddParam("id", i);
				DataTable table = conn.SelectQuery(query);
				if (table.Rows.Count == 1)
				{
					DataRow row = table.Rows[0];
					string t = conn.DataToString(row["Title"]);
					string p = conn.DataToString(row["Parameters"]);
					DateTime lr = conn.DataToDateTime(row["LastRun"], DateTime.MinValue);
					int nr = conn.DataToInt(row["NewRecords"], 0);
					return new SavedSearch(i, user.ID, t, new Search.SearchParameters(p), lr, nr);
				}
				else
					return null;
			}
		}

		/// <summary>
		/// Delete a saved search
		/// </summary>
		/// <remarks>
		/// Deletes this saved search
		/// </remarks>
		public void Delete()
		{
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"DELETE FROM SavedSearches WHERE UserID={userid} AND ID={id}");
				query.AddParam("userid", User.CurrentUser().ID);
				query.AddParam("id", id);
				conn.ExecQuery(query);
			}
		}

		/// <summary>
		/// Search parameters
		/// </summary>
		/// <remarks>
		/// Returns the parameters of this saved search
		/// </remarks>
		/// <value>
		/// The parameters of this saved search
		/// </value>
		public Search.SearchParameters Parameters
		{
			get
			{
				return parameters;
			}
		}

		/// <summary>
		/// Last run date
		/// </summary>
		/// <remarks>
		/// Returns the date and time when this saved search was last run
		/// </remarks>
		/// <value>
		/// The date and time when this saved search was last run
		/// </value>
		public DateTime LastRun
		{
			get
			{
				return lastrun;
			}
		}
		
		/// <summary>
		/// Title
		/// </summary>
		/// <remarks>
		/// Returns the title of this saved search
		/// </remarks>
		/// <value>
		/// The title of this saved search
		/// </value>
		public string Title
		{
			get
			{
				return title;
			}
		}
		
		/// <summary>
		/// Owner
		/// </summary>
		/// <remarks>
		/// Returns the internal identifier of the user owning this saved search
		/// </remarks>
		/// <value>
		/// The internal identifier of the user owning this saved search
		/// </value>
		public int UserID
		{
			get
			{
				return userid;
			}
		}

		/// <summary>
		/// Internal identifier
		/// </summary>
		/// <remarks>
		/// Returns the internal identifier of this saved search
		/// </remarks>
		/// <value>
		/// The internal identifier of this saved search
		/// </value>
		public int ID
		{
			get
			{
				return id;
			}
		}

		/// <summary>
		/// Number of new records
		/// </summary>
		/// <remarks>
		/// Returns the number of new records that match this saved search. Any record
		/// created in the last seven days is considered new.
		/// </remarks>
		/// <value>
		/// The number of new records that match this saved search 
		/// </value>
		public int NewRecords
		{
			get
			{
				return newrecords;
			}
		}

		private void UpdateStatus()
		{
			using (DBConnection conn = DBConnector.GetConnection())
			{
				Query query = new Query(conn,
					@"UPDATE SavedSearches SET LastRun={lastrun},NewRecords={newrecords} WHERE ID={id}");
				query.AddParam("id", id);
				query.AddParam("lastrun", lastrun);
				query.AddParam("newrecords", newrecords);
				conn.ExecQuery(query);
			}
		}

		/// <summary>
		/// Compare objects
		/// </summary>
		/// <remarks>
		/// Compares two SavedSearch objects for sorting purposes.
		/// </remarks>
		/// <param name="obj">A second SavedSearch object</param>
		/// <returns>If both SavesSearch objects have the same title, returns the result of
		/// the comparison of the internal identifiers of the objects, otherwise returns the
		/// result of the comparison of the titles of the objects.</returns>
		public int CompareTo(object obj)
		{
			SavedSearch s = obj as SavedSearch;
			if (s == null)
				throw new ArgumentException("Can only compare to other SavedSearch object", "obj");
			if (title == s.title)
				return id.CompareTo(s.id);
			else
				return title.CompareTo(s.title);
		}

		/// <summary>
		/// Check for new images
		/// </summary>
		/// <remarks>
		/// Runs the saved search to find new images matching the search parameters.
		/// </remarks>
		public void CheckForNewImages()
		{
			TimeSpan sincelastrun = DateTime.Now - lastrun;
			if (sincelastrun < TimeSpan.FromDays(1))
				return;
			BackgroundTask task = new BackgroundTask(
				String.Format("SavedSearchChecking{0}", id),
				-2,
				"SavedSearch");
			task.OnRun += new BackgroundTaskDelegate(CheckForNewImagesRun);
			task.Queue();
		}

		private bool CheckForNewImagesRun(BackgroundTask task, object info)
		{
			Orciid.Core.User.GetByID(userid).Activate(false, null);
			RecordDateCondition cond = new RecordDateCondition();
			cond.CreatedWithin = TimeSpan.FromDays(7);
			Search.SearchParameters p = (Search.SearchParameters)parameters.Clone();
			p.conditions.Add(cond); 
			ImageIdentifier[] result = Search.Execute(p, 
				Configuration.Instance.GetInt("ui.maxsearchresults"), true);
			lastrun = DateTime.Now;
			newrecords = (result != null ? result.Length : 0);
			UpdateStatus();
			return true;
		}
	}
}
