using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using RJH.CommandLineHelper;
using Orciid.Core;

namespace MDIDImport
{
	class Import
	{
		#region Schema
		const string SCHEMA = @"
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified'>
  <xs:element name='mdid1databasedump'>
    <xs:complexType>
      <xs:sequence>
        <xs:element name='Users'>
          <xs:complexType>
            <xs:sequence>
              <xs:element name='User' maxOccurs='unbounded'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='UserID' type='xs:string'/>
                    <xs:element name='Password' type='xs:string'/>
                    <xs:element name='FName' type='xs:string' minOccurs='0'/>
                    <xs:element name='LName' type='xs:string'/>
                    <xs:element name='Type' type='xs:byte'/>
                    <xs:element name='External' type='xs:string'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name='ControlledLists'>
          <xs:complexType>
            <xs:sequence>
              <xs:element name='Countries'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Country' type='xs:string' maxOccurs='unbounded'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name='Cultures'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Culture' type='xs:string' maxOccurs='unbounded'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name='Media'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Medium' type='xs:string' maxOccurs='unbounded'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name='Periods'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Period' type='xs:string' maxOccurs='unbounded'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name='Styles'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Style' type='xs:string' maxOccurs='unbounded'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name='CatalogData'>
          <xs:complexType>
            <xs:sequence>
              <xs:element name='Record' maxOccurs='unbounded'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='ImageID' type='xs:string'/>
                    <xs:element name='CreatorName' type='xs:string'/>
                    <xs:element name='CreationYear' type='xs:string'/>
                    <xs:element name='Title' type='xs:string'/>
                    <xs:element name='Site' type='xs:string'/>
                    <xs:element name='CopyrightPermission' type='xs:string'/>
                    <xs:element name='Copyright' type='xs:string'/>
                    <xs:element name='Notes' type='xs:string'/>
                    <xs:element name='ImageSize' type='xs:int'/>
                    <xs:element name='Style' type='xs:string'/>
                    <xs:element name='Period' type='xs:string'/>
                    <xs:element name='Medium' type='xs:string'/>
                    <xs:element name='Country' type='xs:string'/>
                    <xs:element name='Culture' type='xs:string'/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name='Slideshows' minOccurs='0'>
          <xs:complexType>
            <xs:sequence>
              <xs:element name='Slideshow' minOccurs='0' maxOccurs='unbounded'>
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name='Slides' minOccurs='0'>
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name='Slide' minOccurs='0' maxOccurs='unbounded'>
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name='Comment' type='xs:string' minOccurs='0'/>
                              </xs:sequence>
                              <xs:attribute name='Image' type='xs:string' use='required'/>
                              <xs:attribute name='Order' type='xs:byte' use='required'/>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name='SlideshowID' type='xs:string' use='required'/>
                  <xs:attribute name='SlideshowTitle' type='xs:string' use='required'/>
                  <xs:attribute name='OwnerID' type='xs:string' use='required'/>
                  <xs:attribute name='CreationDate' type='xs:string' use='required'/>
                  <xs:attribute name='Archived' type='xs:boolean' use='required'/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name='timestamp' type='xs:string' use='required'/>
    </xs:complexType>
  </xs:element>
</xs:schema>
";
		#endregion

		private bool skipdata = false;
		private bool skipusers = false;
		private bool skipshows = false;
		private string xmlfile = null;
		private string sourcefiles;
		private string collectiontitle = "MDID1 Collection";
		private string username = "admin";
		private string password = "admin";
		private string emailext = "jmu.edu";

		[CommandLineSwitch("skipdata", "Skip data import")]
		public bool SkipData
		{
			get { return skipdata; }
			set { skipdata = value; }
		}

		[CommandLineSwitch("skipusers", "Skip user import")]
		public bool SkipUsers
		{
			get { return skipusers; }
			set { skipusers = value; }
		}

		[CommandLineSwitch("skipshows", "Skip slideshow import")]
		public bool SkipShows
		{
			get { return skipshows; }
			set { skipshows = value; }
		}

