using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Xml;
using System.Xml.Serialization;
using Orciid.Core;

namespace Orciid.WebServices
{
	/// <summary>
	/// Web service class for Flash-based MDID LightTable and SlideshowViewer
	/// </summary>
	[WebService(Namespace="http://mdid.jmu.edu/webservices",
			Description = "MDID Flash Player Service")]
	public class FlashPlayer : OrciidWebService
	{
		public FlashPlayer()
		{
			//CODEGEN: This call is required by the ASP.NET Web Services Designer
			InitializeComponent();
		}

		public struct SlideshowInfo 
		{
			public int slideshowid;
			public string title;
			public string firstname;
			public string lastname;
			public string datecreated;
			public string errormessage;
			public bool userloggedin;
		}

		public struct SessionInfo 
		{
			public bool isactive;
			public int duration;
		}

		public struct GetImageResponse
		{
			public bool success;
			public string errormessage;
			public string imageresource;
			public string imagenotes;
			public FieldsEntry catalogdata; 
		}

		public struct FieldEntry
		{
			[XmlAttribute]
			public string name;
			[XmlText]
			public string val;

			public FieldEntry(string n, string v)
			{
				name = n;
				val = v;
			}
		}

		public struct FieldsEntry
		{
			[XmlArray]
			[XmlArrayItem(ElementName = "field", Type = typeof(FieldEntry))]
			public ArrayList fields;
		}

		public struct ImageInfo 
		{
			public string imageresource;
			public int slideid;
			public string imageid;
			public bool scratch;
			public string slideannotation;
			public string imagenotes;
			public FieldsEntry catalogdata;
		}

		public struct UpdateSlideshowResponseInfo 
		{
			public bool success;
			public string errormessage;
		}

		public struct Note 
		{
			public string imageid;
			public string newtext;
		}

		public struct SlideUpdateInfo 
		{
			public int slideid;
			public bool isscratch;
			public string imageid;
			public bool updateannotation;
			public string annotation;
		}

		#region Component Designer generated code
		
		//Required by the Web Services Designer 
		private IContainer components = null;
				
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if(disposing && components != null)
			{
				components.Dispose();
			}
			base.Dispose(disposing);		
		}
		
		#endregion

		/// <summary>
		/// Updates changes made to a slideshow through the Light Table
		/// </summary>
		/// <remarks>
		/// Updates the order in which slides appear within a slideshow; updates the removal
		/// of slides from a slideshow; updates the scratch property of the slides in a slideshow;
		/// and updates changes to slide annotations and images notes of the slides in a slideshow.
		/// </remarks>
		/// <param name="slideshowID">Identifies the slideshow to return to the client.</param>
		/// <param name="slides">An array of SlideUpdateInfo structs</param>
		/// <param name="notes">An array of structures containing an imageID and a string holding the note.</param>
		/// <returns>Returns a structure with a boolean flag indicating success or failure and a string holding any error messages</returns>
		[WebMethod(EnableSession = true)]
		public UpdateSlideshowResponseInfo UpdateSlideshow(int slideshowID, SlideUpdateInfo[] slides, Note[] notes) 
		{
			CheckSSLRequirement(false);
			UpdateSlideshowResponseInfo updateresponse = new UpdateSlideshowResponseInfo();
			//Context.Request.InputStream.Seek(0,System.IO.SeekOrigin.Begin);
			//byte[] buffer = new byte[this.Context.Request.InputStream.Length];
			//char[] cbuffer = new char[this.Context.Request.InputStream.Length];
			//this.Context.Request.InputStream.Read(buffer, 0, (int) this.Context.Request.InputStream.Length);
			//buffer.CopyTo(cbuffer, 0);
			//string test = new string(cbuffer);
			//Orciid.Core.User user = Orciid.Core.User.GetByLogin("admin", "admin"); 
			Orciid.Core.User user = Session["CurrentUser"] as Orciid.Core.User;
			if (user != null) 
			{
				user.Activate(Context.Request.UserHostAddress);
				Session["CurrentUser"] = user;
			} 
			else 
			{
				updateresponse.success = false;
				updateresponse.errormessage = "session expired";
				return updateresponse;

			}
			Slideshow show = Slideshow.GetByID(slideshowID);
			if (show == null) 
			{
				updateresponse.success = false;
				updateresponse.errormessage = "unable to find slideshow";
				return updateresponse;
			}
			ArrayList slideids = new ArrayList();
			ArrayList scratchids = new ArrayList();
			try 
			{
				for(int i = 0; i < slides.Length; i++) 
				{
					if (slides[i].slideid == 0) 
					{ //have a new slide
						Slide slide = new Slide();
						slide.ImageID = ImageIdentifier.Parse(slides[i].imageid);
						if (slides[i].annotation.Length > 0) slide.Annotation = slides[i].annotation;
						// need to update the show so that the slide gets an ID 
						show.Add(slide);
						show.Update();
						if (slides[i].isscratch)
							scratchids.Add(slide.ID);
						else 
							slideids.Add(slide.ID);
					} 
					else 
					{
						if (slides[i].updateannotation) 
						{
							Slide slide = show.GetSlideByID(slides[i].slideid);
							if (slide != null) slide.Annotation = slides[i].annotation;
						}
						if (slides[i].isscratch)
							scratchids.Add(slides[i].slideid);
						else 
							slideids.Add(slides[i].slideid);
					}
				}
				show.Reorder((int[])slideids.ToArray(typeof(int)), (int[])scratchids.ToArray(typeof(int)));
			}
			catch
			{
				updateresponse.success = false;
				updateresponse.errormessage = "server error";
				return updateresponse;
			}
			for (int i = 0; i < notes.Length; i++) 
			{
				Image image = Image.GetByID(ImageIdentifier.Parse(notes[i].imageid));
				if (image != null) 
				{
					image.SetAnnotation(notes[i].newtext);
				}
			}
			try 
			{
				show.Update();
			}
			catch (Exception e)
			{
				updateresponse.success = false;
				updateresponse.errormessage = e.Message.ToString();
				return updateresponse;
			}
			updateresponse.success = true;
			return updateresponse;
		}

