using KaoriStudio.Core.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace KaoriStudio.Core.IO.FileSystem
{
	/// <summary>
	/// A wrapper for standard directories
	/// </summary>
	public struct StandardDirectoryInfo : IDirectoryInfo
	{
		/// <summary>
		/// The underlying directory
		/// </summary>
		public DirectoryInfo DirectoryInfo
		{
			get
			{
				return dir;
			}
		}

		private DirectoryInfo root;
		private DirectoryInfo dir;

		/// <summary>
		/// Creates a new StandardDirectoryInfo
		/// </summary>
		/// <param name="root">The root directory</param>
		/// <param name="dir">The directory</param>
		public StandardDirectoryInfo(DirectoryInfo root, DirectoryInfo dir)
		{
			this.root = root;
			this.dir = dir;
		}

		/// <summary>
		/// Whether this directory exists
		/// </summary>
		public bool Exists
		{
			get
			{
				return dir.Exists;
			}
		}

		/// <summary>
		/// The full name of this directory
		/// </summary>
		public string FullName
		{
			get
			{
				var rootName = root.FullName;
				var rootNameLength = rootName.Length;
				var dirName = dir.FullName.Replace('\\', '/');
				switch(rootName[rootNameLength])
				{
					case '/':
					case '\\':
						return dirName.Substring(rootNameLength);
					default:
						return dirName.Substring(rootNameLength + 1);
				}
			}
		}

		/// <summary>
		/// The file stamp
		/// </summary>
		public FileStamp FileStamp
		{
			get
			{
				return new FileStamp(dir.CreationTime, dir.LastWriteTime, dir.LastAccessTime);
			}
		}

		/// <summary>
		/// The name of this directory
		/// </summary>
		public string Name
		{
			get
			{
				return dir.Name;
			}
		}

		/// <summary>
		/// The parent of this directory
		/// </summary>
		public IDirectoryInfo Parent
		{
			get
			{
				return new StandardDirectoryInfo(root, dir.Parent);
			}
		}

		/// <summary>
		/// The root file system
		/// </summary>
		public IFileSystem Root
		{
			get
			{
				return new StandardFileSystem(root);
			}
		}

		/// <summary>
		/// Enumerates all the subdirectories
		/// </summary>
		/// <returns>The enumerable list of subdirectories</returns>
		public IEnumerable<IDirectoryInfo> EnumerateDirectories()
		{
			var lroot = root;
			return dir.EnumerateDirectories().Select(x => (IDirectoryInfo)new StandardDirectoryInfo(lroot, x));
		}

		/// <summary>
		/// Enumerates all the files
		/// </summary>
		/// <returns>The enumerable list of files</returns>
		public IEnumerable<IFileInfo> EnumerateFiles()
		{
			var ldir = dir;
			var lroot = root;
			return dir.EnumerateFiles().Select(x => (IFileInfo)new StandardFileInfo(lroot, ldir, x));
		}

		/// <summary>
		/// Enumerates all the files and directories
		/// </summary>
		/// <returns>The enumerable list of files and directories</returns>
		public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos()
		{
			var ldir = dir;
			var lroot = root;
			return dir.EnumerateFileSystemInfos()
				.Select(x =>
				{
					var fi = x as FileInfo;

					if (fi != null)
						return (IFileSystemInfo)new StandardFileInfo(lroot, ldir, fi);
					

					var di = x as DirectoryInfo;

					if (di != null)
						return (IFileSystemInfo)new StandardDirectoryInfo(lroot, di);

					return null;
				});
		}

		string RelativePath(string name)
		{
			return Path.Combine(dir.FullName,
					name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
		}

		/// <summary>
		/// Gets a subdirectory
		/// </summary>
		/// <param name="name">The directory name</param>
		/// <returns>The subdirectory</returns>
		public IDirectoryInfo GetDirectory(string name)
		{
			var di = new DirectoryInfo(RelativePath(name));
			if (!dir.IsAncestorOf(di))
				throw new FileNotFoundException();

			return new StandardDirectoryInfo(root, di);
		}

		/// <summary>
		/// Gets a file
		/// </summary>
		/// <param name="name">The file name</param>
		/// <returns>The file</returns>
		public IFileInfo GetFile(string name)
		{

			var fi = new FileInfo(RelativePath(name));
			if (!dir.IsAncestorOf(fi))
				throw new FileNotFoundException();

			return new StandardFileInfo(root, dir, fi);
		}
	}
}
