using XGame.Framework.Memory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using XGame.Framework.Interfaces;

namespace XGame.Framework
{
    /// <summary>
    /// 非泛型对象池
    /// * 非线程安全
    /// </summary>
    public static partial class ObjectPool
    {
        static readonly Dictionary<Type, Stack<object>> pool = new();

        const int CACHE_LIMIT = 32;

        internal static void Initialize()
        {
            MemoryMonitor.Register(Clear);
        }

        internal static void Dispose()
        {
            pool.Clear();
            MemoryMonitor.UnRegister(Clear);
        }
        public static void Clear()
        {
            pool.Clear();
        }
        /// <summary>
        /// 获取ReusableObject (需与Recycle成对)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Acquire<T>() where T : class, IReference, new()
        {
            Type type = typeof(T);
            T instance = (T)Create(type);
            return instance;
        }

        /// <summary>
        /// 回收 (需与Acquire成对)
        /// </summary>
        /// <param name="obj"></param>
        public static void Recycle(IReference obj)/* where T : class, IReference, new()*/
        {
            Assert.IsNotNull(obj, $"ObjectPool.Recycle: args不能为null");
            var type = obj.GetType();

            Assert.IsNotNull(type);
            Assert.IsFalse(type.IsInterface, $"ObjectPool.Recycle: type不能是接口, type: {type.FullName}");
            Assert.IsFalse(type.IsValueType, $"ObjectPool.Recycle: type不能是值类型, type: {type.FullName}");
            Assert.IsFalse(type.IsEnum, $"ObjectPool.Recycle: type不能是枚举, type: {type.FullName}");
            Assert.IsFalse(type.IsAbstract, $"ObjectPool.Recycle: type不能是抽象类, type: {type.FullName}");
            Assert.IsFalse(type.IsGenericType, $"ObjectPool.Recycle: type不能是泛型类型, type: {type.FullName}");
            Assert.IsFalse(type.IsGenericTypeDefinition, $"ObjectPool.Recycle: type不能是泛型定义类型, type: {type.FullName}");
            Assert.IsFalse(type.IsArray, $"ObjectPool.Recycle: type不能是数组, type: {type.FullName}");
            Assert.IsFalse(type.IsPointer, $"ObjectPool.Recycle: type不能是指针, type: {type.FullName}");
            Assert.IsTrue(type.IsClass, $"ObjectPool.Recycle: type不是Class, type: {type.FullName}");

            CheckConstructor(type);

            if (!pool.TryGetValue(type, out var stack))
            {
                stack = new Stack<object>();
                pool.Add(type, stack);
            }
            obj.Clear();

            if (stack.Count >= CACHE_LIMIT)
                stack.Clear();
            else if (stack.Contains(obj))
            {
                Log.Error($"ObjectPool.Recycle 重复回收, type: {type.FullName}");
                return;
            }
            stack.Push(obj);
        }

        static object Create(Type type)
        {
            Assert.IsNotNull(type);
            Assert.IsFalse(type.IsInterface, $"ObjectPool.AutoCreate: type不能是接口, type: {type.FullName}");
            Assert.IsFalse(type.IsValueType, $"ObjectPool.AutoCreate: type不能是值类型, type: {type.FullName}");
            Assert.IsFalse(type.IsEnum, $"ObjectPool.AutoCreate: type不能是枚举, type: {type.FullName}");
            Assert.IsFalse(type.IsAbstract, $"ObjectPool.AutoCreate: type不能是抽象类, type: {type.FullName}");
            Assert.IsFalse(type.IsGenericType, $"ObjectPool.AutoCreate: type不能是泛型类型, type: {type.FullName}");
            Assert.IsFalse(type.IsGenericTypeDefinition, $"ObjectPool.AutoCreate: type不能是泛型定义类型, type: {type.FullName}");
            Assert.IsFalse(type.IsArray, $"ObjectPool.AutoCreate: type不能是数组, type: {type.FullName}");
            Assert.IsFalse(type.IsPointer, $"ObjectPool.AutoCreate: type不能是指针, type: {type.FullName}");
            Assert.IsTrue(type.IsClass, $"ObjectPool.AutoCreate: type不是Class, type: {type.FullName}");

            CheckConstructor(type);


            if (pool.ContainsKey(type))
            {
                var stack = pool[type];
                if (stack.Count > 0)
                    return stack.Pop();
            }
            return Activator.CreateInstance(type);
        }



        [Conditional(MacroDefine.DEBUG), Conditional(MacroDefine.UNITY_EDITOR)]
        static void CheckConstructor(Type type)
        {
            BindingFlags flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
            Type[] args = new Type[] { };
            var ctor_0 = type.GetConstructor(flag, null, args, null);
            if (ctor_0 == null)
                throw new InvalidOperationException($"Error: These is no parameter constructor at: {type.FullName}");
        }
    }
}