		/// <summary>
		/// Refresh the user session to delay expiration
		/// </summary>
		/// <remarks>
		/// Delays the session expiration by the number of minutes specified in sessionState timeout
		/// node located in the "web.config" file.
		/// </remarks>
		/// <returns>Returns a SessionInfo structure whose members include the boolean member "isactive"
		/// which indicates whether user is still logged in and the integer member duration which indicates
		/// the length in minutes of an MDID session.</returns>
		[WebMethod(EnableSession = true)]
		public SessionInfo RefreshSession() 
		{
			CheckSSLRequirement(false);
			SessionInfo sessioninfo = new SessionInfo();
			//Orciid.Core.User user = Orciid.Core.User.GetByLogin("admin", "admin"); 
			Orciid.Core.User user = Session["CurrentUser"] as Orciid.Core.User;
			if (user == null) 
			{
				sessioninfo.isactive = false;
			}			
			else 
			{
				sessioninfo.isactive = true;

			}
			sessioninfo.duration = Context.Session.Timeout;
			return sessioninfo;
		}

		/// <summary>
		/// Gets slideshow information for specified slideshow.
		/// </summary>
		/// <remarks>If the slideshow exists and and is accessible to the user making the request,
		/// it retrieves the slideshow title, its owner's first and last names, and the date the
		/// slideshow requires; otherwise, it returns an error message.</remarks>
		/// <param name="slideshowid">A slideshow identifier</param>
		/// <returns>An non-negative integer identifying a single slideshow or -1 if no slideshow
		/// is identified. If the slideshow identifier is greater than -1, it also returns the slideshow
		/// title, its owner's first and last names, and the date the slideshow was created; otherwise,
		/// it returns a human-readable error message.</returns>
		[WebMethod(EnableSession = true)]
		public SlideshowInfo GetSlideshowInfo(int slideshowid)
		{
			CheckSSLRequirement(false);
			//Context.Request.InputStream.Seek(0,System.IO.SeekOrigin.Begin);
			//byte[] buffer = new byte[this.Context.Request.InputStream.Length];
			//char[] cbuffer = new char[this.Context.Request.InputStream.Length];
			//this.Context.Request.InputStream.Read(buffer, 0, (int) this.Context.Request.InputStream.Length);
			//buffer.CopyTo(cbuffer, 0);
			//string test = new string(cbuffer);
			SlideshowInfo slideshowinfo = new SlideshowInfo();
			//Orciid.Core.User user = Orciid.Core.User.GetByLogin("admin", "admin"); 
			Orciid.Core.User user = Session["CurrentUser"] as Orciid.Core.User;
			if (user != null) 
			{
				user.Activate(Context.Request.UserHostAddress);
				//Session["CurrentUser"] = user;
				slideshowinfo.userloggedin = true;
			}			
			else 
			{
				slideshowinfo.slideshowid = -1;
				slideshowinfo.errormessage = "You are not logged in or your session has expired.";
				slideshowinfo.userloggedin = false;
				return slideshowinfo;
			}
			if (slideshowid < 0) 
			{
				slideshowinfo.slideshowid = -1;
				slideshowinfo.errormessage = "Invalid slideshow identifier.";
				return slideshowinfo;
			} 
			Slideshow show = Slideshow.GetByID(slideshowid);
			if (show == null) 
			{
				slideshowinfo.slideshowid = -1;
				slideshowinfo.errormessage = "Slideshow not found or not accessible.";
			}
			else
			{
				slideshowinfo.slideshowid = slideshowid;
				Orciid.Core.User showowner = Orciid.Core.User.GetByID(show.Owner);
				slideshowinfo.title = show.Title;
				slideshowinfo.firstname = showowner.FirstName;
				slideshowinfo.lastname = showowner.LastName;
				slideshowinfo.datecreated = show.CreationDate;
			}

			return slideshowinfo;
		}

