// reference from https://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/HandleCollector.cs,d0f99220d8e1b708

using System;
using System.Runtime.InteropServices;

namespace HandyControl.Tools.Interop
{
    internal static class HandleCollector
    {
        private static HandleType[] HandleTypes;
        private static int HandleTypeCount;

        private static readonly object HandleMutex = new object();

        internal static IntPtr Add(IntPtr handle, int type)
        {
            HandleTypes[type - 1].Add();
            return handle;
        }

        [System.Security.SecuritySafeCritical]
        internal static SafeHandle Add(SafeHandle handle, int type)
        {
            HandleTypes[type - 1].Add();
            return handle;
        }

        internal static void Add(int type)
        {
            HandleTypes[type - 1].Add();
        }

        internal static int RegisterType(string typeName, int expense, int initialThreshold)
        {
            lock (HandleMutex)
            {
                if (HandleTypeCount == 0 || HandleTypeCount == HandleTypes.Length)
                {
                    HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
                    if (HandleTypes != null)
                    {
                        Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
                    }
                    HandleTypes = newTypes;
                }

                HandleTypes[HandleTypeCount++] = new HandleType(expense, initialThreshold);
                return HandleTypeCount;
            }
        }

        internal static IntPtr Remove(IntPtr handle, int type)
        {
            HandleTypes[type - 1].Remove();
            return handle;
        }

        [System.Security.SecuritySafeCritical]
        internal static SafeHandle Remove(SafeHandle handle, int type)
        {
            HandleTypes[type - 1].Remove();
            return handle;
        }

        internal static void Remove(int type)
        {
            HandleTypes[type - 1].Remove();
        }

        private class HandleType
        {
            private readonly int _initialThreshHold;
            private int _threshHold;
            private int _handleCount;
            private readonly int _deltaPercent;

            internal HandleType(int expense, int initialThreshHold)
            {
                _initialThreshHold = initialThreshHold;
                _threshHold = initialThreshHold;
                _deltaPercent = 100 - expense;
            }

            internal void Add()
            {
                lock (this)
                {
                    _handleCount++;
                    var performCollect = NeedCollection();

                    if (!performCollect)
                    {
                        return;
                    }
                }

                GC.Collect();

                var sleep = (100 - _deltaPercent) / 4;
                System.Threading.Thread.Sleep(sleep);
            }

            private bool NeedCollection()
            {

                if (_handleCount > _threshHold)
                {
                    _threshHold = _handleCount + _handleCount * _deltaPercent / 100;
                    return true;
                }

                var oldThreshHold = 100 * _threshHold / (100 + _deltaPercent);
                if (oldThreshHold >= _initialThreshHold && _handleCount < (int) (oldThreshHold * .9F))
                {
                    _threshHold = oldThreshHold;
                }

                return false;
            }

            internal void Remove()
            {
                lock (this)
                {
                    _handleCount--;

                    _handleCount = Math.Max(0, _handleCount);
                }
            }
        }
    }
}
