﻿using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Epic.Monads;
using LiveLinq.Core;
using MoreCollections;

namespace LiveLinq.Dictionary
{
    internal class DictionaryChange<TKey, TValue> : IDictionaryChange<TKey, TValue>
    {
        private readonly ImmutableDictionary<TKey, TValue> items;

        public CollectionChangeType Type { get; }
        public IReadOnlyList<TKey> Keys { get; }
        public IReadOnlyList<TValue> Values { get; }
        public IReadOnlyList<IKeyValuePair<TKey, TValue>> Items { get; }

        public virtual bool IsEffectivelyStrict => Type == CollectionChangeType.Add || Keys.Count == 0 || Values.Any();

        public IMaybe<TValue> this[TKey key]
        {
            get
            {
                if (items.ContainsKey(key))
                    return Epic.Monads.Utility.Something(items[key]);
                return Epic.Monads.Utility.Nothing<TValue>();
            }
        }

        public DictionaryChange(CollectionChangeType type, ImmutableDictionary<TKey, TValue> items)
        {
            this.items = items;
            Keys = items.Keys.ToImmutableList();
            Values = items.Values.ToImmutableList();
            Items = items.Select(kvp => MoreCollections.Utility.KeyValuePair(kvp.Key, kvp.Value)).ToImmutableList();
            Type = type;
        }

        public DictionaryChange(CollectionChangeType type, ImmutableList<TKey> keys)
        {
            items = ImmutableDictionary<TKey, TValue>.Empty;
            Keys = keys;
            Values = ImmutableList<TValue>.Empty;
            Items = ImmutableList<IKeyValuePair<TKey, TValue>>.Empty;
            Type = type;
        }

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

        public IEnumerable<IDictionaryChange<TKey, TValue>> Itemize()
        {
            foreach (var item in Items)
            {
                yield return new DictionaryChange<TKey, TValue>(Type, ImmutableDictionary<TKey, TValue>.Empty.Add(item.Key, item.Value));
            }
        }
    }
}