		/// <summary>
		/// Gets a single image.
		/// </summary>
		/// <remarks>The current user must have read access to the requested image.</remarks>
		/// <param name="imageIdentifier">Identifies the image to return to the client.</param>
		/// <param name="imageFormatCode">Indicates image format to use in constructing the
		/// imageresource element of the ImageInfo structure. "T" is for "thumbnail" format;
		/// "M" is for "medium" format; and "F" is for "full" format.</param>
		/// <returns>a boolean flag indicating whether the image was successfully retrieved; 
		/// an error message if the image was not successfully retrieved; and an XML-formatted 
		/// list of images and corresponding catalog information, including the image ID,  the 
		/// catalog data, and the image notes.</returns>
		[WebMethod(EnableSession = true)]
		public GetImageResponse GetImageObject(string imageIdentifier, string imageFormatCode)
		{
			CheckSSLRequirement(false);
			Orciid.Core.User user = Session["CurrentUser"] as Orciid.Core.User;
			GetImageResponse getimageresponse = new GetImageResponse();
			if (user != null) 
				user.Activate(Context.Request.UserHostAddress);
			else 
			{
				getimageresponse.success = false;
				getimageresponse.errormessage = "You are not logged in.";
				return getimageresponse;
			}
			Image image;
			try 
			{
				image = Image.GetByID(ImageIdentifier.Parse(imageIdentifier));
			}
			catch (Exception e) 
			{
				getimageresponse.success = false;
				getimageresponse.errormessage =  e.Message;
				return getimageresponse;
			}
			if (image == null) 
			{
				getimageresponse.success = false;
				getimageresponse.errormessage = "Image is not available.";
				return getimageresponse;
			}
			Collection coll;
			coll = Collection.GetByID(image.CollectionID);
			//XmlDocument doc = new XmlDocument();
			if (imageFormatCode == "M") 
			{
				DateTime mydate;
				if (coll != null)
					mydate = coll.GetResourceModificationDate(image.Resource, ImageSize.Medium);
				else
					mydate = DateTime.MinValue;
				getimageresponse.imageresource = String.Format("getimage.aspx?cid={0}&res={1}&format={2}&lm={3}&exp={4}", image.ID.CollectionID, Server.UrlEncode(image.Resource), imageFormatCode, mydate.Ticks/1000, 60 * 24 * 7);
			} 
			else 
			{
				getimageresponse.imageresource = String.Format("getimage.aspx?cid={0}&res={1}&format={2}", image.ID.CollectionID, Server.UrlEncode(image.Resource), imageFormatCode);
			}
			getimageresponse.imagenotes = "" + image.GetAnnotation(user);
			getimageresponse.catalogdata.fields = new ArrayList();
			if (coll != null)
			{
				getimageresponse.catalogdata.fields.Add(new FieldEntry("Collection", coll.Title));
				foreach (Field f in coll.GetFields())
					foreach (string s in image.GetDisplayValues(f))
						getimageresponse.catalogdata.fields.Add(new FieldEntry(f.Label, s));
			}
			getimageresponse.success = true;
			return getimageresponse;
		}

