using System;
using System.Threading;
using Microsoft.SPOT;

namespace Logger
{
    public class SynchronizedLogEntryQueue : LogEntryQueue
    {
        private object          _sync;
        private int             _consumersWaiting;
        private int             _producersWaiting;
        private AutoResetEvent  _available;

        public SynchronizedLogEntryQueue(int capacity) : base(capacity)
        {
            _available = new AutoResetEvent(false);
            _sync = new object();
            _consumersWaiting = 0;
            _producersWaiting = 0;
        }

        public bool Queued()
        {
            bool emptyQ;
            lock (_sync)
            {
                emptyQ = Empty();
            }
            return emptyQ;
        }

        public void Add(LogEntry e)
        {
            Monitor.Enter(_sync);
            try
            {
                while (Full())
                {
                    _producersWaiting++;
                    Monitor.Exit(_sync);
                    _available.WaitOne();
                    Monitor.Enter(_sync);
                    _producersWaiting--;
                }
                Insert(e);
                if (_consumersWaiting > 0)
                    _available.Set();
            }
            finally
            {
                Monitor.Exit(_sync);
            }
        }

        public LogEntry Remove(ref LogEntry e)
        {
            LogEntry item;
            Monitor.Enter(_sync);
            try
            {
                while (Empty())
                {
                    _consumersWaiting++;
                    Monitor.Exit(_sync);
                    _available.WaitOne();
                    Monitor.Enter(_sync);
                    _consumersWaiting--;
                }
                item = Delete();
                e = (LogEntry)item.Clone();
                if (_producersWaiting > 0)
                    _available.Set();
            }
            finally
            {
                Monitor.Exit(_sync);
            }
            return item;
        }
    }
}
