using UnityEngine;
using System.Collections.Generic;
using System;
using System.Collections.ObjectModel;
using System.Linq;

/// <summary>
/// 泛型对象池的父类,支持活跃对象的查找
/// </summary>
/// <typeparam name="BaseClass">基类对象池</typeparam>
public abstract class BasePool<BaseClass> : MonoBehaviour where BaseClass : MonoBehaviour
{
    /// <summary>
    /// 对象池本体
    /// </summary>
    protected Dictionary<Type, Stack<BaseClass>> Pools = new Dictionary<Type, Stack<BaseClass>>();
    /// <summary>
    /// 活跃对象记录表
    /// </summary>
    protected Dictionary<Type, List<BaseClass>> ActiveObjectList = new Dictionary<Type, List<BaseClass>>();

    /// <summary>
    /// 依据一个原型,返回一个空的可用对象
    /// </summary>
    public DerivedClass Get<DerivedClass>(DerivedClass prefab) where DerivedClass : BaseClass
    {
        var type = typeof(DerivedClass);
        if (!Pools.ContainsKey(type))
        {
            Pools[type] = new Stack<BaseClass>();
            ActiveObjectList[type] = new List<BaseClass>();
        }
        DerivedClass obj;
        //对象池逻辑
        if (Pools[type].Count > 0)
        {
            obj = (DerivedClass)Pools[type].Pop();
            ActiveObjectList[type].Add(obj);
        }
        else
        {
            //如果对象池没有对象,则创建一个对象
            obj = Instantiate(prefab);
            ActiveObjectList[type].Add(obj);
            OnCreate(obj);
        }
        OnGet(obj);
        obj.gameObject.SetActive(true);
        obj.enabled = true;

        return obj;
    }
    /// <summary>
    /// 回收当前对象
    /// </summary>
    public void Release(BaseClass Object)
    {
        var type = Object.GetType();
        //如果字典中没有该类的对象池,则创建一个该类型的对象池
        if (!Pools.ContainsKey(type)) Pools[type] = new Stack<BaseClass>();

        OnRelease(Object);
        Object.gameObject.SetActive(false);

        Pools[type].Push(Object);
        ActiveObjectList[type].Remove(Object);
    }
    /// <summary>
    /// 该对象是否有被实例化
    /// </summary>
    /// <param name="Object"></param>
    /// <returns></returns>
    public bool ContainsObject(BaseClass Object)
    {
        var type = Object.GetType();
        //如果没有该类型的对象池,则返回假
        if (!Pools.ContainsKey(type)) return false;
        return ActiveObjectList[type].Contains(Object);
    }
    /// <summary>
    /// 清空对象池
    /// </summary>
    /// <remarks>
    /// <paramref name="该操作会清空所有的对象"/>
    /// </remarks>
    public void ClearPool()
    {
        foreach (var stack in Pools.Values)
        {
            while (stack.Count > 0)
            {
                Destroy(stack.Pop().gameObject);
            }
        }
        Pools.Clear();
    }
    /// <summary>
    /// 返回一个当前类型的只读列表
    /// </summary>
    public ReadOnlyCollection<DerivedClass> GetActiveObjectList<DerivedClass>() where DerivedClass : BaseClass
    { 
        var type=typeof(DerivedClass);
        if (!Pools.ContainsKey(type))
        {
            ActiveObjectList[type] = new List<BaseClass>();
        }
        //what the fuck ? this code is SHIT!
        return ActiveObjectList[type].Cast<DerivedClass>().ToList().AsReadOnly();
    }
    /// <summary>
    /// 对指定类型的所有活跃对象执行指定操作
    /// </summary>
    /// <typeparam name="DerivedClass">要操作的派生类型</typeparam>
    /// <param name="action">要对每个对象执行的操作</param>
    /// <param name="includeInactive">是否包含非活跃对象（已回收但未销毁的对象）</param>
    public void ForEach<DerivedClass>(Action<DerivedClass> action) where DerivedClass : BaseClass
    {
        var type = typeof(DerivedClass);

        // 检查是否有该类型的活跃对象列表
        if (!ActiveObjectList.TryGetValue(type, out var list) || list == null) return;

        for (int i = list.Count - 1; i >= 0; i--)
        {
            var item = (DerivedClass)list[i];
            action?.Invoke(item);
        }
    }
    /// <summary>
    /// 当对象首次创建时调用
    /// </summary>
    protected virtual void OnCreate(BaseClass obj) { }

    /// <summary>
    /// 当对象从池中取出时调用
    /// </summary>
    protected virtual void OnGet(BaseClass obj) { }

    /// <summary>
    /// 当对象回收到池中时调用
    /// </summary>
    protected virtual void OnRelease(BaseClass obj) { }
}
/*
首先必须分清楚我们需要的是哪种对象池。
在一个类里面完成所有不同对象池功能的定义是绝对不现实的，这是显而易见的。
但是我们又想实现一定意义上的对象池复用，所以我们需要的其实是一个可重复实例化的对象池。
很容易的得到，这个对象池应该继承与MonoBehaviour，因为其实所有的脚本类其实都继承与这个类。
所以依据里氏转换原则，MonoBehaviour可以被转换为任意我们已有的脚本。
并且可以使用unity相关的API，如SetActive(false)等。

我们的对象仅仅只能在必要时 生成/初始化 但是不同类型的对象完全不一样，为了实现基对象池与限定类型的解耦合，
我们可以采用回调函数的方式，即定义一个Action()类型的的回调函数，并且在每次对象池调用对象时，即刻执行这个Action()
这个Action()既可以是我再子类对象池中覆写的函数，也可以是子类自带的初始化函数

当然采用回调函数的方法未免还是有点大题小做，可以通过仿照生命周期函数的方式，完成基类的定义，
并且，基类不进行实例化，它只可以被继承至子类，以保证安全性，我们必须防止所有的实例共用一个栈的情况发生

理论上是可行的
*/