﻿using System;
using System.Collections.Generic;

namespace Hardcore
{
    /// <summary>
    /// Extension methods for <see cref="IDictionary{TKey,TValue}"/>
    /// </summary>
    public static class DictionaryExtensions
    {
        /// <summary>
        /// Gets value by specified key and write defaultValue if value not found
        /// </summary>
        public static TValue Retrieve<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue> defaultValueProvider)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));
            if (defaultValueProvider == null) throw new ArgumentNullException(nameof(defaultValueProvider));

            TValue val;
            if (!dict.TryGetValue(key, out val))
            {
                val = defaultValueProvider();
                dict.Add(key, val);
            }
            return val;
        }

        /// <summary>
        /// Gets value by specified key and write defaultValue if value not found
        /// </summary>
        public static TValue Retrieve<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));

            return Retrieve(dict, key, () => defaultValue);
        }

        /// <summary>
        /// Gets value by specified key and write defaultValue if value not found
        /// </summary>
        public static TValue Retrieve<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) where TValue : new()
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));

            return Retrieve(dict, key, () => new TValue());
        }

        /// <summary>
        /// Gets value from dictionary if exists or defaultValue in other case
        /// </summary>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));

            TValue val;
            return dict.TryGetValue(key, out val) ? val : default(TValue);
        }

        /// <summary>
        /// Gets value from dictionary if exists or defaultValue in other case
        /// </summary>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));

            TValue val;
            return dict.TryGetValue(key, out val) ? val : defaultValue;
        }

        /// <summary>
        /// Adds a value pair if it not contains with specified key or replace contains
        /// </summary>
        public static void ForceAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));

            if (dict.ContainsKey(key))
                dict[key] = value;
            else dict.Add(key, value);
        }
    }
}
