using System;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Collections;

namespace Orciid.Core
{
	/// <summary>
	/// Extended Image Resource
	/// </summary>
	/// <remarks>
	/// Extended resources allow more than JPEG files to be connected to an image
	/// record.  Each extended resource is represented by an XML file, which can
	/// contain references to other files, links, etc.
	/// </remarks>
	public class ExtendedResource
	{
		/// <summary>
		/// Display indicator
		/// </summary>
		/// <remarks>
		/// Determines if and how an individual item referenced in the extended resource is displayed.
		/// </remarks>
		public enum Display
		{
			/// <summary>
			/// Do not display item
			/// </summary>
			None,
			/// <summary>
			/// Use default display method
			/// </summary>
			Default,
			/// <summary>
			/// Use custom display method
			/// </summary>
			Custom
		}

		/// <summary>
		/// Individual item in extended resource
		/// </summary>
		/// <remarks>
		/// Each item in an extended resource has several properties
		/// that determine if, how, and what is displayed.
		/// </remarks>
		public struct Record
		{
			private ImageSize format;
			private Display display;
			private string type;
			private string image;
			private string link;
			private string data;
			private string label;

			internal Record(ImageSize format, Display display, string type, string image,
				string link, string data, string label)
			{
				this.format = format;
				this.display = display;
				this.type = type;
				this.image = image;
				this.link = link;
				this.data = data;
				this.label = label;
			}

			/// <summary>
			/// Image format
			/// </summary>
			/// <remarks>
			/// Determines if this item is a thumbnail, medium or full size 
			/// </remarks>
			/// <value>
			/// The format/size of the item
			/// </value>
			public ImageSize Format
			{
				get
				{
					return format;
				}
			}

			/// <summary>
			/// Display option
			/// </summary>
			/// <remarks>
			/// Determines if and how the item is displayed
			/// </remarks>
			/// <value>
			/// The display option for the item
			/// </value>
			public Display Display
			{
				get
				{
					return display;
				}
			}

			/// <summary>
			/// Item mime type
			/// </summary>
			/// <remarks>
			/// Determines the mime type of the item
			/// </remarks>
			/// <value>
			/// The item's mime type
			/// </value>
			public string Type
			{
				get
				{
					return type;
				}
			}

			/// <summary>
			/// Associated image
			/// </summary>
			/// <remarks>
			/// If the item represents a regular image resource, this property contains 
			/// the resource value
			/// </remarks>
			/// <value>
			/// The image resource to be used for this item
			/// </value>
			public string Image
			{
				get
				{
					return image;
				}
			}

			/// <summary>
			/// Hyperlink URL
			/// </summary>
			/// <remarks>
			/// If the item represents a hyperlink, this property contains the URL
			/// </remarks>
			/// <value>
			/// The URL of the hyperlink
			/// </value>
			public string Link
			{
				get
				{
					return link;
				}
			}

			/// <summary>
			/// Custom data
			/// </summary>
			/// <remarks>
			/// This property contains custom data for the item
			/// </remarks>
			/// <value>
			/// Custom data
			/// </value>
			public string Data
			{
				get
				{
					return data;
				}
			}

			/// <summary>
			/// Hyperlink label
			/// </summary>
			/// <remarks>
			/// If the item represents a hyperlink, this property contains the label
			/// </remarks>
			/// <value>
			/// The label of the hyperlink
			/// </value>
			public string Label
			{
				get
				{
					return label;
				}
			}
		}

		#region XSD
		private string ExtendedResourceSchema = @"
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified' attributeFormDefault='unqualified'>
	<xs:element name='resource'>
		<xs:complexType>
			<xs:sequence>
				<xs:element name='thumb'>
					<xs:complexType>
						<xs:sequence>
							<xs:element name='image' type='xs:string'/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
				<xs:element name='medium' type='sizedresource' minOccurs='0' maxOccurs='unbounded'/>
				<xs:element name='full' type='sizedresource' minOccurs='0' maxOccurs='unbounded'/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	<xs:complexType name='sizedresource'>
		<xs:sequence>
			<xs:element name='image' type='xs:string' minOccurs='0'/>
			<xs:element name='link' type='xs:string' minOccurs='0'/>
			<xs:element name='data' type='xs:string' minOccurs='0'/>
			<xs:element name='label' type='xs:string' minOccurs='0'/>
		</xs:sequence>
		<xs:attribute name='display' use='optional'>
			<xs:simpleType>
				<xs:restriction base='xs:string'>
					<xs:pattern value='default'/>
					<xs:pattern value='custom'/>
					<xs:pattern value='none'/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name='type' type='xs:string' use='optional'/>
	</xs:complexType>
</xs:schema>
";
		#endregion

