using System;
using System.IO;
using Orciid.Media.Util;
using Orciid.Media.Converters;

namespace Orciid.Media
{
	public class MediaRepository
	{
		private string rootdirectory;
		private MediaCache cache;

		private static char[] pathseperators = 
			new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };

		public readonly string DefaultDirectory = "__repository__";

		public MediaRepository(string rootdir)
		{
			rootdirectory = rootdir.TrimEnd(pathseperators);
			cache = new DiskMediaCache(Path.Combine(rootdirectory, "__cache__"));
		}

		public string RootDirectory
		{
			get
			{
				return rootdirectory;
			}
		}

		public MediaCache Cache
		{
			get
			{
				return cache;
			}
		}

		public void StoreFile(string filename, string saveas)
		{
			StoreFile(File.OpenRead(filename), saveas, DefaultDirectory);
		}

		public void StoreFile(string filename, string saveas, string subdirectory)
		{
			StoreFile(File.OpenRead(filename), saveas, subdirectory);
		}

		public void StoreFile(Stream stream, string saveas)
		{
			StoreFile(stream, saveas, DefaultDirectory);
		}

		public void StoreFile(Stream stream, string saveas, string subdirectory)
		{
			if (!IsValidFileName(saveas))
				throw new ArgumentException("Invalid file name", "saveas");
			if (!IsValidSubdirectory(subdirectory))
				throw new ArgumentException("Invalid subdirectory", "subdirectory");
			string pathout = Path.Combine(rootdirectory, subdirectory);
			if (!Directory.Exists(pathout))
				Directory.CreateDirectory(pathout);
			string fileout = Path.Combine(pathout, saveas);
			using (Stream outstream = File.Create(fileout))
				Tools.CopyStream(stream, outstream);
		}

		public Stream RetrieveFile(string filename)
		{
			return RetrieveFile(filename, DefaultDirectory);
		}

		public Stream RetrieveFile(string filename, string subdirectory)
		{
			if (!IsValidFileName(filename))
				throw new ArgumentException("Invalid file name", "saveas");
			if (!IsValidSubdirectory(subdirectory))
				throw new ArgumentException("Invalid subdirectory", "subdirectory");
			return File.OpenRead(Path.Combine(Path.Combine(rootdirectory, subdirectory), filename));
		}

		public Stream RetrieveFile(string filename, string subdirectory, string mimetype)
		{
			if (MimeType.GetFromExtension(filename) == mimetype)
				return RetrieveFile(filename, subdirectory);
			else
				return RetrieveFile(filename, subdirectory, mimetype, Parameters.Default);
		}

		public Stream RetrieveFile(string filename, string subdirectory, string mimetype, 
			Parameters parameters)
		{
			if (subdirectory == null || subdirectory.Length == 0)
				subdirectory = DefaultDirectory;
			if (!IsValidFileName(filename))
				throw new ArgumentException("Invalid file name", "saveas");
			if (!IsValidSubdirectory(subdirectory))
				throw new ArgumentException("Invalid subdirectory", "subdirectory");
			string masterfilename = Path.Combine(Path.Combine(rootdirectory, subdirectory), filename);
			string cachefilename = GetCacheFileName(filename, subdirectory, mimetype, parameters);
			Stream cached = cache.ReadFile(cachefilename, File.GetLastWriteTime(masterfilename));
			if (cached != null)
				return cached;
			string tempfile = Tools.GetTempFileName(MimeType.GetExtension(mimetype));
			try
			{
				if (!MediaConverter.Convert(masterfilename, tempfile, parameters))
					return null;
				using (Stream tempstream = File.OpenRead(tempfile))
					using (cached = cache.WriteFile(cachefilename))
						Tools.CopyStream(tempstream, cached);
			}
			finally
			{
				File.Delete(tempfile);
			}
			return cache.ReadFile(cachefilename, File.GetLastWriteTime(masterfilename));
		}

		internal string GetCacheFileName(string filename, string subdirectory, string mimetype, 
			Parameters parameters)
		{
			string s = String.Format("{0}_{1}.{2}",
				Path.Combine(subdirectory, filename),
				parameters.ToString(),
				MimeType.GetExtension(mimetype));
			s = s.Replace(Path.DirectorySeparatorChar, '_');
			s = s.Replace(Path.AltDirectorySeparatorChar, '_');
			return s;
		}

		public bool FileExists(string filename)
		{
			return FileExists(filename, DefaultDirectory);
		}

		public bool FileExists(string filename, string subdirectory)
		{
			return IsValidFileName(filename) &&
				IsValidSubdirectory(subdirectory) &&
				File.Exists(Path.Combine(Path.Combine(rootdirectory, subdirectory), filename));
		}

		public bool IsValidFileName(string filename)
		{
			return (Path.GetFileName(filename) == filename);
		}

		public bool IsValidSubdirectory(string subdirectory)
		{
			if (Path.IsPathRooted(subdirectory))
				return false;
			string effective = Path.GetFullPath(Path.Combine(rootdirectory, subdirectory));
			if (effective.Length < rootdirectory.Length)
				return false;
			int i = effective.IndexOfAny(pathseperators, rootdirectory.Length);
			if (i >= 0)
				effective = effective.Substring(0, i);
			return (rootdirectory == effective);
		}
	}
}
