using FishNet.Connection;
using FishNet.Managing;
using FishNet.Object;
using FishNet.Serializing.Helping;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Utility.Performance
{
    /// 
    /// Various ListCache instances that may be used on the Unity thread.
    /// 
    public static class ListCaches
    {
        /// 
        /// Cache collection for NetworkObjects.
        /// 
        private static Stack> _networkObjectCaches = new Stack>();
        /// 
        /// Cache collection for NetworkObjects.
        /// 
        private static Stack> _networkBehaviourCaches = new Stack>();
        /// 
        /// Cache collection for NetworkObjects.
        /// 
        private static Stack> _transformCaches = new Stack>();
        /// 
        /// Cache collection for NetworkConnections.
        /// 
        private static Stack> _networkConnectionCaches = new Stack>();
        /// 
        /// Cache collection for ints.
        ///         
        private static Stack> _intCaches = new Stack>();
        #region GetCache.
        /// 
        /// Returns a NetworkObject cache. Use StoreCache to return the cache.
        /// 
        /// 
        public static ListCache GetNetworkObjectCache()
        {
            ListCache result;
            if (_networkObjectCaches.Count == 0)
                result = new ListCache();
            else
                result = _networkObjectCaches.Pop();
            return result;
        }
        /// 
        /// Returns a NetworkConnection cache. Use StoreCache to return the cache.
        /// 
        /// 
        public static ListCache GetNetworkConnectionCache()
        {
            ListCache result;
            if (_networkConnectionCaches.Count == 0)
                result = new ListCache();
            else
                result = _networkConnectionCaches.Pop();
            return result;
        }
        /// 
        /// Returns a Transform cache. Use StoreCache to return the cache.
        /// 
        /// 
        public static ListCache GetTransformCache()
        {
            ListCache result;
            if (_transformCaches.Count == 0)
                result = new ListCache();
            else
                result = _transformCaches.Pop();
            return result;
        }
        /// 
        /// Returns a NetworkBehaviour cache. Use StoreCache to return the cache.
        /// 
        /// 
        public static ListCache GetNetworkBehaviourCache()
        {
            ListCache result;
            if (_networkBehaviourCaches.Count == 0)
                result = new ListCache();
            else
                result = _networkBehaviourCaches.Pop();
            return result;
        }
        /// 
        /// Returns an int cache. Use StoreCache to return the cache.
        /// 
        /// 
        public static ListCache GetIntCache()
        {
            ListCache result;
            if (_intCaches.Count == 0)
                result = new ListCache();
            else
                result = _intCaches.Pop();
            return result;
        }
        #endregion
        #region StoreCache.
        /// 
        /// Stores a NetworkObject cache.
        /// 
        /// 
        public static void StoreCache(ListCache cache)
        {
            cache.Reset();
            _networkObjectCaches.Push(cache);
        }
        /// 
        /// Stores a NetworkConnection cache.
        /// 
        /// 
        public static void StoreCache(ListCache cache)
        {
            cache.Reset();
            _networkConnectionCaches.Push(cache);
        }
        /// 
        /// Stores a Transform cache.
        /// 
        /// 
        public static void StoreCache(ListCache cache)
        {
            cache.Reset();
            _transformCaches.Push(cache);
        }
        /// 
        /// Stores a NetworkBehaviour cache.
        /// 
        /// 
        public static void StoreCache(ListCache cache)
        {
            cache.Reset();
            _networkBehaviourCaches.Push(cache);
        }
        /// 
        /// Stores an int cache.
        /// 
        /// 
        public static void StoreCache(ListCache cache)
        {
            cache.Reset();
            _intCaches.Push(cache);
        }
        #endregion
    }
    /// 
    /// Creates a reusable cache of T which auto expands.
    /// 
    public class ListCache
    {
        #region Public.
        /// 
        /// Collection cache is for.
        /// 
        public List Collection = new List();
        /// 
        /// Entries currently written.
        /// 
        public int Written => Collection.Count;
        #endregion
        #region Private.
        /// 
        /// Cache for type.
        /// 
        private Stack _cache = new Stack();
        #endregion
        public ListCache()
        {
            Collection = new List();
        }
        public ListCache(int capacity)
        {
            Collection = new List(capacity);
        }
        /// 
        /// Returns T from cache when possible, or creates a new object when not.
        /// 
        /// 
        private T Retrieve()
        {
            if (_cache.Count > 0)
                return _cache.Pop();
            else
                return Activator.CreateInstance();
        }
        /// 
        /// Stores value into the cache.
        /// 
        /// 
        private void Store(T value)
        {
            _cache.Push(value);
        }
        /// 
        /// Adds a new value to Collection and returns it.
        /// 
        /// 
        public T AddReference()
        {
            T next = Retrieve();
            Collection.Add(next);
            return next;
        }
        /// 
        /// Inserts an bject into Collection and returns it.
        /// 
        /// 
        public T InsertReference(int index)
        {
            //Would just be at the end anyway.
            if (index >= Collection.Count)
                return AddReference();
            T next = Retrieve();
            Collection.Insert(index, next);
            return next;
        }
        /// 
        /// Adds value to Collection.
        /// 
        /// 
        public void AddValue(T value)
        {
            Collection.Add(value);
        }
        /// 
        /// Inserts value into Collection.
        /// 
        /// 
        public void InsertValue(int index, T value)
        {
            //Would just be at the end anyway.
            if (index >= Collection.Count)
                AddValue(value);
            else
                Collection.Insert(index, value);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(ListCache values)
        {
            int w = values.Written;
            List c = values.Collection;
            for (int i = 0; i < w; i++)
                AddValue(c[i]);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(T[] values)
        {
            for (int i = 0; i < values.Length; i++)
                AddValue(values[i]);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(List values)
        {
            for (int i = 0; i < values.Count; i++)
                AddValue(values[i]);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(HashSet values)
        {
            foreach (T item in values)
                AddValue(item);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(ISet values)
        {
            foreach (T item in values)
                AddValue(item);
        }
        /// 
        /// Adds values to Collection.
        /// 
        /// 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddValues(IReadOnlyCollection values)
        {
            foreach (T item in values)
                AddValue(item);
        }
        /// 
        /// Resets cache.
        /// 
        public void Reset()
        {
            foreach (T item in Collection)
                Store(item);
            Collection.Clear();
        }
    }
}