		private bool isvalid = false;
		private XmlNode resource = null;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <remarks>
		/// Creates and initializes an extended resource object from an XML file
		/// </remarks>
		/// <param name="xmlfile">The XML file to load</param>
		public ExtendedResource(string xmlfile)
		{		
			XmlValidatingReader reader = null;
			try
			{
				reader = new XmlValidatingReader(
					new XmlTextReader(new StreamReader(xmlfile)));
				XmlSchemaCollection schemas = new XmlSchemaCollection();
				schemas.Add(null, new XmlTextReader(new StringReader(ExtendedResourceSchema)));
				reader.ValidationType = ValidationType.Schema;
				reader.Schemas.Add(schemas);
				XmlDocument doc = new XmlDocument();
				doc.Load(reader);
				resource = doc.DocumentElement;
				isvalid = true;
			}
			catch
			{
			}
			finally
			{
				if (reader != null)
					reader.Close();
			}
		}

		/// <summary>
		/// Validity indicator
		/// </summary>
		/// <remarks>
		/// If the extended resource XML file was not in the correct format, did not exist,
		/// or was unavailable for other reasons, this flag is <c>false</c>.
		/// </remarks>
		/// <value><c>true</c> if the extended resource was loaded successfully, 
		/// <c>false</c> otherwise</value>
		public bool IsValid
		{
			get
			{
				return isvalid;
			}
		}

		private XmlNodeList GetNodeList(ImageSize format)
		{
			XmlNodeList nodes = null;
			switch (format)
			{
				case ImageSize.Thumbnail:
					nodes = resource.SelectNodes("/resource/thumb");
					break;
				case ImageSize.Medium:
					nodes = resource.SelectNodes("/resource/medium");
					break;
				case ImageSize.Full:
					nodes = resource.SelectNodes("/resource/full");
					break;
			}
			return nodes;
		}

		/// <summary>
		/// Image resource
		/// </summary>
		/// <remarks>
		/// Requests the visual representation of the extended resource in a specified format
		/// </remarks>
		/// <param name="format">The format (thumbnail, medium, or full size) of the
		/// desired image resource</param>
		/// <returns>The image resource for the requested format, or an empty string
		/// if no image resource exists in that format</returns>
		public string GetImageResource(ImageSize format)
		{
			if (!isvalid)
				return "";
			foreach (XmlNode recnode in GetNodeList(format))
			{
				XmlNode node = recnode.SelectSingleNode("image");
				if (node != null && node.InnerText != null && node.InnerText.Length > 0)
					return node.InnerText;
			}
			return "";
		}

		/// <summary>
		/// All items
		/// </summary>
		/// <remarks>
		/// This method returns all items for the specified format for an extended resource
		/// </remarks>
		/// <param name="format">The desired format</param>
		/// <returns>An array of extended resource records in the specified format</returns>
		public Record[] GetRecords(ImageSize format)
		{
			if (!isvalid)
				return null;
			ArrayList list = new ArrayList();
			foreach (XmlNode node in GetNodeList(format))
			{
				Display display;
				switch (node.Attributes["display"].Value)
				{
					case "none":
						display = Display.None;
						break;
					case "custom":
						display = Display.Custom;
						break;
					default:
						display = Display.Default;
						break;
				}
				XmlAttribute type = node.Attributes["type"];
				XmlNode image = node.SelectSingleNode("image");
				XmlNode link = node.SelectSingleNode("link");
				XmlNode data = node.SelectSingleNode("data");
				XmlNode label = node.SelectSingleNode("label");
				list.Add(new Record(
					format, 
					display, 
					(type != null ? type.Value : null),
					(image != null ? image.InnerText : null),
					(link != null ? link.InnerText : null),
					(data != null ? data.InnerText : null),
					(label != null ? label.InnerText : null)
					));
			}
			return (Record[])list.ToArray(typeof(Record));
		}
	}
}
