﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;

namespace OpenCube.IO.FileSystem.Overload
{
	internal class OverloadingFolder
		: OverloadingEntry<IFolder>, IFolder
	{
		[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "While not marked as pure, we know they are")]
		internal OverloadingFolder(OverloadingFolder parent, IEnumerable<IFolder> folders)
			: base(parent, folders)
		{
			Contract.Requires<ArgumentNullException>(folders != null, "At least one folder is required.");
			Contract.Requires(folders.Any(), "At least one folder is required.");
		}

		/// <summary>
		/// Gets the files in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of files.
		/// </returns>
		public IEnumerable<IFile> GetFiles()
		{
			// group by does keep source order. yes!
			// http://stackoverflow.com/questions/1452507/linq-to-objects-does-groupby-preserve-order-of-elements
			// http://msdn.microsoft.com/en-us/library/bb534304.aspx
			return
				from IFolder folder in Entries
				from IFile file in folder.GetFiles()
				where file != null
				group file by file.Name.ToUpperInvariant()
					into fileGroups
				select new OverloadingFile(this, fileGroups);
		}

		/// <summary>
		/// Gets the file with the given name.
		/// </summary>
		/// <param name="fileName">The name of the file to get.</param>
		/// <returns>The file with the given name, or <c>null</c> when it could not be found.</returns>
		public IFile GetFile(string fileName)
		{
			IEnumerable<IFile> files =
				from IFolder folder in Entries
				where folder != null
				let file = folder.GetFile(fileName)
				where file != null
				select file;

			return new OverloadingFile(this, files);
		}

		public IFile CreateFile(string fileName)
		{
			throw new NotSupportedException("Overloading file sytem is read-only");
		}

		public IFile GetOrCreateFile(string fileName)
		{
			throw new NotSupportedException("Overloading file sytem is read-only");
		}

		/// <summary>
		/// Gets the folders in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of folders.
		/// </returns>
		public IEnumerable<IFolder> GetFolders()
		{
			// group by does keep source order. yes!
			// http://stackoverflow.com/questions/1452507/linq-to-objects-does-groupby-preserve-order-of-elements
			// http://msdn.microsoft.com/en-us/library/bb534304.aspx
			return
				from IFolder folder in Entries
				from IFolder subFolder in folder.GetFolders()
				where subFolder != null
				group subFolder by subFolder.Name.ToUpperInvariant()
					into folderGroups
				select new OverloadingFolder(this, folderGroups);
		}

		/// <summary>
		/// Gets the folder with the given name.
		/// </summary>
		/// <param name="folderName">The name of the folder to get.</param>
		/// <returns>The folder with the given name, or <c>null</c> when it could not be found.</returns>
		public IFolder GetFolder(string folderName)
		{
			IEnumerable<IFolder> folders =
				from IFolder folder in Entries
				where folder != null
				select folder.GetFolder(folderName);

			return new OverloadingFolder(this, folders);
		}

		/// <summary>
		/// Creates the folder with the given name.
		/// </summary>
		/// <param name="folderName">The name of the folder to create.</param>
		/// <returns>The folder with the given name.</returns>
		/// <exception cref="NotSupportedException">Overloading file system is read-only</exception>
		public IFolder CreateFolder(string folderName)
		{
			throw new NotSupportedException("Overloading file system is read-only");
		}

		/// <summary>
		/// Gets or creates the folder with the given name.
		/// </summary>
		/// <param name="folderName">The name of the folder to get, or create.</param>
		/// <returns>The folder with the given name.</returns>
		/// <exception cref="NotSupportedException">Overloading file system is read-only</exception>
		public IFolder GetOrCreateFolder(string folderName)
		{
			throw new NotSupportedException("Overloading file system is read-only");
		}

		/// <summary>
		/// Gets the entires in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of items.
		/// </returns>
		public IEnumerable<IFileSystemEntry> GetEntries()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Gets the entry with the given name in this folder.
		/// </summary>
		/// <param name="entryName">Name of the entry to get.</param>
		/// <returns>The entry with the given name, or <c>null</c> when it could not be found.</returns>
		public IFileSystemEntry GetEntry(string entryName)
		{
			throw new NotImplementedException();
		}
	}
}