// <copyright file="ContainerExtensions.cs" company="Scratch Foundation">
// Copyright (c) Scratch Foundation. All rights reserved.
// </copyright>

namespace ScratchLink.Extensions;

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

/// <summary>
/// Extensions for use with containers or other enumerables.
/// </summary>
internal static class ContainerExtensions
{
    /// <summary>
    /// If this enumerable is null, return an empty enumerable of the same type.
    /// Otherwise, return the enumerable as-is.
    /// </summary>
    /// <typeparam name="T">The type of object enumerated by the enumerable.</typeparam>
    /// <param name="original">The possibly-null enumerable.</param>
    /// <returns>A never-null enumerable.</returns>
    public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> original) =>
        original ?? Enumerable.Empty<T>();

    /// <summary>
    /// Attempt to retrieve a value and, if that succeeds, cast it. Good for dictionaries containing <c>object</c>.
    /// If the key is not found, the output variable is set to the type's default value.
    /// Adapted from <a href="https://stackoverflow.com/a/63203652">this Stack Overflow answer</a>.
    /// </summary>
    /// <typeparam name="TValueAs">The type to cast the value to.</typeparam>
    /// <typeparam name="TKey">The type of key in the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of value in the dictionary.</typeparam>
    /// <param name="dictionary">The dictionary to look in.</param>
    /// <param name="key">The dictionary to look for.</param>
    /// <param name="value">The variable to receive the casted value. Set to <c>default</c> if the key is not found.</param>
    /// <returns>
    /// True if the key was found, regardless of what happens in the cast, or false if the key was not found.
    /// </returns>
    public static bool TryGetValueAs<TValueAs, TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, out TValueAs value)
        where TValueAs : TValue
    {
        if (dictionary.TryGetValue(key, out TValue rawValue))
        {
            value = (TValueAs)rawValue;
            return true;
        }

        value = default;
        return false;
    }
}