// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;

#if DEBUG
using System.Runtime.CompilerServices;
#endif

namespace Microsoft.CodeAnalysis.PooledObjects
{
    // Dictionary that can be recycled via an object pool
    // NOTE: these dictionaries always have the default comparer.
    internal sealed partial class PooledDictionary<K, V> : Dictionary<K, V>
#if !MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER
        , IPooled
#endif
        where K : notnull
    {
        private readonly ObjectPool<PooledDictionary<K, V>> _pool;

        private PooledDictionary(ObjectPool<PooledDictionary<K, V>> pool, IEqualityComparer<K> keyComparer)
            : base(keyComparer)
        {
            _pool = pool;
        }

        public ImmutableDictionary<K, V> ToImmutableDictionaryAndFree()
        {
            var result = this.ToImmutableDictionary(this.Comparer);
            this.Free();
            return result;
        }

        public ImmutableDictionary<K, V> ToImmutableDictionary() => this.ToImmutableDictionary(this.Comparer);

        public void Free()
        {
            this.Clear();
            _pool?.Free(this);
        }

        // global pool
        private static readonly ObjectPool<PooledDictionary<K, V>> s_poolInstance = CreatePool(EqualityComparer<K>.Default);

        // if someone needs to create a pool;
        public static ObjectPool<PooledDictionary<K, V>> CreatePool(IEqualityComparer<K> keyComparer)
        {
            ObjectPool<PooledDictionary<K, V>>? pool = null;
            pool = new ObjectPool<PooledDictionary<K, V>>(() => new PooledDictionary<K, V>(pool!, keyComparer), 128);
            return pool;
        }

        public static PooledDictionary<K, V> GetInstance(
#if DEBUG
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int lineNumber = 0
#endif
            )
        {
            var instance = s_poolInstance.Allocate(
#if DEBUG
                filePath, lineNumber
#endif
                );
            Debug.Assert(instance.Count == 0);
            return instance;
        }

#if !MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER
        public static PooledDisposer<PooledDictionary<K, V>> GetInstance(
            out PooledDictionary<K, V> instance
#if DEBUG
            , [CallerFilePath] string filePath = ""
            , [CallerLineNumber] int lineNumber = 0
#endif
            )
        {
            instance = GetInstance(
#if DEBUG
                filePath, lineNumber
#endif
                );
            return new PooledDisposer<PooledDictionary<K, V>>(instance);
        }

        // Nothing special to do here.
        void IPooled.Free(bool discardLargeInstance)
            => this.Free();
#endif
    }
}