		/// <summary>
		/// Gets a single slideshow.
		/// </summary>
		/// <remarks>The current user must have read access to the requested slideshow.</remarks>
		/// <param name="slideshowID">Identifies the slideshow to return to the client.</param>
		/// <param name="includeScratch">If true, slides marked "scratch" are included in the</param>
		/// <param name="imageFormatCode">Indicates image format to use in constructing the
		/// imageresource element of the ImageInfo structure. "T" is for "thumbnail" format;
		/// "M" is for "medium" format; and "F" is for "full" format.</param>
		/// <returns>An XML-formatted list of images and corresponding catalog information, including
		/// the image ID, a boolean flag indicating whether the image is active or resides in the
		/// scratch space, the catalog data, the slide annotation, and the image notes.</returns>
		[WebMethod(EnableSession = true)]
		public ImageInfo[] GetSlideshow(int slideshowID, bool includeScratch, string imageFormatCode)
		{
			CheckSSLRequirement(false);
			Orciid.Core.User user = Session["CurrentUser"] as Orciid.Core.User;
			if (user != null) 
				user.Activate(Context.Request.UserHostAddress);
			else 
				return null;
			Slideshow show = Slideshow.GetByID(slideshowID);
			if (show == null)
				return null;
			Orciid.Core.User showowner = Orciid.Core.User.GetByID(show.Owner);
			string password = Session["slideshowpassword:" + show.ID.ToString()] as String;
			Slide[] slides = show.GetSlides(password);
			if (slides == null || slides.Length == 0) 
				return new ImageInfo[0];
			Image[] images = show.GetImages(password);
			ArrayList imageinfolist = new ArrayList(images.Length);
			//XmlDocument doc = new XmlDocument();
			for (int i = 0; i < slides.Length; i++) 
			{
				if (includeScratch || !slides[i].Scratch) 
				{
					ImageInfo imageinfo = new ImageInfo();
					Collection coll = Collection.GetByID(slides[i].ImageID.CollectionID);
					if (imageFormatCode == "M") 
					{
						long myticks = 1000;
						if (images[i] != null)
							myticks = images[i].GetResourceModificationDate(ImageSize.Medium).Ticks;
						if (coll == null || images[i] == null || (images[i].IsShareable && !images[i].IsShared))
							imageinfo.imageresource = 
								String.Format("getimage.aspx?id={0}&cid={1}&format={2}&lm={3}&exp=10080&ssid={4}", 
								slides[i].ImageID.ID, slides[i].ImageID.CollectionID, imageFormatCode, 
								myticks/1000, show.ID);
						else
							imageinfo.imageresource = 
								String.Format("getimage.aspx?cid={0}&res={1}&format={2}&lm={3}&exp=10080", 
								images[i].ID.CollectionID, Server.UrlEncode(images[i].Resource), imageFormatCode, 
								myticks/1000);							
					} 
					else 
					{
						if (coll == null || images[i] == null || (images[i].IsShareable && !images[i].IsShared))
							imageinfo.imageresource = 
								String.Format("getimage.aspx?id={0}&cid={1}&format={2}&ssid={3}", 
								slides[i].ImageID.ID, slides[i].ImageID.CollectionID, imageFormatCode, show.ID);
						else
							imageinfo.imageresource = 
								String.Format("getimage.aspx?cid={0}&res={1}&format={2}", 
								images[i].ID.CollectionID, Server.UrlEncode(images[i].Resource), imageFormatCode);
					}
					imageinfo.slideid = slides[i].ID;
					if (images[i] == null) 
					{
						imageinfo.imageid = slides[i].ImageID.ToString();
						imageinfo.imagenotes = "";
					}
					else 
					{
						imageinfo.imageid = images[i].ID.ToString();
						imageinfo.imagenotes = "" + images[i].GetAnnotation(showowner);
					}
					imageinfo.scratch = slides[i].Scratch;
					imageinfo.slideannotation = slides[i].Annotation;
					imageinfo.catalogdata.fields = new ArrayList();
					
					string colltitle;
					if (coll != null)
						colltitle = coll.Title;
					else if (images[i] != null)
						colltitle = images[i].GetCollectionTitle();
					else
						colltitle = "n/a";						
					imageinfo.catalogdata.fields.Add(new FieldEntry("Collection", colltitle));
					
					// workaround for bug 583, find title and add field to cataloging data
					// with label "Title" so that light table will find and use it
					bool hastitle = false;
					string title = null;
					// end workaround

					if (images[i] == null) 
						imageinfo.catalogdata.fields.Add(new FieldEntry("Title", "n/a"));
					else 
						foreach (Field f in images[i].GetFields())
							foreach (string s in images[i].GetDisplayValues(f))
							{

								// workaround for bug 583
								if (f.Label == "Title")
									hastitle = true;
								else if (!hastitle && title == null && String.Compare(f.DCName, "Title", true) == 0)
									title = s;
								// end workaround

								imageinfo.catalogdata.fields.Add(new FieldEntry(f.Label, s));
							}

					// workaround for bug 583
					if (!hastitle && title != null)
						imageinfo.catalogdata.fields.Add(new FieldEntry("Title", title));
					// end workaround

					imageinfolist.Add(imageinfo);
				}
			}
			return (ImageInfo[])imageinfolist.ToArray(typeof(ImageInfo));
		}
	}
}