		[CommandLineSwitch("xmlfile", "XML file to import")]
		public string XmlFile
		{
			get { return xmlfile; }
			set { xmlfile = value; }
		}

		[CommandLineSwitch("sourcefiles", "Directory of image files")]
		public string SourceFiles
		{
			get { return sourcefiles; }
			set { sourcefiles = value; }
		}

		[CommandLineSwitch("title", "Collection title")]
		public string CollectionTitle
		{
			get { return collectiontitle; }
			set { collectiontitle = value; }
		}

		[CommandLineSwitch("username", "User name of administrative account")]
		public string UserName
		{
			get { return username; }
			set { username = value; }
		}

		[CommandLineSwitch("password", "Password for administrative account")]
		public string Password
		{
			get { return password; }
			set { password = value; }
		}

		[CommandLineSwitch("email", "Email domain for accounts")]
		public string EmailExt
		{
			get { return emailext; }
			set { emailext = value; }
		}

		ControlledList[] controlledlists = null;
		Collection collection = null;
		XmlDocument doc;

		int ImportList(string table, string column)
		{
			if (controlledlists == null)
				controlledlists = ControlledList.GetControlledLists(collection.ID);

			string title = String.Format("{0}", table);

			XmlNodeList nodelist = doc.SelectNodes(String.Format(
				"/mdid1databasedump/ControlledLists/{0}/{1}", table, column));

			foreach (ControlledList list in controlledlists)
				if (list.Title == title)
				{
					// controlled list exists, update
					Console.Write("Updating controlled list {0}... ", title);
					SortedList entries = list.GetSortedList();
					ArrayList newentries = new ArrayList();
					foreach (XmlNode node in nodelist)
					{
						string entry = node.InnerText.Trim();
						if (entry.Length > 0 && !entries.ContainsKey(entry))
							newentries.Add(entry);
					}
					if (newentries.Count > 0)
					{
						Console.WriteLine("{0} new entries", newentries.Count);
						list.Add((string[])newentries.ToArray(typeof(string)));
					}
					else
						Console.WriteLine("no changes");
					return list.ID;
				}

			Console.Write("Creating controlled list {0}... ", title);
			// no matching list found, create new
			ControlledList l = new ControlledList();
			l.Title = title;
			l.Description = String.Format("Contains list of {0} imported from MDID1", table);
			l.Standard = false;
			l.Origin = "MDID1";
			l.CollectionID = collection.ID;
			l.Update();
			ArrayList la = new ArrayList();
			foreach (XmlNode node in nodelist)
			{
				string v = node.InnerText.Trim();
				if (v.Length > 0)
					la.Add(v);
			}
			Console.WriteLine("{0} new entries", la.Count);						
			l.Add((string[])la.ToArray(typeof(string)));
			controlledlists = null;
			return l.ID;
		}

