// <copyright file="DictionaryExtensions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

#if NETFRAMEWORK
using System.Collections;
using System.Collections.Concurrent;

namespace IntegrationTests.Helpers.Compatibility;

public static class DictionaryExtensions
{
    public static TValue? GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
    {
        if (dictionary == null)
        {
            throw new ArgumentNullException(nameof(dictionary));
        }

        return dictionary.TryGetValue(key, out var value)
                    ? value
                    : default;
    }

    public static TValue? GetValueOrDefault<TValue>(this IDictionary dictionary, object key)
    {
        if (dictionary == null)
        {
            throw new ArgumentNullException(nameof(dictionary));
        }

        return dictionary.TryGetValue(key, out TValue? value)
                    ? value
                    : default;
    }

    public static bool TryGetValue<TValue>(this IDictionary dictionary, object key, out TValue? value)
    {
        if (dictionary == null)
        {
            throw new ArgumentNullException(nameof(dictionary));
        }

        object? valueObj;

        try
        {
            // per its contract, IDictionary.Item[] should return null instead of throwing an exception
            // if a key is not found, but let's use try/catch to be defensive against misbehaving implementations
            valueObj = dictionary[key];
        }
        catch
        {
            valueObj = null;
        }

        switch (valueObj)
        {
            case TValue valueTyped:
                value = valueTyped;
                return true;
            case IConvertible convertible:
                value = (TValue)convertible.ToType(typeof(TValue), provider: null);
                return true;
            default:
                value = default;
                return false;
        }
    }

    public static bool IsEmpty<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
    {
        if (dictionary is ConcurrentDictionary<TKey, TValue> concurrentDictionary)
        {
            return concurrentDictionary.IsEmpty;
        }

        return dictionary.Count == 0;
    }
}
#endif
