using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace squirrowse.web.Common
{
    public class ConcurrentFixedSizeQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>, ICollection
    {
        private readonly ConcurrentQueue<T> m_concurrentQueue;
        private readonly int m_maxSize;

        public ConcurrentFixedSizeQueue(int maxSize) : this(Array.Empty<T>(), maxSize)
        {
        }

        public ConcurrentFixedSizeQueue(IEnumerable<T> initialCollection, int maxSize)
        {
            if (initialCollection == null) throw new ArgumentNullException(nameof(initialCollection));

            m_concurrentQueue = new ConcurrentQueue<T>(initialCollection);
            m_maxSize = maxSize;
        }

        public bool IsEmpty => m_concurrentQueue.IsEmpty;

        public int Count => m_concurrentQueue.Count;

        public void CopyTo(T[] array, int index)
        {
            m_concurrentQueue.CopyTo(array, index);
        }

        public T[] ToArray()
        {
            return m_concurrentQueue.ToArray();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return m_concurrentQueue.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        // Explicit ICollection implementations.
        void ICollection.CopyTo(Array array, int index)
        {
            ((ICollection) m_concurrentQueue).CopyTo(array, index);
        }

        object ICollection.SyncRoot => ((ICollection) m_concurrentQueue).SyncRoot;
        bool ICollection.IsSynchronized => ((ICollection) m_concurrentQueue).IsSynchronized;

        // Explicit IProducerConsumerCollection<T> implementations.
        bool IProducerConsumerCollection<T>.TryAdd(T item)
        {
            return ((IProducerConsumerCollection<T>) m_concurrentQueue).TryAdd(item);
        }

        bool IProducerConsumerCollection<T>.TryTake(out T item)
        {
            return ((IProducerConsumerCollection<T>) m_concurrentQueue).TryTake(out item);
        }

        public void Enqueue(T item)
        {
            m_concurrentQueue.Enqueue(item);

            if (m_concurrentQueue.Count > m_maxSize) m_concurrentQueue.TryDequeue(out var result);
        }

        public void TryPeek(out T result)
        {
            m_concurrentQueue.TryPeek(out result);
        }

        public bool TryDequeue(out T result)
        {
            return m_concurrentQueue.TryDequeue(out result);
        }

        public override int GetHashCode()
        {
            return m_concurrentQueue.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            return m_concurrentQueue.Equals(obj);
        }

        public override string ToString()
        {
            return m_concurrentQueue.ToString();
        }
    }
}