using Lookup.Interface;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Lookup.Abstract;

/// <summary>
/// Basic <see cref="ILookup{TKey,TValue}"/> implementation
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public abstract class Lookup<TKey, TValue>
  : Dictionary<TKey, ICollection<TValue>>,
    ILookup<TKey, TValue>
  where TKey : notnull
{
  #region Constructor

  /// <inheritdoc />
  protected Lookup()
  {
  }

  /// <inheritdoc />
  protected Lookup(IDictionary<TKey, ICollection<TValue>> dictionary) : base(dictionary)
  {
  }

  /// <inheritdoc />
  protected Lookup(
    IDictionary<TKey, ICollection<TValue>> dictionary, IEqualityComparer<TKey>? comparer
  ) : base(dictionary, comparer)
  {
  }

  /// <inheritdoc />
  protected Lookup(IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection) :
    base(collection)
  {
  }

  /// <inheritdoc />
  protected Lookup(
    IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection,
    IEqualityComparer<TKey>? comparer
  ) : base(collection, comparer)
  {
  }

  /// <inheritdoc />
  protected Lookup(IEqualityComparer<TKey>? comparer) : base(comparer)
  {
  }

  /// <inheritdoc />
  protected Lookup(int capacity) : base(capacity)
  {
  }

  /// <inheritdoc />
  protected Lookup(int capacity, IEqualityComparer<TKey>? comparer) : base(capacity, comparer)
  {
  }

  /// <inheritdoc />
  protected Lookup(SerializationInfo info, StreamingContext context) : base(info, context)
  {
  }

  #endregion

  #region Implementation of IDictionary<TKey,ICollection<TValue>>

  /// <inheritdoc />
  public void Add(TKey key, TValue value)
  {
    var collection = TryGetValue(key, out var values) ? values : Add(key);
    collection.Add(value);
  }

  /// <inheritdoc />
  public void AddRange(TKey key, IEnumerable<TValue> values)
  {
    var collection = TryGetValue(key, out var existingCollection)
      ? existingCollection
      : Add(key);

    foreach (var value in values)
    {
      collection.Add(value);
    }
  }

  /// <inheritdoc />
  public bool Remove(TKey key, TValue value)
  {
    if (!TryGetValue(key, out var values))
    {
      return false;
    }

    values.Remove(value);
    return true;
  }

  /// <inheritdoc />
  public ICollection<TValue> GetOrAdd(TKey key)
  {
    return TryGetValue(key, out var values)
      ? values
      : Add(key);
  }

  #endregion

  #region Implementation of ILookup<TKey,TValue>

  /// <inheritdoc />
  public abstract ICollection<TValue> Add(TKey key);

  #endregion

  #region Implementation of IDisposable

  /// <inheritdoc cref="Dispose()"/>
  protected virtual void Dispose(bool disposing)
  {
  }

  /// <inheritdoc />
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  #endregion
}
