using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace miew.ReadOnly
{
	public static class Extensions
	{
		static public IReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TSrc, TKey, TValue>(
			this IEnumerable<TSrc> ie,
			Func<TSrc, TKey> key_selector,
			Func<TSrc, TValue> value_selector)
		{
			return new ReadOnlyDictionary<TKey, TValue>(ie.ToDictionary(key_selector, value_selector));
		}
		static public IReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IDictionary<TKey, TValue> d)
		{
			return new ReadOnlyDictionary<TKey, TValue>(d);
		}
		static public IReadOnlyCollection<T> ToReadOnlyCollection<T>(this IEnumerable<T> seq)
		{
			return new ReadOnlyCollection<T>(seq);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public struct DeferredCollection<T> : ICollection<T>
	{
		int c;
		IEnumerable<T> ie;
		public DeferredCollection(int c, IEnumerable<T> ie)
		{
			this.c = c;
			this.ie = ie;
		}

		public bool Contains(T item) { return ie.Contains(item); }

		public void CopyTo(T[] array, int arrayIndex)
		{
			foreach (T t in ie)
				array[arrayIndex++] = t;
		}

		public void Add(T item) { throw new NotImplementedException(); }
		public void Clear() { throw new NotImplementedException(); }
		public int Count { get { return c; } }
		public bool IsReadOnly { get { return true; } }
		public bool Remove(T item) { throw new NotImplementedException(); }
		public IEnumerator<T> GetEnumerator() { return ie.GetEnumerator(); }
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return ie.GetEnumerator();
		}
	};

	public class ReadOnlyCollection<T> : IReadOnlyCollection<T>, ICollection<T>
	{
		T[] arr;
		public ReadOnlyCollection(IEnumerable<T> seq)
		{
			this.arr = seq as T[] ?? seq.ToArray();
		}

		public int Count { get { return arr.Length; } }
		public T this[int ix] { get { return arr[ix]; } }
		public int IndexOf(T item) { return System.Array.IndexOf<T>(arr, item); }
		public bool Contains(T item) { return System.Array.IndexOf<T>(arr, item) != -1; }
		public void CopyTo(T[] array, int arrayIndex) { arr.CopyTo(array, arrayIndex); }

		public IEnumerator<T> GetEnumerator() { return arr.AsEnumerable<T>().GetEnumerator(); }

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return arr.AsEnumerable<T>().GetEnumerator();
		}
		public static explicit operator T[](ReadOnlyCollection<T> ro)
		{
			return ro.arr;
		}
		public static explicit operator ReadOnlyCollection<T>(T[] seq)
		{
			return new ReadOnlyCollection<T>(seq);
		}

		public bool IsReadOnly { get { return true; } }
		public void Add(T item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(T item) { throw new InvalidOperationException(); }
	};

	public interface IReadOnlyCollection<T> : IEnumerable<T>
	{
		bool Contains(T item);
		void CopyTo(T[] array, int arrayIndex);
		int Count { get; }
		T this[int ix] { get; }
		int IndexOf(T item);
	};

	public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
	{
		bool ContainsKey(TKey key);

		ICollection<TKey> Keys { get; }

		ICollection<TValue> Values { get; }

		int Count { get; }

		bool IsReadOnly { get; }

		bool TryGetValue(TKey key, out TValue value);

		TValue this[TKey key] { get; }
	}

	public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
	{
		static ReadOnlyDictionary<TKey, TValue> _e = null;
		public static ReadOnlyDictionary<TKey, TValue> Empty { get { return _e = _e ?? new ReadOnlyDictionary<TKey, TValue>(); } }

		private IDictionary<TKey, TValue> _dict;

		public ReadOnlyDictionary()
		{
			_dict = new Dictionary<TKey, TValue>();
		}

		public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
		{
			_dict = dictionary;
		}

		public bool ContainsKey(TKey key)
		{
			return _dict.ContainsKey(key);
		}

		public ICollection<TKey> Keys
		{
			get { return _dict.Keys; }
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			return _dict.TryGetValue(key, out value);
		}

		public ICollection<TValue> Values
		{
			get { return _dict.Values; }
		}

		public TValue this[TKey key]
		{
			get { return _dict[key]; }
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			return _dict.Contains(item);
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
			_dict.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get { return _dict.Count; }
		}

		public bool IsReadOnly
		{
			get { return true; }
		}

		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			return _dict.GetEnumerator();
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	};

	public struct CovariantCollectionWrapper<TOut, TIn> : ICollection<TOut> where TIn : TOut
	{
		readonly ICollection<TIn> coll;

		public CovariantCollectionWrapper(ICollection<TIn> coll)
		{
			this.coll = coll;
		}

		public bool Contains(TOut item)
		{
			return item is TIn && coll.Contains((TIn)item);
		}

		public void CopyTo(TOut[] array, int arrayIndex)
		{
			foreach (TOut t in this)
				array[arrayIndex++] = t;
		}

		public int Count { get { return coll.Count; } }

		public bool IsReadOnly { get { return true; } }

		public IEnumerator<TOut> GetEnumerator()
		{
			foreach (TIn t in coll)
				yield return t;
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
		public void Add(TOut item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(TOut item) { throw new InvalidOperationException(); }
	};

	public struct CovariantListWrapper<TOut, TIn> : IList<TOut> where TIn : TOut
	{
		readonly IList<TIn> list;

		public CovariantListWrapper(IList<TIn> list)
		{
			this.list = list;
		}

		public int IndexOf(TOut item)
		{
			// (not covariant but permitted)
			return item is TIn ? list.IndexOf((TIn)item) : -1;
		}

		public TOut this[int index]
		{
			get { return list[index]; }
			set { throw new InvalidOperationException(); }
		}

		public bool Contains(TOut item)
		{
			// (not covariant but permitted)
			return item is TIn && list.Contains((TIn)item);
		}

		public void CopyTo(TOut[] array, int arrayIndex)
		{
			foreach (TOut t in this)
				array[arrayIndex++] = t;
		}

		public int Count { get { return list.Count; } }

		public bool IsReadOnly { get { return true; } }

		public IEnumerator<TOut> GetEnumerator()
		{
			foreach (TIn t in list)
				yield return t;
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public void Insert(int index, TOut item) { throw new InvalidOperationException(); }
		public void RemoveAt(int index) { throw new InvalidOperationException(); }
		public void Add(TOut item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(TOut item) { throw new InvalidOperationException(); }
	}

	public class CovariantDictionaryWrapper<TKey, TOut, TIn> : IReadOnlyDictionary<TKey, TOut> where TIn : TOut
	{
		private IDictionary<TKey, TIn> _dict;
		private ICollection<TOut> values = null;

		public CovariantDictionaryWrapper(IDictionary<TKey, TIn> dictionary)
		{
			_dict = dictionary;
		}

		public bool ContainsKey(TKey key)
		{
			return _dict.ContainsKey(key);
		}

		public ICollection<TKey> Keys
		{
			get { return _dict.Keys; }
		}

		public bool TryGetValue(TKey key, out TOut value)
		{
			TIn v;
			if (!_dict.TryGetValue(key, out v))
			{
				value = default(TOut);
				return false;
			}
			value = v;
			return true;
		}

		public ICollection<TOut> Values
		{
			get { return values = values ?? new CovariantCollectionWrapper<TOut, TIn>(_dict.Values); }
		}

		public TOut this[TKey key]
		{
			get { return _dict[key]; }
		}

		public bool Contains(KeyValuePair<TKey, TOut> item)
		{
			if (!(item.Value is TIn))
				return false;
			return _dict.Contains(new KeyValuePair<TKey, TIn>(item.Key, (TIn)item.Value));
		}

		public void CopyTo(KeyValuePair<TKey, TOut>[] array, int arrayIndex)
		{
			foreach (var kvp in this)
				array[arrayIndex++] = kvp;
		}

		public int Count
		{
			get { return _dict.Count; }
		}

		public bool IsReadOnly
		{
			get { return true; }
		}

		public IEnumerator<KeyValuePair<TKey, TOut>> GetEnumerator()
		{
			foreach (var kvp in _dict)
				yield return new KeyValuePair<TKey, TOut>(kvp.Key, kvp.Value);
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	};
}