using CommonLang.Concurrent;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace CommonLang
{
    public static class ObjectPoolStatus
    {
        internal static AtomicLong s_total_count = new AtomicLong(0);
        internal static AtomicLong s_total_inactive = new AtomicLong(0);
        public static long TotalCount
        {
            get { return s_total_count.Value; }
        }
        public static long TotalInactive
        {
            get { return s_total_inactive.Value; }
        }
        public static long TotalActive
        {
            get { return TotalCount - TotalInactive; }
        }
    }

    public class ObjectPool<T>
    {
        public delegate T ActionCreater();

        private readonly Stack<T> m_Stack = new Stack<T>();
        private readonly ActionCreater m_ActionOnCreate;
        private readonly Action<T> m_ActionOnGet;
        private readonly Action<T> m_ActionOnRelease;

        private AtomicLong count = new AtomicLong(0);
        public long countAll
        {
            get { return count.Value; }
        }
        public long countInactive
        {
            get { return this.m_Stack.Count; }
        }
        public long countActive
        {
            get { return this.countAll - this.countInactive; }
        }
        public ObjectPool(ActionCreater actionCreate = null, Action<T> actionOnGet = null, Action<T> actionOnRelease = null)
        {
            this.m_ActionOnGet = actionOnGet;
            this.m_ActionOnRelease = actionOnRelease;
            this.m_ActionOnCreate = actionCreate;
        }
        public T Get()
        {
            lock (this)
            {
                T t;
                if (this.m_Stack.Count == 0)
                {
                    if (m_ActionOnCreate != null)
                    {
                        t = m_ActionOnCreate();
                    }
                    else
                    {
                        t = ((default(T) == null) ? Activator.CreateInstance<T>() : default(T));
                    }
                    this.count++;
                    ObjectPoolStatus.s_total_count++;
                }
                else
                {
                    t = this.m_Stack.Pop();
                    ObjectPoolStatus.s_total_inactive--;
                }
                if (this.m_ActionOnGet != null)
                {
                    this.m_ActionOnGet(t);
                }
                return t;
            }
        }
        public void Release(T element)
        {
            lock (this)
            {
                if (this.m_Stack.Count > 0 && object.ReferenceEquals(this.m_Stack.Peek(), element))
                {
                    throw new Exception("Internal error. Trying to destroy object that is already released to pool.");
                }
                if (this.m_ActionOnRelease != null)
                {
                    this.m_ActionOnRelease(element);
                }
                this.m_Stack.Push(element);
                ObjectPoolStatus.s_total_inactive++;
            }
        }
    }

    public static class ListObjectPool<T>
    {
        private static readonly ObjectPool<List<T>> s_ListPool = new ObjectPool<List<T>>(s_ListPool_OnCreate, null, s_ListPool_OnRelease);
        private static List<T> s_ListPool_OnCreate()
        {
            return new AutoReleaseList<T>();
        }
        private static void s_ListPool_OnRelease(List<T> l)
        {
            l.Clear();
        }
        public static AutoReleaseList<T> AllocAutoRelease()
        {
            AutoReleaseList<T> ret = s_ListPool.Get() as AutoReleaseList<T>;
            return ret;
        }
        public static AutoReleaseList<T> AllocAutoRelease(IEnumerable<T> added)
        {
            AutoReleaseList<T> ret = s_ListPool.Get() as AutoReleaseList<T>;
            if (added != null) { ret.AddRange(added); }
            return ret;
        }
        public static AutoReleaseList<T> AllocAutoRelease(T[] added)
        {
            AutoReleaseList<T> ret = s_ListPool.Get() as AutoReleaseList<T>;
            if (added != null) { ret.AddRange(added); }
            return ret;
        }
        public static AutoReleaseList<T> AllocAutoRelease(int capacity)
        {
            AutoReleaseList<T> ret = s_ListPool.Get() as AutoReleaseList<T>;
            if (capacity != 0)
            {
                ret.Capacity = capacity;
            }
            return ret;
        }
        public static void Release(List<T> toRelease)
        {
            s_ListPool.Release(toRelease);
        }
        public class AutoReleaseList<R> : List<R>, IDisposable
        {
            internal AutoReleaseList() { }
            void IDisposable.Dispose()
            {
                ListObjectPool<R>.Release(this);
            }
        }
    }

    public static class MemoryStreamObjectPool
    {
        private static readonly ObjectPool<AutoRelease> s_Pool = new ObjectPool<AutoRelease>(s_ListPool_OnCreate, null, null);
        private static AutoRelease s_ListPool_OnCreate()
        {
            return new AutoRelease();
        }
        public static AutoRelease AllocAutoRelease()
        {
            AutoRelease ret = s_Pool.Get() as AutoRelease;
            return ret;
        }
        public static AutoRelease AllocAutoRelease(int capacity)
        {
            AutoRelease ret = s_Pool.Get() as AutoRelease;
            ret.Capacity = capacity;
            return ret;
        }
        public static AutoRelease AllocAutoRelease(byte[] buffer)
        {
            AutoRelease ret = s_Pool.Get() as AutoRelease;
            IO.IOUtil.WriteToEnd(ret, buffer, 0, buffer.Length);
            ret.Position = 0;
            return ret;
        }
        public static AutoRelease AllocAutoRelease(byte[] buffer, int offset, int length)
        {
            AutoRelease ret = s_Pool.Get() as AutoRelease;
            IO.IOUtil.WriteToEnd(ret, buffer, offset, length);
            ret.Position = 0;
            return ret;
        }

        public static void Release(AutoRelease toRelease)
        {
            s_Pool.Release(toRelease);
        }
        public class AutoRelease : MemoryStream
        {
            internal AutoRelease() { }
            protected override void Dispose(bool disposing)
            {
                this.Position = 0;
                this.SetLength(0);
                MemoryStreamObjectPool.Release(this);
            }
        }
    }


}