// ---------------------------------------------------------------------------
// BT.Framework
// ObservableDictionary.cs — 변경 이벤트를 내장한 Dictionary<TKey, TValue>
// ---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;

namespace BurningTimes.Core.Container
{
    /// <summary>
    /// Add/Remove/Update/Reset 이벤트를 인스턴스 단위로 발행하는 <see cref="IDictionary{TKey, TValue}"/>.
    /// </summary>
    /// <remarks>
    /// 설계 문서 §3-2. 기존 <c>UniDictionary</c> 상응 통합 대체.
    /// Updated 는 동일 키에 새 값이 대입될 때만 발행, 기존 키가 없던 경우에는 Added 로 처리.
    /// </remarks>
    public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private readonly Dictionary<TKey, TValue> _map;

        public event Action<TKey, TValue> Added;
        public event Action<TKey, TValue> Removed;
        /// <summary>동일 키에 새 값 대입 시 발행. 인자는 (key, oldValue, newValue).</summary>
        public event Action<TKey, TValue, TValue> Updated;
        public event Action Reset;

        public ObservableDictionary() { _map = new Dictionary<TKey, TValue>(); }
        public ObservableDictionary(int capacity) { _map = new Dictionary<TKey, TValue>(capacity); }
        public ObservableDictionary(IEqualityComparer<TKey> comparer) { _map = new Dictionary<TKey, TValue>(comparer); }

        public TValue this[TKey key]
        {
            get => _map[key];
            set
            {
                if (_map.TryGetValue(key, out var old))
                {
                    _map[key] = value;
                    Updated?.Invoke(key, old, value);
                }
                else
                {
                    _map[key] = value;
                    Added?.Invoke(key, value);
                }
            }
        }

        public ICollection<TKey> Keys => _map.Keys;
        public ICollection<TValue> Values => _map.Values;
        public int Count => _map.Count;
        public bool IsReadOnly => false;

        public void Add(TKey key, TValue value)
        {
            _map.Add(key, value);
            Added?.Invoke(key, value);
        }

        public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);

        public bool Remove(TKey key)
        {
            if (!_map.TryGetValue(key, out var old)) return false;
            _map.Remove(key);
            Removed?.Invoke(key, old);
            return true;
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            if (!_map.TryGetValue(item.Key, out var existing) ||
                !EqualityComparer<TValue>.Default.Equals(existing, item.Value)) return false;
            return Remove(item.Key);
        }

        public void Clear()
        {
            if (_map.Count == 0) return;
            _map.Clear();
            Reset?.Invoke();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
            => _map.TryGetValue(item.Key, out var v) && EqualityComparer<TValue>.Default.Equals(v, item.Value);

        public bool ContainsKey(TKey key) => _map.ContainsKey(key);

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
            => ((ICollection<KeyValuePair<TKey, TValue>>)_map).CopyTo(array, arrayIndex);

        public bool TryGetValue(TKey key, out TValue value) => _map.TryGetValue(key, out value);

        public Dictionary<TKey, TValue>.Enumerator GetEnumerator() => _map.GetEnumerator();
        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => _map.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => _map.GetEnumerator();
    }
}
