using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Assets.Source.Patterns.ObjectPool;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Assets.Source.Patterns
{
    public class MonoPool<T> where T : Component
    {
        private readonly Transform _root;
        private readonly List<T> _pooledObjects = new List<T>();
        private readonly List<T> _spawnedObjects = new List<T>();

        public MonoPool(Transform root)
        {
            _root = root;
        }

        public ReadOnlyCollection<T> SpawnedObjects => _spawnedObjects.AsReadOnly();
        public ReadOnlyCollection<T> PooledObjects => _pooledObjects.AsReadOnly();

        public T WarmPool(T template)
        {
            var obj = Object.Instantiate(template, _root);
            obj.name = template.name;
            _pooledObjects.Add(obj);
            obj.gameObject.SetActive(false);
            return obj;
        }

        public T SpawnFirst(T fallbackTemplate)
        {
            var obj = _pooledObjects.LastOrDefault();

            if (obj == null)
            {
                WarmPool(fallbackTemplate);
                return SpawnFirst(fallbackTemplate);
            }

            return SpawnConcrete(obj);
        }

        public T SpawnConcrete(T concreteFromPool)
        {
            if (concreteFromPool == null)
                return null;

            concreteFromPool.gameObject.SetActive(true);

            if (concreteFromPool is IPoolObject)
                (concreteFromPool as IPoolObject).OnSpawnFromPool();

            _pooledObjects.Remove(concreteFromPool);
            _spawnedObjects.Add(concreteFromPool);

            return concreteFromPool;
        }

        public void Release(T obj)
        {
            obj.gameObject.SetActive(false);
            obj.transform.SetParent(_root);

            if (obj is IPoolObject)
                (obj as IPoolObject).OnReturnToPool();

            _spawnedObjects.Remove(obj);
            _pooledObjects.Add(obj);
        }

        public void ReleaseAll()
        {
            while (_spawnedObjects.Count > 0)
                Release(_spawnedObjects.First());
        }
    }
}

