using System.Collections;
using System.Collections.Generic;

namespace KitsuneCafe.Sys.Collections
{
  public class BiDictionary<TKey, TValue>
    : IDictionary<BiDictionary<TKey, TValue>.Left, BiDictionary<TKey, TValue>.Right>,
     IDictionary<BiDictionary<TKey, TValue>.Right, BiDictionary<TKey, TValue>.Left>
  {
    public record Left(TKey Value)
    {
      public static implicit operator Left(TKey key) => new(key);
      public static implicit operator TKey(Left left) => left.Value;
    }

    public record Right(TValue Value)
    {
      public static implicit operator Right(TValue value) => new(value);
      public static implicit operator TValue(Right right) => right.Value;
    }

    private readonly Dictionary<Left, Right> forward = new();
    private readonly Dictionary<Right, Left> reverse = new();

    public ICollection<Left> Keys => ((IDictionary<Left, Right>)forward).Keys;

    public ICollection<Right> Values => ((IDictionary<Left, Right>)forward).Values;

    public int Count => ((ICollection<KeyValuePair<Left, Right>>)forward).Count;

    public bool IsReadOnly => ((ICollection<KeyValuePair<Left, Right>>)forward).IsReadOnly;

    ICollection<Right> IDictionary<Right, Left>.Keys => ((IDictionary<Right, Left>)reverse).Keys;

    ICollection<Left> IDictionary<Right, Left>.Values => ((IDictionary<Right, Left>)reverse).Values;

    public Left this[Right key] { get => ((IDictionary<Right, Left>)reverse)[key]; set => ((IDictionary<Right, Left>)reverse)[key] = value; }
    public Right this[Left key] { get => ((IDictionary<Left, Right>)forward)[key]; set => ((IDictionary<Left, Right>)forward)[key] = value; }

    public void Add(Left key, Right value)
    {
      ((IDictionary<Left, Right>)forward).Add(key, value);
    }

    public bool ContainsKey(Left key)
    {
      return ((IDictionary<Left, Right>)forward).ContainsKey(key);
    }

    public bool Remove(Left key)
    {
      return ((IDictionary<Left, Right>)forward).Remove(key);
    }

    public bool TryGetValue(Left key, out Right value)
    {
      return ((IDictionary<Left, Right>)forward).TryGetValue(key, out value);
    }

    public void Add(KeyValuePair<Left, Right> item)
    {
      ((ICollection<KeyValuePair<Left, Right>>)forward).Add(item);
    }

    public void Clear()
    {
      ((ICollection<KeyValuePair<Left, Right>>)forward).Clear();
    }

    public bool Contains(KeyValuePair<Left, Right> item)
    {
      return ((ICollection<KeyValuePair<Left, Right>>)forward).Contains(item);
    }

    public void CopyTo(KeyValuePair<Left, Right>[] array, int arrayIndex)
    {
      ((ICollection<KeyValuePair<Left, Right>>)forward).CopyTo(array, arrayIndex);
    }

    public bool Remove(KeyValuePair<Left, Right> item)
    {
      return ((ICollection<KeyValuePair<Left, Right>>)forward).Remove(item);
    }

    public IEnumerator<KeyValuePair<Left, Right>> GetEnumerator()
    {
      return ((IEnumerable<KeyValuePair<Left, Right>>)forward).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return ((IEnumerable)forward).GetEnumerator();
    }

    public void Add(Right key, Left value)
    {
      ((IDictionary<Right, Left>)reverse).Add(key, value);
    }

    public bool ContainsKey(Right key)
    {
      return ((IDictionary<Right, Left>)reverse).ContainsKey(key);
    }

    public bool Remove(Right key)
    {
      return ((IDictionary<Right, Left>)reverse).Remove(key);
    }

    public bool TryGetValue(Right key, out Left value)
    {
      return ((IDictionary<Right, Left>)reverse).TryGetValue(key, out value);
    }

    public void Add(KeyValuePair<Right, Left> item)
    {
      ((ICollection<KeyValuePair<Right, Left>>)reverse).Add(item);
    }

    public bool Contains(KeyValuePair<Right, Left> item)
    {
      return ((ICollection<KeyValuePair<Right, Left>>)reverse).Contains(item);
    }

    public void CopyTo(KeyValuePair<Right, Left>[] array, int arrayIndex)
    {
      ((ICollection<KeyValuePair<Right, Left>>)reverse).CopyTo(array, arrayIndex);
    }

    public bool Remove(KeyValuePair<Right, Left> item)
    {
      return ((ICollection<KeyValuePair<Right, Left>>)reverse).Remove(item);
    }

    IEnumerator<KeyValuePair<Right, Left>> IEnumerable<KeyValuePair<Right, Left>>.GetEnumerator()
    {
      return ((IEnumerable<KeyValuePair<Right, Left>>)reverse).GetEnumerator();
    }
  }
}
