using System;
using System.Collections;
using System.Data;
using System.IO;
using OpenPOP.POP3;
using OpenPOP.MIMEParser;
using Orciid.Media;
using Orciid.Media.Util;
using Orciid.Core.Util;

namespace Orciid.Core.Camio
{
	/// <summary>
	/// CAMIO interface
	/// </summary>
	/// <remarks>
	/// Supports the import of CAMIO emails containing images and image data from a POP3
	/// email account.
	/// </remarks>
	public class CamioEmailImport
	{
		private const string TASKID = "CamioEmailImport";
		private const string SENDEREMAIL = "CAMIO@rlg.org";

		/// <summary>
		/// Maps CAMIO fields to dublin core fields
		/// </summary>
		private static Hashtable DublinCoreMappings;

		static CamioEmailImport()
		{
			DublinCoreMappings = new Hashtable(12);
			DublinCoreMappings.Add("creator", "creator");
			DublinCoreMappings.Add("title", "title");
			DublinCoreMappings.Add("date", "date");
			DublinCoreMappings.Add("object type", "type");
			DublinCoreMappings.Add("materials and techniques", "format.medium");
			DublinCoreMappings.Add("dimensions", "format.extent");
			DublinCoreMappings.Add("style", "coverage.temporal");
			DublinCoreMappings.Add("contributor", "contributor");
			DublinCoreMappings.Add("id", "identifier");
			DublinCoreMappings.Add("copyright", "rights");
			DublinCoreMappings.Add("database", "source");
			DublinCoreMappings.Add("note", null);
		}

		/// <summary>
		/// Initialize CAMIO import
		/// </summary>
		/// <remarks>
		/// Schedules a background task to check for new email from CAMIO on a regular
		/// basis.
		/// </remarks>
		[Initialize]
		public static void Initialize()
		{
			// this method could run before anything else is initialized, so wait a few
			// minutes before querying configuration or database
			if (DequeueTask())
				ScheduleBackgroundTask(TimeSpan.FromMinutes(5));
		}

		private static void ScheduleBackgroundTask(TimeSpan delay)
		{
			BackgroundTask task = new BackgroundTask(TASKID, -1, "camio", DateTime.Now + delay);
			task.OnRun += new BackgroundTaskDelegate(task_OnRun);
			task.OnFinish += new BackgroundTaskDelegate(task_OnFinish);
			task.Queue();
		}

		/// <summary>
		/// Import emails now
		/// </summary>
		/// <remarks>
		/// Immediately performs an import of CAMIO emails
		/// </remarks>
		/// <returns><c>true</c> if the task was successfully started, <c>false</c> if the task
		/// was already running or could not be controlled</returns>
		public static bool RunNow()
		{
			if (!DequeueTask())
				return false;
			ScheduleBackgroundTask(TimeSpan.Zero);
			return true;
		}

		private static bool DequeueTask()
		{
			BackgroundTask task = BackgroundTask.FindTask(TASKID);
			if (task != null)
			{
				try
				{
					task.Dequeue();
				}
				catch
				{
					return false;
				}
			}
			return true;
		}

		private static bool task_OnRun(BackgroundTask task, object info)
		{
			Properties props = Properties.GetSystemProperties();
			bool enabled = (props.GetAsInt("camioenabled", 0) != 0);
			int collid = props.GetAsInt("camiocollid", 0);
			string imgowner = props.Get("camioimgowner", "");
			string pop3server = props.Get("camiopop3server", "");
			int pop3port = props.GetAsInt("camiopop3port", 110);
			bool pop3ssl = (props.GetAsInt("camiopop3ssl", 0) != 0);
			string account = props.Get("camioaccount", "");
			string password = props.Get("camiopassword", "");
			string prefix = props.Get("camioprefix", "");
			if (!enabled)
				return false;
			
			Collection coll = Collection.GetByID(false, collid);
			if (coll == null)
				throw new CoreException("CamioEmailImport: Collection not found");
			
			User owner = User.GetByLogin(imgowner);
			if (owner == null)
				throw new CoreException("CamioEmailImport: Owner not found");
			if (!User.HasPrivilege(Privilege.ReadCollection | Privilege.PersonalImages | 
				Privilege.SuggestImages, coll, owner))
				throw new CoreException("CamioEmailImport: Owner does not have required " +
					"privileges on target collection (Read, Personal Images, Suggest Images)");

			if (pop3server.Length == 0 || account.Length == 0 || password.Length == 0)
				throw new CoreException("CamioEmailImport: POP3 server not fully configured");

			POPClient client = new POPClient(pop3server, pop3port, account, password,
				AuthenticationMethod.TRYBOTH, pop3ssl);
			int count = client.GetMessageCount();
			for (int i = 1; i <= count; i++)
			{
				Message message = client.GetMessage(i, false);
				if (message.FromEmail == SENDEREMAIL)
				{
					MemoryStream image = null;
					string mimetype = "";
					string data = null;
					foreach (Attachment a in message.Attachments)
					{
						string ext = Path.GetExtension(a.ContentFileName).ToLower();
						if (ext == ".csv")
							data = a.DecodeAsText();
						else if (ext == ".jpg")
						{
							mimetype = "image/jpeg";
							image = new MemoryStream(a.DecodedAsBytes());
						}
					}
					if (image != null && data != null)
					{
						try
						{
							if (SubmitImage(coll, owner, image, mimetype, data, prefix))
								client.DeleteMessage(i);
						}
						catch (Exception ex)
						{
							TransactionLog.Instance.AddException(
								"CAMIO Email Import", 
								"Error while submitting image file", 
								ex);
						}
					}
				}
			}			
			client.Disconnect();
			return true;
		}

		private static bool task_OnFinish(BackgroundTask task, object info)
		{
			Properties props = Properties.GetSystemProperties();
			if (props.GetAsInt("camioenabled", 0) != 0)
				ScheduleBackgroundTask(TimeSpan.FromHours(props.GetAsInt("camiofrequency", 2)));
			return true;
		}

		private static bool SubmitImage(Collection coll, User owner, Stream imagestream, 
			string mimetype, string data, string prefix)
		{
			DataTable table = CsvParser.Parse(data, true);
			if (table.Rows.Count != 1)
				return false;

			string sender = table.Rows[0]["note"] as string;
			User user = (sender != null ? User.GetByLogin(sender) : null);
			if (user != null && 
				User.HasPrivilege(Privilege.ReadCollection | Privilege.PersonalImages | 
				Privilege.SuggestImages, coll, user))
				user.Activate(false, null);
			else
				owner.Activate(false, null);

			Image image = coll.CreateImage(true);
			foreach (DataColumn column in table.Columns)
			{
				string dcfield = DublinCoreMappings[column.Caption] as string;
				if (dcfield == null)
					continue;
				Field field = coll.GetField(dcfield, true, true);
				if (field == null)
					continue;
				string val = table.Rows[0][column].ToString();
				if (dcfield == "identifier")
					val = prefix + val;
				image[field].Add(val);
			}
			image.Update();
			coll.SuggestImageForInclusion(true, image.ID);
			bool success = image.SetResourceData(imagestream, mimetype);
			if (!success)
				image.Delete();
			return success;
		}
	}
}