		void CreateField(string label, string name, FieldType type, 
			int controlledlistid, bool searchable, bool keywordsearchable,
			bool sortable, bool browsable, bool shortview)
		{
			Field field = collection.GetField(name, false, false);
			if (field == null)
			{
				Console.WriteLine("Creating field {0}", name);
				field = new Field();
				field.Label = label;
				field.Name = name;
				field.Type = type;
				if (controlledlistid > 0)
					field.ControlledListID = controlledlistid;
				field.Searchable = searchable;
				field.KeywordSearchable = keywordsearchable;
				field.Sortable = sortable;
				field.Browsable = browsable;
				field.ShortView = (shortview ? DisplayMode.All : DisplayMode.None);
				field.LongView = DisplayMode.All;
				collection.AddField(field);
			}
			else
				Console.WriteLine("Found field {0}", name);
		}
		public void Run(string[] args)
		{
			Parser parser = new Parser( System.Environment.CommandLine, this );
			parser.Parse();

			if (xmlfile == null)
			{
				#region usage
				Console.WriteLine(@"
MDID Data Migration Tool

Usage: mdidimport /xmlfile:<filename> [/skipdata] [/skipusers] [/skipshows]
                  [/title:<title>] [/username:<username>] 
                  [/password:<password>] [/email:<email>]

/xmlfile:<filename> 
    Specify import file.  This file must have been created using the data 
    export page in MDID1.

[/skipdata] 
    Do not import image data.

[/skipusers] 
    Do not import user accounts.

[/skipshows]
    Do not import slideshows.
                  
[/title:<title>] 
    Title of target collection.  Must not exist or must have been created 
    using this tool.

[/username:<username>] 
    Account to run this tool with.  Must be an administrative account.
    Default: admin

[/password:<password>]
    Password for the account to run this tool with. Default: admin
				  
[/email:<email>]
    Email domain for imported user accounts, e.g. yourinstitution.edu

");
				#endregion
				return;
			}

			Configuration.Instance.Initialize(Directory.GetCurrentDirectory());
//			try
//			{
				User.GetByLogin(username, password).Activate(null);
//			}
//			catch
//			{
//				Console.WriteLine("Could not log in");
//				return;
//			}

			Console.Write("Reading XML... ");
			XmlValidatingReader reader = new XmlValidatingReader(new XmlTextReader(
				new StreamReader(xmlfile)));
			XmlSchemaCollection schemas = new XmlSchemaCollection();
			schemas.Add(null, new XmlTextReader(new StringReader(SCHEMA)));
			reader.ValidationType = ValidationType.Schema;
			reader.Schemas.Add(schemas);
			doc = new XmlDocument();
			doc.Load(reader);
			Console.WriteLine("done");
				
			foreach (Collection coll in Collection.GetCollections(CollectionType.Internal))
				if (coll.Title == collectiontitle)
				{
					collection = coll;
					Console.WriteLine("Collection found");
					break;
				}

			if (collection == null)
			{
				Console.WriteLine("Collection not found, creating");
				collection = new Collection();
				collection.Title = collectiontitle;
				collection.Description = "Collection imported from MDID1";
				collection.ResourcePath = Path.Combine(
					Configuration.Instance.GetString("content.resourcepath"), "mdid1");
				ImageSettings full = new ImageSettings();
				ImageSettings medium = new ImageSettings();
				ImageSettings thumb = new ImageSettings();
				full.fixedsize = false;
				full.quality = 100;
				full.height = 3000;
				full.width = 3000;
				medium.fixedsize = false;
				medium.quality = 85;
				medium.height = 800;
				medium.width = 800;
				thumb.fixedsize = true;
				thumb.quality = 70;
				thumb.height = 72;
				thumb.width = 96;
				collection.SetImageSettings(ImageSize.Full, full);
				collection.SetImageSettings(ImageSize.Medium, medium);
				collection.SetImageSettings(ImageSize.Thumbnail, thumb);
				collection.Update();
			}

			int countrylistid = ImportList("Countries", "Country");
			int culturelistid = ImportList("Cultures", "Culture");
			int mediumlistid = ImportList("Media", "Medium");
			int periodlistid = ImportList("Periods", "Period");
			int stylelistid = ImportList("Styles", "Style");

			CreateField("ID", "ID", FieldType.Text, 0, true, true, true, true, true);
			CreateField("Title", "Title", FieldType.Text, 0, true, true, true, true, true);
			CreateField("Creator", "Creator", FieldType.Text, 0, true, true, true, true, true);
			CreateField("Creation Year", "CreationYear", FieldType.Text, 0, true, true, true, true, true);
			CreateField("Medium", "Medium", FieldType.ControlledList, mediumlistid, true, true, true, true, true);
			CreateField("Culture", "Culture", FieldType.ControlledList, culturelistid, true, true, true, true, false);
			CreateField("Period", "Period", FieldType.ControlledList, periodlistid, true, true, true, true, false);
			CreateField("Style", "Style", FieldType.ControlledList, stylelistid, true, true, true, true, false);
			CreateField("Country", "Country", FieldType.ControlledList, countrylistid, true, true, true, true, false);
			CreateField("Site", "Site", FieldType.Text, 0, true, true, true, true, false);
			CreateField("Copyright Permission", "CopyrightPermission", FieldType.Text, 0, false, false, false, false, false);
			CreateField("Copyright", "Copyright", FieldType.Text, 0, false, false, false, false, false);
			CreateField("Notes", "Notes", FieldType.Text, 0, true, true, false, false, false);

			if (!skipdata)
			{
				XmlNodeList nodelist = doc.SelectNodes("/mdid1databasedump/CatalogData/Record");

				int created = 0;
				int updated = 0;
				int problems = 0;
				int count = 0;
				int total = nodelist.Count;

				foreach (XmlNode node in nodelist)
				{
					count++;

					Hashtable fieldvalues = new Hashtable();
					foreach (XmlNode child in node.ChildNodes)
					{
						string name = child.Name;
						string val = child.InnerText.Trim();
						if (name == "CopyrightPermission")
							val = (val == "True" ? "YES" : "NO");
						else if (name == "ImageID")
							name = "ID";
						else if (name == "CreatorName")
							name = "Creator";
						else if (name == "ImageSize")
							val = "";
						if (val.Length > 0)
							fieldvalues.Add(name, val);
					}
					string resource = (string)fieldvalues["ID"] + ".jpg";
					Console.Write("{0}/{1} {2}: ", count, total, resource);
					ArrayList images = collection.GetImagesByResource(resource);
					if (images == null || images.Count == 0)
					{
						Image image = collection.CreateImage(false);
						image.Resource = resource;
						foreach (string fieldname in fieldvalues.Keys)
						{
							string v = (string)fieldvalues[fieldname];
							if (v != null)
								image[fieldname].Add(v);
						}
						image.Update();
						created++;
						Console.WriteLine("added");
					}
					else if (images.Count == 1)
					{
						Image image = (Image)images[0];
						bool changed = false;
						foreach (string fieldname in fieldvalues.Keys)
						{
							string v = fieldvalues[fieldname] as string;
							FieldData data = image[fieldname];
							if (v != null && v.Length > 0)
							{
								if (data.Count == 0 || data.Count > 1 || data[0] != v)
								{
									data.RemoveAll();
									data.Add(v);
									changed = true;
								}
							}
							else
							{
								if (data.Count != 0)
								{
									data.RemoveAll();
									changed = true;
								}
							}
						}
						if (changed)
						{
							image.Update();
							updated++;
							Console.WriteLine("updated");
						}
						else
							Console.WriteLine("unchanged");					
					}
					else
					{
						Console.WriteLine("Problem: more than one image with this resource");
						problems++;
					}
				}

				Console.WriteLine("{0} images created\n{1} images updated\n{2} problems",
					created, updated, problems);
			}

			MemberUserGroup facultygroup = null;
			AuthenticatedUserGroup defaultgroup = null;
			foreach (UserGroup group in UserGroup.GetUserGroups())
			{
				if (group.Title == "Faculty" && group is MemberUserGroup)
					facultygroup = (MemberUserGroup)group;
				else if (group is AuthenticatedUserGroup)
					defaultgroup = (AuthenticatedUserGroup)group;
			}

			if (!skipusers)
			{
				// creating groups
				if (facultygroup == null)
				{
					facultygroup = new MemberUserGroup();
					facultygroup.Title = "Faculty";
					facultygroup.Update();
					Console.WriteLine("Created user group: Faculty");
				}
				else
					Console.WriteLine("Found user group: Faculty");
				if (defaultgroup == null)
				{
					defaultgroup = new AuthenticatedUserGroup();
					defaultgroup.Title = "Authenticated Users";
					defaultgroup.Update();
					Console.WriteLine("Created user group: Authenticated Users");
				}
				else
					Console.WriteLine("Found user group: Authenticated Users");

				// copy users

				XmlNodeList nodelist = doc.SelectNodes("/mdid1databasedump/Users/User");

				foreach (XmlNode node in nodelist)
				{
					string login = null;
					string xpassword = null;
					string firstname = null;
					string lastname = null;
					string type = null;
					foreach (XmlNode child in node.ChildNodes)
						switch (child.Name)
						{
							case "UserID":
								login = child.InnerText.Trim();
								break;
							case "Password":
								xpassword = child.InnerText.Trim();
								break;
							case "FName":
								firstname = child.InnerText.Trim();
								break;
							case "LName":
								lastname = child.InnerText.Trim();
								break;
							case "Type":
								type = child.InnerText.Trim();
								break;
						}

					if (login != null && lastname != null)
					{
						User user = User.GetByLogin(login);
						if (user != null)
							Console.WriteLine("Found user {0}", login);
						else 
						{
							user = new User();
							user.Login = login;
							user.LastName = lastname;
							user.FirstName = firstname;
							if (emailext != null && emailext.Length > 0)
							{
								if (login.IndexOf("@") < 0)
									user.Email = login + "@" + emailext;
								else
									user.Email = login;
							}
							user.Update();
							if (xpassword != null && xpassword.Length > 0)
								user.SetPassword(xpassword);
							switch (type)
							{
								case "2":
									facultygroup.Add(user);
									break;
								case "3":
									user.Administrator = true;
									user.Update();
									break;
							}
							Console.WriteLine("Created user {0}", login);
						}
					}
				}
			}

			if (!skipshows)
			{
				XmlNodeList nodelist = doc.SelectNodes("/mdid1databasedump/Slideshows/Slideshow");
				
				foreach (XmlNode node in nodelist)
				{
					Console.Write("Processing slideshow {0}: ", node.Attributes["SlideshowTitle"].Value);
					User user = User.GetByLogin(node.Attributes["OwnerID"].Value);
					if (user == null)
					{
						Console.WriteLine("User not found");
						continue;
					}
					SortedList folders = user.GetFolders();
					int folder;
					if (folders.ContainsKey("MDID1 Slideshows"))
						folder = (int)folders["MDID1 Slideshows"];
					else
						folder = user.CreateFolder("MDID1 Slideshows");
					Slideshow[] shows = Slideshow.GetByFolder(user.ID, folder);

					bool found = false;
					if (shows != null)
						foreach (Slideshow s in shows)
							if (s.Title == node.Attributes["SlideshowTitle"].Value)
							{
								found = true;
								break;
							}
					if (found)
					{
						Console.WriteLine("already exists");
						continue;
					}
					Slideshow show = new Slideshow();
					show.Title = node.Attributes["SlideshowTitle"].Value;
					show.ArchiveFlag = (node.Attributes["Archived"].Value == "1");
					show.Owner = user.ID;
					show.Folder = folder;

					foreach (XmlNode slidenode in node.SelectNodes("Slides/Slide"))
					{
						ArrayList images = collection.GetImagesByResource(slidenode.Attributes["Image"].Value + ".jpg");
						if (images != null && images.Count == 1)
						{
							Slide slide = new Slide();
							slide.ImageID = ((Image)images[0]).ID;
							slide.Scratch = false;
							XmlNodeList commentlist = slidenode.SelectNodes("Comment");
							if (commentlist != null && commentlist.Count == 1)
								foreach (XmlNode comment in commentlist)
									slide.Annotation = comment.InnerText.Trim();
							show.Add(slide);
						}
					}

					show.Update();
					if (defaultgroup != null)
						show.GetAccessControl().AddUserGroupPrivilege(Privilege.ViewSlideshow, Privilege.None, defaultgroup.ID);
					Console.WriteLine("created ({0} slides)", show.GetSlides().Length);
				}
			}
		}

		

		[STAThread]
		static void Main(string[] args)
		{
			Import import = new Import();
			import.Run(args);
		}
	}
}
