using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Immutable;

public class ImmutableDefaultDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IString {
  public readonly TValue defaultValue;
  public readonly ImmutableDictionary<TKey, TValue> dictionary;
  
  public ImmutableDefaultDictionary(TValue defaultValue) {
    this.defaultValue = defaultValue;
    this.dictionary = ImmutableDictionary<TKey, TValue>.Empty;
  }

  public ImmutableDefaultDictionary(TValue defaultValue, ImmutableDictionary<TKey, TValue> dictionary) {
    this.defaultValue = defaultValue;
    this.dictionary = dictionary;
  }

  public TValue this[TKey key] {
    get => dictionary.GetOrDefault(key, defaultValue);
  }

  public ImmutableDefaultDictionary<TKey, TValue> Add(TKey key, TValue value)
    => new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.Add(key, value));

  public ImmutableDefaultDictionary<TKey, TValue> SetItem(TKey key, TValue value)
    => new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.SetItem(key, value));

  public ImmutableDefaultDictionary<TKey, TValue> Remove(TKey key)
    => new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, dictionary.Remove(key));

  public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dictionary.GetEnumerator();

  IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator();

  public ImmutableDefaultDictionaryLens<
           TKey,
           TValue,
           ImmutableDefaultDictionary<TKey, TValue>>
    lens {
      get => this.ChainLens(x => x);
    }

  public override string ToString()
    => "ImmutableDefaultDictionary {\n"
       + this.Select(kvp => (ks: kvp.Key.ToString(),
                             vs: kvp.Value.ToString()))
          .OrderBy(p => p.ks)
          .Select(p => $"{{ {p.ks}, {p.vs} }}")
          .JoinWith(",\n")
       + "\n}";

  public string Str() => ToString();
}

public static class ImmutableDefaultDictionaryExtensionMethods {
  public static ImmutableDefaultDictionary<UKey, UValue> ToImmutableDefaultDictionary<T, UKey, UValue>(this IEnumerable<T> e, UValue defaultValue, Func<T, UKey> key, Func<T, UValue> value)
    => new ImmutableDefaultDictionary<UKey, UValue>(defaultValue, e.ToImmutableDictionary(key, value));

  public static ImmutableDefaultDictionary<TKey, TValue> ToImmutableDefaultDictionary<TKey, TValue>(this ImmutableDictionary<TKey, TValue> d, TValue defaultValue)
    => new ImmutableDefaultDictionary<TKey, TValue>(defaultValue, d);
}