using System;
using System.Text.RegularExpressions;
using System.IO;

namespace Orciid.Core
{
	/// <summary>
	/// Resource Path
	/// </summary>
	/// <remarks>
	/// A resource path is a path name pointing to a directory holding all the image
	/// resources for a collection.
	/// </remarks>
	public class ResourcePath
	{
		private string resourcepath;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <remarks>
		/// Initializes the resource path object with a given resource path string.
		/// </remarks>
		/// <param name="rp">The resource path as a string</param>
		public ResourcePath(string rp)
		{
			if (rp == null || rp.Length == 0)
				throw new CoreException("Resource path must not be null or empty");
			if (rp.Length > 255)
				throw new CoreException("Resource path cannot be longer than 255 characters");
			resourcepath = rp;
		}

		/// <summary>
		/// Resource path as string
		/// </summary>
		/// <remarks>
		/// Converts the resource path back to a string for storage
		/// </remarks>
		/// <returns>Resource path as string</returns>
		public override string ToString()
		{
			return resourcepath;
		}

		/// <summary>
		/// Physical root
		/// </summary>
		/// <remarks>
		/// Determines the physical root for the resource path.  For most paths, this
		/// will be the resource path itself.  Some paths have placeholders, which allows
		/// the image directories to be organized further, in this case the physical root
		/// would be the resource path up to the first placeholder.
		/// </remarks>
		/// <value>
		/// The physical root of the resource path
		/// </value>
		public string PhysicalRoot
		{
			get
			{
				string r;
				int p = resourcepath.IndexOf("*");
				if (p < 0)
					r = resourcepath;
				else
					r = resourcepath.Substring(0, p - 1);
				if (!r.EndsWith(Path.DirectorySeparatorChar.ToString()) &&
					!r.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
					r += Path.DirectorySeparatorChar;
				if (Directory.Exists(r))
					return r;
				return "";
			}
		}

		/// <summary>
		/// Create image directories
		/// </summary>
		/// <remarks>
		/// This method creates three directories under the directory specified by the
		/// resource path called "full", "medium" and "thumb".  If the directories already
		/// exist, cannot be created, or if the resource path includes placeholders, the
		/// method returns without doing anything.
		/// </remarks>
		public void CreateImageDirectories()
		{
			// do not create directories for resource paths that contain variables
			// since those depend on the resource of each individual image
			if (resourcepath.IndexOf('*') >= 0)
				return;
			try
			{
				Directory.CreateDirectory(resourcepath);
				Directory.CreateDirectory(Path.Combine(resourcepath, "full"));
				Directory.CreateDirectory(Path.Combine(resourcepath, "medium"));
				Directory.CreateDirectory(Path.Combine(resourcepath, "thumb"));
			}
			catch
			{
			}
		}
	}

	/// <summary>
	/// Resource path parser
	/// </summary>
	/// <remarks>
	/// This class is used to parse placeholders in a resource path.  Valid placeholders
	/// in a resource path are:
	/// <list type="bullet">
	/// <item>*FORMAT*: Will be replaced by the format string ("full", "medium" or "thumb")</item>
	/// <item>*RESOURCE*: Will be replaced by the image resource (usually the image file name)</item>
	/// <item>*PREFIX <c>#</c>*: Use a number in place of the number sign <c>#</c>. Will be replaced
	/// by the first <c>#</c> characters of the image resource.</item>
	/// </list>
	/// Resource paths are stored in the global database, but can be remapped using the local
	/// configuration file in a multiple server installation.  Add a content.mapresourcepath
	/// entry with the path-prefix used in the database and the actual path-prefix separated by a
	/// comma. Example: <code>&lt;mapresourcepath&gt;c:\images\,\\server\images\&lt;/mapresourcepath&gt;</code>
	/// would map a resource path stored in the database of <c>c:\images\collection</c> to
	/// <c>\\server\images\collection</c>.
	/// </remarks>
	public class ResourcePathParser
	{
		private string resourcepath;
		private string resource;
		private string formatsubdir;

		private string ReplaceResourcePathVariables(Match m)
		{
			if (m.Value == "*FORMAT*")
				return formatsubdir;
			if (m.Value == "*RESOURCE*")
				return resource;
			if (m.Value.StartsWith("*PREFIX "))
			{
				try
				{
					int c = Int32.Parse(m.Value.Substring(8, m.Value.Length - 9));
					if (c > resource.Length)
						return resource;
					else
						return resource.Substring(0, c);
				}
				catch (FormatException)
				{
					return m.Value;
				}
			}
			else
				return m.Value;
		}

		/// <summary>
		/// Parse resource path
		/// </summary>
		/// <remarks>
		/// Replaces placeholders in the resource path with their actual values and returns
		/// a physical path.
		/// </remarks>
		/// <param name="resourcepath">The resource path to parse</param>
		/// <param name="resource">The image resource to be resolved</param>
		/// <param name="format">The image format used for resolution</param>
		/// <param name="useformat">If <c>false</c>, no image format will be used when
		/// resolving the path.  Useful for extended resources.</param>
		/// <returns>The resolved path to the given resource.</returns>
		public string Parse(ResourcePath resourcepath, string resource, ImageSize format, bool useformat)
		{
			this.resourcepath = resourcepath.ToString();
			this.resource = resource;

			if (Configuration.Instance.ContainsKey("content.mapresourcepath"))
				foreach (string map in Configuration.Instance.GetMultiString("content.mapresourcepath"))
				{
					string[] parts = map.Split(',');
					if (parts.Length != 2)
						throw new CoreException("Invalid resource path mapping in configuration file");
					if (this.resourcepath.StartsWith(parts[0]))
					{
						this.resourcepath = parts[1] + this.resourcepath.Substring(parts[0].Length);
						break;
					}
				}

			if (useformat)
			{
				switch (format)
				{
					case ImageSize.Full:
						formatsubdir = "full";
						break;
					case ImageSize.Medium:
						formatsubdir = "medium";
						break;
					case ImageSize.Thumbnail:
						formatsubdir = "thumb";
						break;
					default:
						return null;
				}
			}
			else
				formatsubdir = "";
			Regex regex = new Regex(@"(\*[^*]+\*)");
			if (regex.IsMatch(this.resourcepath))
				return regex.Replace(this.resourcepath, 
					new MatchEvaluator(ReplaceResourcePathVariables));
			else 
				return Path.Combine(Path.Combine(this.resourcepath, formatsubdir), resource);
		}
	}
}
